Disable krb4 cross-realm in krb524d and krb5kdc. Provide an option to
authorSam Hartman <hartmans@mit.edu>
Mon, 17 Mar 2003 01:03:11 +0000 (01:03 +0000)
committerSam Hartman <hartmans@mit.edu>
Mon, 17 Mar 2003 01:03:11 +0000 (01:03 +0000)
reenable (-X) which prints a warning that you are creating a security
hole.

Remove support for generating krb4 tickets encrypted using 3DES
service keys as it is insecure.  They are still accepted however.

The KDc is much more strict about accepting only tickets that it would
have issued in the current configuration.  In particular if the KDC
would choose some enctype for writing a TGT, other enctypes will not
be accepted when using a TGT.

Ticket: 1385
Target_Version: 1.3
Tags: pullup

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@15286 dc483132-0cff-0310-8789-dd5450dbe970

src/kdc/ChangeLog
src/kdc/kdc_util.h
src/kdc/kerberos_v4.c
src/kdc/main.c
src/krb524/ChangeLog
src/krb524/cnv_tkt_skey.c
src/krb524/krb524d.c
src/lib/kdb/ChangeLog
src/lib/kdb/keytab.c

index eb4273615c0aa7f663caa35431c8189a1888a6c9..040281988c8f31b663693e11b75e1deb4b63d484 100644 (file)
@@ -1,3 +1,16 @@
+2003-03-16  Sam Hartman  <hartmans@mit.edu>
+
+       * main.c (initialize_realms): Add support to call
+       enable_v4_crossrealm if the user wants insecure operation 
+
+       * kerberos_v4.c: Add enable_v4_crossrealm.  By default krb4
+       cross-realm is not allowed as it is insecure.  Also, remove
+       support for generating krb4 tickets encrypted in 3DES as they are
+       insecure. 
+
+       * kdc_util.h: Define enable_v4_crossrealm, new function to enable
+       secure krb4 cross-realm authentication 
+
 2003-03-05  Tom Yu  <tlyu@mit.edu>
 
        * main.c (init_realm): Update call to krb5_ktdb_resolve().
index 9abe3b86046bc2b2b9ad7c6829d28c8a917f15e2..05ba07f4f3775aa00a8594afca388e87a0df69c9 100644 (file)
@@ -176,6 +176,7 @@ krb5_error_code process_v4 (const krb5_data *,
                                      const krb5_fulladdr *,
                                      krb5_data **);
 void process_v4_mode (const char *, const char *);
+void enable_v4_crossrealm(char *);
 #else
 #define process_v4(foo,bar,quux,foobar)        KRB5KRB_AP_ERR_BADVERSION
 #endif
index a87a1d5e50c8f144cb89eb6df468050a51e747e6..01359792f5495ca103884d4ee1aa6721d4915013 100644 (file)
@@ -146,7 +146,7 @@ static krb5_data *response;
 
 void kerberos_v4 (struct sockaddr_in *, KTEXT);
 void kerb_err_reply (struct sockaddr_in *, KTEXT, long, char *);
-static int set_tgtkey (char *, krb5_kvno);
+static int set_tgtkey (char *, krb5_kvno, krb5_boolean);
 
 /* Attributes converted from V5 to V4 - internal representation */
 #define V4_KDB_REQUIRES_PREAUTH  0x1
