From: Theodore Tso <tytso@mit.edu>
Date: Fri, 10 Jun 1994 22:37:30 +0000 (+0000)
Subject: Fixed inter-realm handling; from OCSG/Cliff Neumann
X-Git-Tag: krb5-1.0-beta4~114
X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=bab09a5b9fc4ec1aebce29696694a70160f7b72c;p=krb5.git

Fixed inter-realm handling; from OCSG/Cliff Neumann

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

diff --git a/src/lib/krb5/krb/gc_frm_kdc.c b/src/lib/krb5/krb/gc_frm_kdc.c
index c825efa2f..f45ffeb18 100644
--- a/src/lib/krb5/krb/gc_frm_kdc.c
+++ b/src/lib/krb5/krb/gc_frm_kdc.c
@@ -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
@@ -17,11 +17,12 @@
  * 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);
 }
diff --git a/src/lib/krb5/krb/gc_via_tgt.c b/src/lib/krb5/krb/gc_via_tgt.c
index 23dc668f3..03ae702c4 100644
--- a/src/lib/krb5/krb/gc_via_tgt.c
+++ b/src/lib/krb5/krb/gc_via_tgt.c
@@ -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;