Fixed inter-realm handling; from OCSG/Cliff Neumann
authorTheodore Tso <tytso@mit.edu>
Fri, 10 Jun 1994 22:37:30 +0000 (22:37 +0000)
committerTheodore Tso <tytso@mit.edu>
Fri, 10 Jun 1994 22:37:30 +0000 (22:37 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@3739 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/krb5/krb/gc_frm_kdc.c
src/lib/krb5/krb/gc_via_tgt.c

index c825efa2fa43d6d69f5198a6f21f27dc58c561cd..f45ffeb187db28f1984ea1c5362d594a15eb8238 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * $Source$
- * $Author$
- *
- * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * Copyright (c) 1994 by the Massachusetts Institute of Technology.
+ * Copyright (c) 1994 CyberSAFE Corporation
+ * Copyright (c) 1993 Open Computing Security Group
+ * Copyright (c) 1990,1991 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
  * this permission notice appear in supporting documentation, and that
  * the name of M.I.T. not be used in advertising or publicity pertaining
  * to distribution of the software without specific, written prior
- * permission.  M.I.T. makes no representations about the suitability of
+ * permission.  Neither M.I.T., the Open Computing Security Group, nor 
+ * CyberSAFE Corporation make any representations about the suitability of
  * this software for any purpose.  It is provided "as is" without express
  * or implied warranty.
  * 
- *
+ * krb5_get_cred_from_kdc()
  * Get credentials from some KDC somewhere, possibly accumulating tgts
  * along the way.
  */
@@ -31,7 +32,7 @@ static char rcsid_gcfkdc_c[] =
 "$Id$";
 #endif /* !lint & !SABER */
 
-
+#include <stdio.h>
 #include <krb5/krb5.h>
 #include <krb5/ext-proto.h>
 #include "int-proto.h"
@@ -62,186 +63,355 @@ static char rcsid_gcfkdc_c[] =
 
 extern krb5_cksumtype krb5_kdc_req_sumtype;
 
-/* helper function: convert flags to necessary KDC options */
-#define flags2options(flags) (flags & KDC_TKT_COMMON_MASK)
+/* helper macro: convert flags to necessary KDC options */
+
+#define FLAGS2OPTS(flags) (flags & KDC_TKT_COMMON_MASK)
+#define TGT_ETYPE \
+      krb5_keytype_array[tgt.keyblock.keytype]->system->proto_enctype;
 
-krb5_error_code
-krb5_get_cred_from_kdc (ccache, cred, tgts)
-    krb5_ccache ccache;
-    krb5_creds *cred;
-    krb5_creds ***tgts;
+
+#ifdef __STDC__
+krb5_error_code krb5_get_cred_from_kdc(
+  krb5_ccache ccache,
+  krb5_creds  *cred,
+  krb5_creds  ***tgts
+)
+#else
+krb5_error_code krb5_get_cred_from_kdc(ccache, cred, tgts)
+  krb5_ccache ccache;
+  krb5_creds  *cred;
+  krb5_creds  ***tgts;
+#endif
 {
-    krb5_creds tgt, tgtq;
-    krb5_creds **ret_tgts = 0;
-    krb5_principal *tgs_list = 0, *next_server;
-    krb5_principal final_server;
-    krb5_error_code retval;
-    int nservers;
-    int returning_tgt = 0;
-    krb5_enctype etype;
-
-    /* in case we never get a TGT, zero the return */
-    *tgts = 0;
-    
-    memset((char *)&tgtq, 0, sizeof(tgtq));
-    memset((char *)&tgt, 0, sizeof(tgt));
+  krb5_creds      **ret_tgts = NULL;
+  int             ntgts = 0;
+
+  krb5_creds      tgt, tgtq;
+  krb5_enctype    etype;
+  krb5_error_code retval;
+  krb5_principal  int_server = NULL;    /* Intermediate server for request */
+
+  krb5_principal  *tgs_list = NULL;
+  krb5_principal  *top_server = NULL;
+  krb5_principal  *next_server = NULL;
+  int             nservers = 0;
+
+  /* in case we never get a TGT, zero the return */
+
+  *tgts = NULL;
+     
+  memset((char *)&tgtq, 0, sizeof(tgtq));
+  memset((char *)&tgt, 0, sizeof(tgt));
+
+  /*
+   * we know that the desired credentials aren't in the cache yet.
+   *
+   * To get them, we first need a tgt for the realm of the server.
+   * first, we see if we have such a TGT in cache.  if not, then
+   * we ask the kdc to give us one.  if that doesn't work, then
+   * we try to get a tgt for a realm that is closest to the target.
+   * once we have that, then we ask that realm if it can give us
+   * tgt for the target.  if not, we do the process over with this
+   * new tgt.
+   */
+  
+  /*
+   * (the ticket may be issued by some other intermediate
+   *  realm's KDC; so we use KRB5_TC_MATCH_SRV_NAMEONLY)
+   */
+  if (retval = krb5_copy_principal(cred->client, &tgtq.client))
+      goto cleanup;
+
+  /* get target tgt from cache */
+  if (retval = krb5_tgtname(krb5_princ_realm(cred->server),
+                            krb5_princ_realm(cred->client),
+                            &int_server)) {
+      goto cleanup;
+  }
+
+  if (retval = krb5_copy_principal(int_server, &tgtq.server)) {
+      goto cleanup;
+  }
+
+  if (retval = krb5_cc_retrieve_cred(ccache,
+                                     KRB5_TC_MATCH_SRV_NAMEONLY,
+                                     &tgtq,
+                                     &tgt)) {
+
+    if (retval != KRB5_CC_NOTFOUND) {
+       goto cleanup;
+    }
 
     /*
-     * we know that the desired credentials aren't in the cache yet.
-     *
-     * To get them, we first need a tgt for the realm of the server.
-     * first, we see if we have such a TGT in cache.
+     * Note that we want to request a TGT from our local KDC, even
+     * if we already have a TGT for some intermediate realm.  The 
+     * reason is that our local KDC may have a shortcut to the
+     * destination realm, and if it does we want to use the
+     * shortcut because it will provide greater security. - bcn
      */
+  
+    /*
+     * didn't find it in the cache so it is time to get a local
+     * tgt and walk the realms tree.
+     */
+    krb5_free_principal(int_server);
+    int_server = NULL;
+    if (retval = krb5_tgtname(krb5_princ_realm(cred->client),
+                              krb5_princ_realm(cred->client),
+                              &int_server)) {
+       goto cleanup;
+    }
+  
+    krb5_free_cred_contents(&tgtq);
+    memset((char *)&tgtq, 0, sizeof(tgtq));
+    if(retval = krb5_copy_principal(cred->client, &tgtq.client))
+       goto cleanup;
+    if(retval = krb5_copy_principal(int_server, &tgtq.server))
+       goto cleanup;
+
+    if (retval = krb5_cc_retrieve_cred(ccache,
+                                       KRB5_TC_MATCH_SRV_NAMEONLY,
+                                       &tgtq,
+                                       &tgt)) {
+       goto cleanup;
+    }
+  
+    /* get a list of realms to consult */
+  
+    if (retval = krb5_walk_realm_tree(krb5_princ_realm(cred->client),
+                                      krb5_princ_realm(cred->server),
+                                      &tgs_list, 
+                                      KRB5_REALM_BRANCH_CHAR)) {
+       goto cleanup;
+    }
+  
+    for (nservers = 0; tgs_list[nservers]; nservers++)
+      ;
+  
+    /* allocate storage for TGT pointers. */
+    
+    if (!(ret_tgts = (krb5_creds **) calloc(nservers+1, sizeof(krb5_creds)))) {
+      retval = ENOMEM;
+      goto cleanup;
+    }
+    *tgts = ret_tgts;
     
     /*
-     * look for ticket with:
-     * client == cred->client,
-     * server == "krbtgt/realmof(cred->server)@realmof(cred->client)"
+     * step one is to take the current tgt and see if there is a tgt for
+     * krbtgt/realmof(target)@realmof(tgt).  if not, try to get one with
+     * the tgt.
+     *
+     * if we don't get a tgt for the target, then try to find a tgt as
+     * close to the target realm as possible. at each step if there isn't
+     * a tgt in the cache we have to try and get one with our latest tgt.
+     * once we have a tgt for a closer realm, we go back to step one.
      *
-     * (actually, the ticket may be issued by some other intermediate
-     *  realm's KDC; so we use KRB5_TC_MATCH_SRV_NAMEONLY below)
+     * once we have a tgt for the target, we go try and get credentials.
      */
+  
+    for (top_server = tgs_list;
+         top_server < tgs_list + nservers;
+         top_server = next_server) {
+    
+      /* look in cache for a tgt for the destination */
+    
+      krb5_free_cred_contents(&tgtq);
+      memset(&tgtq, 0, sizeof(tgtq));
+      if(retval = krb5_copy_principal(tgt.client, &tgtq.client))
+         goto cleanup;
 
-    if (retval = krb5_copy_principal(cred->client, &tgtq.client))
-       goto errout;
-    if (retval = krb5_tgtname(krb5_princ_realm(cred->server),
-                             krb5_princ_realm(cred->client), &final_server))
-       goto errout;
-    if (retval = krb5_copy_principal(final_server, &tgtq.server))
-       goto errout;
-
-    retval = krb5_cc_retrieve_cred (ccache,
-                                   KRB5_TC_MATCH_SRV_NAMEONLY,
-                                   &tgtq,
-                                   &tgt);
-
-    if (retval != 0) {
-       if (retval != KRB5_CC_NOTFOUND)
-           goto errout;
-       /* don't have the right TGT in the cred cache.  Time to iterate
-          across realms to get the right TGT. */
-
-       /* get a list of realms to consult */
-       if (retval = krb5_walk_realm_tree(krb5_princ_realm(cred->client),
-                                         krb5_princ_realm(cred->server),
-                                         &tgs_list, KRB5_REALM_BRANCH_CHAR))
-           goto errout;
-
-       /* walk the list BACKWARDS until we find a cached
-          TGT, then move forward obtaining TGTs until we get the last
-          TGT needed */
-       for (next_server = tgs_list; *next_server;)
-               next_server++;
-       nservers = next_server - tgs_list;
-       next_server--;
-
-       /* next_server now points to the last TGT */
-       for (; next_server >= tgs_list; next_server--) {
-           krb5_free_principal(tgtq.server);
-           if (retval = krb5_copy_principal(*next_server, &tgt.server))
-               goto errout;
-           retval = krb5_cc_retrieve_cred (ccache,
-                                           KRB5_TC_MATCH_SRV_NAMEONLY,
-                                           &tgtq,
-                                           &tgt);
-           if (retval) {
-               if (retval != KRB5_CC_NOTFOUND)
-                   goto errout;
-               continue;
-           }
-           next_server++;
-           break;                      /* found one! */
-       }
-       if (next_server < tgs_list) {
-           /* didn't find any */
-           retval = KRB5_NO_TKT_IN_RLM;
-           goto errout;
+      krb5_free_principal(int_server);
+      int_server = NULL;
+      if (retval = krb5_tgtname(krb5_princ_realm(cred->server),
+                               krb5_princ_realm(*top_server),
+                               &int_server)) {
+         goto cleanup;
+      }
+    
+      if(retval = krb5_copy_principal(int_server, &tgtq.server))
+         goto cleanup;
+
+      if (retval = krb5_cc_retrieve_cred(ccache,
+                                        KRB5_TC_MATCH_SRV_NAMEONLY,
+                                        &tgtq,
+                                        &tgt)) {
+    
+       if (retval != KRB5_CC_NOTFOUND) {
+           goto cleanup;
        }
-       /* allocate storage for TGT pointers. */
-       ret_tgts = (krb5_creds **)calloc(nservers+1, sizeof(krb5_creds));
-       if (!ret_tgts) {
-           retval = ENOMEM;
-           goto errout;
+  
+       /* didn't find it in the cache so try and get one */
+       /* with current tgt.                              */
+    
+       if (!valid_keytype(tgt.keyblock.keytype)) {
+           retval = KRB5_PROG_KEYTYPE_NOSUPP;
+           goto cleanup;
        }
-       *tgts = ret_tgts;
-       for (nservers = 0; *next_server; next_server++, nservers++) {
-           krb5_data *tmpdata;
-
-           if (!valid_keytype(tgt.keyblock.keytype)) {
-               retval = KRB5_PROG_KEYTYPE_NOSUPP;
-               goto errout;
-           }
-           /* now get the TGTs */
+    
+       krb5_free_cred_contents(&tgtq);
+       memset(&tgtq, 0, sizeof(tgtq));
+       tgtq.times        = tgt.times;
+       if (retval = krb5_copy_principal(tgt.client, &tgtq.client))
+           goto cleanup;
+       if(retval = krb5_copy_principal(int_server, &tgtq.server))
+           goto cleanup;
+       tgtq.is_skey      = FALSE;
+       tgtq.ticket_flags = tgt.ticket_flags;
+       etype             = TGT_ETYPE;
+       if(retval = krb5_get_cred_via_tgt(&tgt,
+                                         FLAGS2OPTS(tgtq.ticket_flags),
+                                         etype,
+                                         krb5_kdc_req_sumtype,
+                                         &tgtq)) {
+             
+       /*
+       * couldn't get one so now loop backwards through the realms
+       * list and try and get a tgt for a realm as close to the
+       * target as possible. the kdc should give us a tgt for the
+       * closest one it knows about, but not all kdc's do this yet.
+       */
+  
+         for (next_server = tgs_list + nservers - 1;
+              next_server > top_server;
+              next_server--) {
            krb5_free_cred_contents(&tgtq);
-           memset((char *)&tgtq, 0, sizeof(tgtq));
-           tgtq.times = tgt.times;
+           memset(&tgtq, 0, sizeof(tgtq));
            if (retval = krb5_copy_principal(tgt.client, &tgtq.client))
-               goto errout;
+               goto cleanup;
 
-           /* ask each realm for a tgt to the end */
-           if (retval = krb5_copy_data(krb5_princ_realm(*next_server),
-                                       &tmpdata)) {
-               goto errout;
+           krb5_free_principal(int_server);
+           int_server = NULL;
+           if (retval = krb5_tgtname(krb5_princ_realm(*next_server),
+                                     krb5_princ_realm(*top_server),
+                                     &int_server)) {
+               goto cleanup;
            }
-           free(krb5_princ_realm(final_server)->data);
-           krb5_princ_set_realm(final_server, tmpdata);
-           if (retval = krb5_copy_principal(final_server, &tgtq.server))
-               goto errout;
-
-           tgtq.is_skey = FALSE;
-           tgtq.ticket_flags = tgt.ticket_flags;
-
-           etype = krb5_keytype_array[tgt.keyblock.keytype]->system->proto_enctype;
-           if (retval = krb5_get_cred_via_tgt(&tgt,
-                                              flags2options(tgtq.ticket_flags),
-                                              etype,
-                                              krb5_kdc_req_sumtype,
-                                              &tgtq))
-               goto errout;
-
-           /* make sure the returned ticket is somewhere in the remaining
-              list, but we can tolerate different expected issuing realms */
-           while (*++next_server &&
-                  !krb5_principal_compare(&(next_server[0])[1],
-                                          &(tgtq.server[1])));
-           if (!next_server) {
-               /* what we got back wasn't in the list! */
-               retval = KRB5_KDCREP_MODIFIED;
-               goto errout;
+    
+           if(retval = krb5_copy_principal(int_server, &tgtq.server))
+               goto cleanup;
+
+           if(retval = krb5_cc_retrieve_cred(ccache,
+                                             KRB5_TC_MATCH_SRV_NAMEONLY,
+                                             &tgtq,
+                                             &tgt)) {
+             if (retval != KRB5_CC_NOTFOUND) {
+                 goto cleanup;
+             }
+  
+             /* not in the cache so try and get one with our current tgt. */
+  
+             if (!valid_keytype(tgt.keyblock.keytype)) {
+                 retval = KRB5_PROG_KEYTYPE_NOSUPP;
+                 goto cleanup;
+             }
+            
+             krb5_free_cred_contents(&tgtq);
+             memset(&tgtq, 0, sizeof(tgtq));
+             tgtq.times        = tgt.times;
+             if (retval = krb5_copy_principal(tgt.client, &tgtq.client))
+                 goto cleanup;
+             if(retval = krb5_copy_principal(int_server, &tgtq.server))
+                 goto cleanup;
+             tgtq.is_skey      = FALSE;
+             tgtq.ticket_flags = tgt.ticket_flags;
+             etype             = TGT_ETYPE;
+             if (retval = krb5_get_cred_via_tgt(&tgt,
+                                                 FLAGS2OPTS(tgtq.ticket_flags),
+                                                 etype,
+                                                 krb5_kdc_req_sumtype,
+                                                 &tgtq)) {
+                 continue;
+             }
+             
+             /* save tgt in return array */
+             if (retval = krb5_copy_creds(&tgtq, &ret_tgts[ntgts])) {
+                 goto cleanup;
+             }
+             
+             tgt = *ret_tgts[ntgts++];
            }
-                                                          
-           /* save tgt in return array */
-           if (retval = krb5_copy_creds(&tgtq, &ret_tgts[nservers]))
-               goto errout;
-           tgt = *ret_tgts[nservers];
-           returning_tgt = 1;          /* don't free it below... */
+          
+           /* got one as close as possible, now start all over */
+  
+           break;
+         }
+  
+         if (next_server == top_server) {
+             goto cleanup;
+         }
+         continue;
+        }
+       /*
+        * Got a tgt.  If it is for the target realm we can go try for the
+        * credentials.  If it is not for the target realm, then make sure it
+        * is in the realms hierarchy and if so, save it and start the loop
+        * over from there.  Note that we only need to compare the instance
+        * names since that is the target realm of the tgt.
+        */
+    
+       for (next_server = top_server; *next_server; next_server++) {
+            krb5_data *realm_1 = krb5_princ_component(next_server[0], 1);
+            krb5_data *realm_2 = krb5_princ_component(tgtq.server, 1);
+            if (realm_1->length == realm_2->length &&
+                !memcmp(realm_1->data, realm_2->data, realm_1->length)) {
+               break;
+            }
        }
+
+       if (!next_server) {
+           retval = KRB5_KDCREP_MODIFIED;
+           goto cleanup;
+       }
+
+       if (retval = krb5_copy_creds(&tgtq, &ret_tgts[ntgts])) {
+           goto cleanup;
+       }
+    
+        tgt = *ret_tgts[ntgts++];
+
+        /* we're done if it is the target */
+
+        if (!*next_server++) break;
+      }
     }
-    /* got/finally have tgt! */
-    if (!valid_keytype(tgt.keyblock.keytype)) {
-       retval = KRB5_PROG_KEYTYPE_NOSUPP;
-       goto errout;
-    }
-    etype = krb5_keytype_array[tgt.keyblock.keytype]->system->proto_enctype;
-
-    if (cred->second_ticket.length)
-       retval = krb5_get_cred_via_2tgt(&tgt,
-                                      KDC_OPT_ENC_TKT_IN_SKEY | flags2options(tgt.ticket_flags),
-                                      etype, krb5_kdc_req_sumtype, cred);
-
-    else
-       retval = krb5_get_cred_via_tgt(&tgt,
-                                      flags2options(tgt.ticket_flags),
-                                      etype,
-                                      krb5_kdc_req_sumtype,
-                                      cred);
-errout:
-    if (!returning_tgt)
-       krb5_free_cred_contents(&tgt);
-    if (final_server)
-           krb5_free_principal(final_server);
-    krb5_free_cred_contents(&tgtq);
-    if (tgs_list)
-       krb5_free_realm_tree(tgs_list);
-    return retval;
+  }
+
+  /* got/finally have tgt!  try for the creds */
+
+  if (!valid_keytype(tgt.keyblock.keytype)) {
+    retval = KRB5_PROG_KEYTYPE_NOSUPP;
+    goto cleanup;
+  }
+
+  etype = TGT_ETYPE;
+  if (cred->second_ticket.length) {
+      retval = krb5_get_cred_via_2tgt(&tgt,
+                                     KDC_OPT_ENC_TKT_IN_SKEY |
+                                     FLAGS2OPTS(tgt.ticket_flags),
+                                     etype,
+                                     krb5_kdc_req_sumtype,
+                                     cred);
+  }
+  else {
+      retval = krb5_get_cred_via_tgt(&tgt,
+                                   FLAGS2OPTS(tgt.ticket_flags), 
+                                   etype,
+                                   krb5_kdc_req_sumtype,
+                                   cred);
+  }
+
+  /* cleanup and return */
+
+cleanup:
+
+  if(tgs_list)  krb5_free_realm_tree(tgs_list);
+  krb5_free_cred_contents(&tgtq); 
+  if (int_server) krb5_free_principal(int_server); 
+  if (ntgts == 0) {
+      *tgts = NULL;
+      if (ret_tgts)  free(ret_tgts);
+      krb5_free_cred_contents(&tgt);
+  }
+  return(retval);
 }
index 23dc668f36ac8822ec6c5e814c5016aca0033b43..03ae702c43bbfd79ac32d0f8e648aaa08bc3c121 100644 (file)
@@ -58,17 +58,20 @@ OLDDECLARG(krb5_creds *, cred)
     krb5_response tgsrep;
 
     /* tgt->client must be equal to cred->client */
-    /* tgt->server must be equal to krbtgt/realmof(cred->client) */
+
     if (!krb5_principal_compare(tgt->client, cred->client))
        return KRB5_PRINC_NOMATCH;
 
     if (!tgt->ticket.length)
        return(KRB5_NO_TKT_SUPPLIED);
 
-    if (retval = krb5_tgtname(krb5_princ_realm(cred->server),
-                             krb5_princ_realm(cred->client), &tempprinc))
+    /* check if we have the right TGT                    */
+    /* tgt->server must be equal to                      */
+    /* krbtgt/realmof(cred->server)@realmof(tgt->server) */
+
+    if(retval = krb5_tgtname(krb5_princ_realm(cred->server),
+                            krb5_princ_realm(tgt->server), &tempprinc))
        return(retval);
-    
     if (!krb5_principal_compare(tempprinc, tgt->server)) {
        krb5_free_principal(tempprinc);
        return KRB5_PRINC_NOMATCH;