@@ -180,6 +180,8 @@ static const struct v4mode_lookup_entry  v4mode_table[] = {
 static const int v4mode_table_nents = sizeof(v4mode_table)/
                                      sizeof(v4mode_table[0]);
 
+static int allow_v4_crossrealm = 0;
+
 void process_v4_mode(const char *program_name, const char *string)
 {
     int i, found;
@@ -205,6 +207,11 @@ void process_v4_mode(const char *program_name, const char *string)
     return;
 }
 
+void enable_v4_crossrealm ( char *programname) {
+    allow_v4_crossrealm = 1;
+    krb5_klog_syslog(LOG_ERR, "Enabling v4 cross-realm compatibility; this is a known security hole");
+}
+
 krb5_error_code
 process_v4(const krb5_data *pkt, const krb5_fulladdr *client_fulladdr,
           krb5_data **resp)
@@ -382,6 +389,14 @@ compat_decrypt_key (krb5_key_data *in5, unsigned char *out4,
 /* array of name-components + NULL ptr
  */
 
+/*
+ * Previously this code returned either a v4 key or a v5 key  and you
+ * could tell from the enctype of the v5 key whether the v4 key was
+ * useful.  Now we return both keys so the code can try both des3 and
+ * des decryption.  We fail if the ticket doesn't have a v4 key.
+ * Also, note as a side effect, the v5 key is basically useless  in
+ * the client case.  It is still returned so the caller can free it.
+ */
 static int
 kerb_get_principal(char *name, char *inst, /* could have wild cards */
                   Principal *principal,
@@ -461,8 +476,28 @@ kerb_get_principal(char *name, char *inst, /* could have wild cards */
            return(0);
        }
     } else {
-       /* XXX yes I know this is a hardcoded search order */
-       if (krb5_dbe_find_enctype(kdc_context, &entries,
+       if ( krb5_dbe_find_enctype(kdc_context, &entries,
+                                 ENCTYPE_DES_CBC_CRC,
+                                 KRB5_KDB_SALTTYPE_V4, kvno, &pkey) &&
+           krb5_dbe_find_enctype(kdc_context, &entries,
+                                 ENCTYPE_DES_CBC_CRC,
+                                 -1, kvno, &pkey)) {
+           lt = klog(L_KRB_PERR,
+                     "KDC V4: failed to find key for %s.%s #%d",
+                     name, inst, kvno);
+           krb5_db_free_principal(kdc_context, &entries, nprinc);
+           return(0);
+       }
+    }
+
+    if (!compat_decrypt_key(pkey, k, k5key, issrv)) {
+       memcpy( &principal->key_low, k, LONGLEN);
+               memcpy( &principal->key_high, (krb5_ui_4 *) k + 1, LONGLEN);
+    }
+    memset(k, 0, sizeof k);
+    if (issrv) {
+       krb5_free_keyblock_contents (kdc_context, k5key);
+       if (krb5_dbe_find_enctype(kdc_context, &entries,
                                  ENCTYPE_DES3_CBC_RAW,
                                  -1, kvno, &pkey) &&
            krb5_dbe_find_enctype(kdc_context, &entries,
@@ -478,17 +513,16 @@ kerb_get_principal(char *name, char *inst, /* could have wild cards */
                                  ENCTYPE_DES_CBC_CRC,
                                  -1, kvno, &pkey)) {
            lt = klog(L_KRB_PERR,
-                     "KDC V4: failed to find key for %s.%s #%d",
+                     "KDC V4: failed to find key for %s.%s #%d (after having found it once)",
                      name, inst, kvno);
            krb5_db_free_principal(kdc_context, &entries, nprinc);
            return(0);
        }
-    }
+       compat_decrypt_key(pkey, k, k5key, issrv);
+    memset (k, 0, sizeof k);
+       }
+
 
-    if (!compat_decrypt_key(pkey, k, k5key, issrv)) {
-       memcpy( &principal->key_low, k, LONGLEN);
-               memcpy( &principal->key_high, (krb5_ui_4 *) k + 1, LONGLEN);
-    }
     /*
      * Convert v5's entries struct to v4's Principal struct:
      * v5's time-unit for lifetimes is 1 sec, while v4 uses 5 minutes,
@@ -732,21 +766,14 @@ kerberos_v4(struct sockaddr_in *client, KTEXT pkt)
            kdb_encrypt_key(key, key, master_key,
                            master_key_schedule, DECRYPT);
            /* construct and seal the ticket */
-           if (K4KDC_ENCTYPE_OK(k5key.enctype)) {
-               krb_create_ticket(tk, k_flags, a_name_data.name,
-                                 a_name_data.instance, local_realm,
-                                 client_host.s_addr, (char *) session_key,
-                                 lifetime, kerb_time.tv_sec,
-                                 s_name_data.name, s_name_data.instance,
-                                 key);
-           } else {
-               krb_cr_tkt_krb5(tk, k_flags, a_name_data.name,
-                               a_name_data.instance, local_realm,
-                               client_host.s_addr, (char *) session_key,
-                               lifetime, kerb_time.tv_sec,
-                               s_name_data.name, s_name_data.instance,
-                               &k5key);
-           }
+           /* We always issue des tickets; the 3des tickets are a broken hack*/
+           krb_create_ticket(tk, k_flags, a_name_data.name,
+                             a_name_data.instance, local_realm,
+                             client_host.s_addr, (char *) session_key,
+                             lifetime, kerb_time.tv_sec,
+                             s_name_data.name, s_name_data.instance,
+                             key);
+
            krb5_free_keyblock_contents(kdc_context, &k5key);
            memset(key, 0, sizeof(key));
            memset(key_s, 0, sizeof(key_s));
@@ -826,8 +853,15 @@ kerberos_v4(struct sockaddr_in *client, KTEXT pkt)
            strncpy(tktrlm, (char *)auth->dat + 3, REALM_SZ);
            tktrlm[REALM_SZ-1] = '\0';
            kvno = (krb5_kvno)auth->dat[2];
-           if (set_tgtkey(tktrlm, kvno)) {
-               lt = klog(L_ERR_UNK,
+           if ((!allow_v4_crossrealm)&&strcmp(tktrlm, local_realm) != 0) {
+             lt = klog(L_ERR_UNK,
+                       "Cross realm ticket from %s denied by policy,", tktrlm);
+             kerb_err_reply(client, pkt,
+                              KERB_ERR_PRINCIPAL_UNKNOWN, lt);
+               return;
+           }
+           if (set_tgtkey(tktrlm, kvno, 0)) {
+             lt = klog(L_ERR_UNK,
                          "FAILED set_tgtkey realm %s, kvno %d. Host: %s ",
                          tktrlm, kvno, inet_ntoa(client_host));
                /* no better error code */
@@ -837,6 +871,19 @@ kerberos_v4(struct sockaddr_in *client, KTEXT pkt)
            }
            kerno = krb_rd_req(auth, "krbtgt", tktrlm, client_host.s_addr,
                ad, 0);
+           if (kerno) {
+               if (set_tgtkey(tktrlm, kvno, 1)) {
+                   lt = klog(L_ERR_UNK,
+                             "FAILED 3des set_tgtkey realm %s, kvno %d. Host: %s ",
+                             tktrlm, kvno, inet_ntoa(client_host));
+                   /* no better error code */
+                   kerb_err_reply(client, pkt,
+                                  KERB_ERR_PRINCIPAL_UNKNOWN, lt);
+                   return;
+               }
+               kerno = krb_rd_req(auth, "krbtgt", tktrlm, client_host.s_addr,
+                                  ad, 0);
+           }
 
            if (kerno) {
                klog(L_ERR_UNK, "FAILED krb_rd_req from %s: %s",
@@ -913,21 +960,13 @@ kerberos_v4(struct sockaddr_in *client, KTEXT pkt)
            des_new_random_key(session_key);
 #endif
 
-           if (K4KDC_ENCTYPE_OK(k5key.enctype)) {
-               krb_create_ticket(tk, k_flags, ad->pname, ad->pinst,
-                                 ad->prealm, client_host.s_addr,
-                                 (char *) session_key, lifetime,
-                                 kerb_time.tv_sec,
-                                 s_name_data.name, s_name_data.instance,
-                                 key);
-           } else {
-               krb_cr_tkt_krb5(tk, k_flags, ad->pname, ad->pinst,
-                               ad->prealm, client_host.s_addr,
-                               (char *) session_key, lifetime,
-                               kerb_time.tv_sec,
-                               s_name_data.name, s_name_data.instance,
-                               &k5key);
-           }
+           /* ALways issue des tickets*/
+           krb_create_ticket(tk, k_flags, ad->pname, ad->pinst,
+                             ad->prealm, client_host.s_addr,
+                             (char *) session_key, lifetime,
+                             kerb_time.tv_sec,
+                             s_name_data.name, s_name_data.instance,
+                             key);
            krb5_free_keyblock_contents(kdc_context, &k5key);
            memset(key, 0, sizeof(key));
            memset(key_s, 0, sizeof(key_s));
@@ -1107,11 +1146,12 @@ check_princ(char *p_name, char *instance, int lifetime, Principal *p,
 
 /* Set the key for krb_rd_req so we can check tgt */
 static int
-set_tgtkey(char *r, krb5_kvno kvno)
+set_tgtkey(char *r, krb5_kvno kvno, krb5_boolean use_3des)
 {
     int     n;
     static char lastrealm[REALM_SZ] = "";
     static int last_kvno = 0;
+    static krb5_boolean last_use_3des = 0;
     static int more;
     Principal p_st;
     Principal *p = &p_st;
@@ -1119,7 +1159,7 @@ set_tgtkey(char *r, krb5_kvno kvno)
     krb5_keyblock k5key;
 
     k5key.contents = NULL;
-    if (!strcmp(lastrealm, r) && last_kvno == kvno)
+    if (!strcmp(lastrealm, r) && last_kvno == kvno && last_use_3des == use_3des)
        return (KSUCCESS);
 
 /*  log("Getting key for %s", r); */
@@ -1141,11 +1181,12 @@ set_tgtkey(char *r, krb5_kvno kvno)
        return KFAILURE;
     }
 
-    if (!K4KDC_ENCTYPE_OK(k5key.enctype)) {
+    if (use_3des&&!K4KDC_ENCTYPE_OK(k5key.enctype)) {
        krb_set_key_krb5(kdc_context, &k5key);
        strncpy(lastrealm, r, sizeof(lastrealm) - 1);
        lastrealm[sizeof(lastrealm) - 1] = '\0';
        last_kvno = kvno;
+       last_use_3des = use_3des;
     } else {
        /* unseal tgt key from master key */
        memcpy(key,                &p->key_low,  4);
index 3e5091cbf3802b27ac86d0aab52ba28d28902f2e..5fb460b0a56d8dae2f5fee0845da247fb8f6640b 100644 (file)
@@ -551,7 +551,7 @@ setup_sam(void)
 void
 usage(char *name)
 {
-    fprintf(stderr, "usage: %s [-d dbpathname] [-r dbrealmname] [-R replaycachename ]\n\t[-m] [-k masterenctype] [-M masterkeyname] [-p port] [-4 v4mode] [-n]\n", name);
+    fprintf(stderr, "usage: %s [-d dbpathname] [-r dbrealmname] [-R replaycachename ]\n\t[-m] [-k masterenctype] [-M masterkeyname] [-p port] [-4 v4mode] [-X] [-n]\n", name);
     return;
 }
 
@@ -606,7 +606,7 @@ initialize_realms(krb5_context kcontext, int argc, char **argv)
      * Loop through the option list.  Each time we encounter a realm name,
      * use the previously scanned options to fill in for defaults.
      */
-    while ((c = getopt(argc, argv, "r:d:mM:k:R:e:p:s:n4:3")) != -1) {
+    while ((c = getopt(argc, argv, "r:d:mM:k:R:e:p:s:n4:X3")) != -1) {
        switch(c) {
        case 'r':                       /* realm name for db */
            if (!find_realm_data(optarg, (krb5_ui_4) strlen(optarg))) {
@@ -662,6 +662,11 @@ initialize_realms(krb5_context kcontext, int argc, char **argv)
            v4mode = strdup(optarg);
 #endif
            break;
+       case 'X':
+#ifdef KRB5_KRB4_COMPAT
+               enable_v4_crossrealm(argv[0]);
+#endif
+               break;
        case '3':
 #ifdef ATHENA_DES3_KLUDGE
            if (krb5_enctypes_list[krb5_enctypes_length-1].etype
index 2a7b6cc54e3f3be3c95d3a9abadabb2efe04dc62..ba03cc0da151ff1faa263037c12a84a88a724fde 100644 (file)
@@ -1,3 +1,11 @@
+2003-03-16  Sam Hartman  <hartmans@mit.edu>
+
+       * krb524d.c (handle_classic_v4): Do not support 3des enctypes as
+       they are insecure.  Also, by default do not allow krb4
+       cross-realm.
+
+       * cnv_tkt_skey.c (krb524_convert_tkt_skey): Don't support 3des tickets
+
 2003-03-12  Ken Raeburn  <raeburn@mit.edu>
 
        * cnv_tkt_skey.c (krb524_convert_tkt_skey): Extract source IP
index 595a1d3929b4c6b355a4619f525fa933bae40cb2..3730ce43c64410696fae1c211ead8aafc8a7cf63 100644 (file)
@@ -184,26 +184,8 @@ int krb524_convert_tkt_skey(context, v5tkt, v4tkt, v5_skey, v4_skey,
                                 sname,
                                 sinst,
                                 v4_skey->contents);
-     } else {
-        /* Force enctype to be raw if using DES3. */
-        if (v4_skey->enctype == ENCTYPE_DES3_CBC_SHA1 ||
-            v4_skey->enctype == ENCTYPE_LOCAL_DES3_HMAC_SHA1)
-            v4_skey->enctype = ENCTYPE_DES3_CBC_RAW;
-        ret = krb524int_krb_cr_tkt_krb5(v4tkt,
-                              0, /* flags */                        
-                              pname,
-                              pinst,
-                              prealm,
-                              sinp->sin_addr.s_addr,
-                              (char *) v5etkt->session->contents,
-                              lifetime,
-                              /* issue_data */
-                              server_time,
-                              sname,
-                              sinst,
-                              v4_skey);
      }
-
+     else abort();
      krb5_free_enc_tkt_part(context, v5etkt);
      v5tkt->enc_part2 = NULL;
      if (ret == KSUCCESS)
index 4995b515fc229978e6c9e181b85f1d8c4517df97..0dce9cbc916aec0992e066ab2acf3e7e55465f82 100644 (file)
@@ -76,6 +76,7 @@ static int debug = 0;
 void *handle = NULL;
 
 int use_keytab, use_master;
+int allow_v4_crossrealm = 0;
 char *keytab = NULL;
 krb5_keytab kt;
 
@@ -137,7 +138,10 @@ int main(argc, argv)
      config_params.mask = 0;
      
      while (argc) {
-         if (strncmp(*argv, "-k", 2) == 0)
+       if (strncmp(*argv, "-X", 2) == 0) {
+        allow_v4_crossrealm = 1;
+       }
+       else if (strncmp(*argv, "-k", 2) == 0)
               use_keytab = 1;
          else if (strncmp(*argv, "-m", 2) == 0)
               use_master = 1;
@@ -524,19 +528,7 @@ handle_classic_v4 (krb5_context context, krb5_ticket *v5tkt,
                                   &v5_service_key, NULL)))
          goto error;
 
-     if ((ret = lookup_service_key(context, v5tkt->server,
-                                  ENCTYPE_DES3_CBC_RAW,
-                                  0, /* highest kvno */
-                                  &v4_service_key, v4kvno)) &&
-        (ret = lookup_service_key(context, v5tkt->server,
-                                  ENCTYPE_LOCAL_DES3_HMAC_SHA1,
-                                  0,
-                                  &v4_service_key, v4kvno)) &&
-        (ret = lookup_service_key(context, v5tkt->server,
-                                  ENCTYPE_DES3_CBC_SHA1,
-                                  0,
-                                  &v4_service_key, v4kvno)) &&
-        (ret = lookup_service_key(context, v5tkt->server,
+     if ( (ret = lookup_service_key(context, v5tkt->server,
                                   ENCTYPE_DES_CBC_CRC,
                                   0,
                                   &v4_service_key, v4kvno)))
@@ -544,8 +536,19 @@ handle_classic_v4 (krb5_context context, krb5_ticket *v5tkt,
 
      if (debug)
          printf("service key retrieved\n");
+     if ((ret = krb5_decrypt_tkt_part(context, &v5_service_key, v5tkt))) {
+       goto error;
+     }
 
-     ret = krb524_convert_tkt_skey(context, v5tkt, &v4tkt, &v5_service_key,
+    if (!(allow_v4_crossrealm || krb5_realm_compare(context, v5tkt->server,
+                                                   v5tkt->enc_part2->client))) {
+ret =  KRB5KDC_ERR_POLICY ;
+ goto error;
+    }
+    krb5_free_enc_tkt_part(context, v5tkt->enc_part2);
+    v5tkt->enc_part2= NULL;
+
+         ret = krb524_convert_tkt_skey(context, v5tkt, &v4tkt, &v5_service_key,
                                   &v4_service_key,
                                   (struct sockaddr_in *)saddr);
      if (ret)
@@ -561,6 +564,9 @@ handle_classic_v4 (krb5_context context, krb5_ticket *v5tkt,
          printf("v4 credentials encoded\n");
 
  error:
+     if (v5tkt->enc_part2)
+        krb5_free_enc_tkt_part(context, v5tkt->enc_part2);
+
      if(v5_service_key.contents)
        krb5_free_keyblock_contents(context, &v5_service_key);
      if (v4_service_key.contents)
index d685be6d9eb6a43a6c7034d321f87ad3bc8d05ff..4592b4c195d1f3acfd7c9045076d58adecbbd028 100644 (file)
@@ -1,3 +1,11 @@
+2003-03-16  Sam Hartman  <hartmans@mit.edu>
+
+       * keytab.c (krb5_ktkdb_get_entry):  Match only against the first
+       enctype  for non-cross-realm tickets so we will only accept
+       tickets that the current configuration would have issued.  For
+       cross-realm tickets be liberal and match against the specified
+       enctype. 
+
 2003-03-05  Tom Yu  <tlyu@mit.edu>
 
        * kdb_xdr.c (krb5_dbe_search_enctype): Check for ktype > 0 rather
index 6ec375ac2b1edd3ce61c5c3c7c0fb39c18e2f89f..6a1dea1524f640ab42c57e22caa47f1df992cde4 100644 (file)
  * or implied warranty.
  * 
  */
+#include <string.h>
 
 #include "k5-int.h"
 #include "kdb_kt.h"
 
+static int
+is_xrealm_tgt(krb5_context, krb5_const_principal);
+
 krb5_error_code krb5_ktkdb_close (krb5_context, krb5_keytab);
 
 krb5_error_code krb5_ktkdb_get_entry (krb5_context, krb5_keytab, krb5_const_principal,
@@ -116,6 +120,8 @@ krb5_ktkdb_get_entry(in_context, id, principal, kvno, enctype, entry)
     krb5_db_entry        db_entry;
     krb5_boolean         more = 0;
     int                  n = 0;
+    int xrealm_tgt = is_xrealm_tgt(context, principal);
+    int similar;
 
     if (ktkdb_ctx)
        context = ktkdb_ctx;
@@ -150,16 +156,31 @@ krb5_ktkdb_get_entry(in_context, id, principal, kvno, enctype, entry)
     if (kerror)
        goto error;
 
+    /* For cross realm tgts, we match whatever enctype is provided;
+     * for other principals, we only match the first enctype that is
+     * found.  Since the TGS and AS code do the same thing, then we
+     * will only successfully decrypt  tickets we have issued.*/
     kerror = krb5_dbe_find_enctype(context, &db_entry,
-                                  enctype, -1, kvno, &key_data);
+                                  xrealm_tgt?enctype:-1,
+                                  -1, kvno, &key_data);
     if (kerror)
        goto error;
 
+
     kerror = krb5_dbekd_decrypt_key_data(context, master_key,
                                         key_data, &entry->key, NULL);
     if (kerror)
        goto error;
 
+    kerror = krb5_c_enctype_compare(context, enctype, entry->key.enctype, &similar);
+    if (kerror)
+       goto error;
+
+    if (!similar) {
+               kerror = KRB5_KDB_NO_PERMITTED_KEY;
+       goto error;
+    }
+
     /*
      * Coerce the enctype of the output keyblock in case we got an
      * inexact match on the enctype.
@@ -176,3 +197,27 @@ krb5_ktkdb_get_entry(in_context, id, principal, kvno, enctype, entry)
     krb5_db_close_database(context);
     return(kerror);
 }
+
+/*
+ * is_xrealm_tgt: Returns true if the principal is a cross-realm  TGT
+ * principal-- a principal with first component  krbtgt and second
+ * component not equal to realm.
+ */
+static int
+is_xrealm_tgt(krb5_context context, krb5_const_principal princ)
+{
+    krb5_data *dat;
+    if (krb5_princ_size(context, princ) != 2)
+       return 0;
+    dat = krb5_princ_component(context, princ, 0);
+    if (strncmp("krbtgt", dat->data, dat->length) != 0)
+       return 0;
+    dat = krb5_princ_component(context, princ, 1);
+    if (dat->length != princ->realm.length)
+       return 1;
+    if (strcmp(dat->data, princ->realm.data) == 0)
+       return 0;
+    return 1;
+
+}
+