Pass extra argument to krb5_walk_realm_tree.
[krb5.git] / src / lib / krb5 / krb / gc_frm_kdc.c
index 71b3066b14c9b026f4e61bf0d8b06af61be50416..4aa8d9dea820ae58c102d63e8ae8ee6f2e29404f 100644 (file)
@@ -2,10 +2,25 @@
  * $Source$
  * $Author$
  *
- * Copyright 1990 by the Massachusetts Institute of Technology.
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
  *
- * For copying and distribution information, please see the file
- * <krb5/mit-copyright.h>.
+ * Export of this software from the United States of America is assumed
+ *   to require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * 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
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
  *
  * Get credentials from some KDC somewhere, possibly accumulating tgts
  * along the way.
@@ -16,12 +31,8 @@ static char rcsid_gcfkdc_c[] =
 "$Id$";
 #endif /* !lint & !SABER */
 
-#include <krb5/copyright.h>
 
 #include <krb5/krb5.h>
-#include <krb5/krb5_err.h>
-#include <errno.h>
-
 #include <krb5/ext-proto.h>
 #include "int-proto.h"
 
@@ -62,9 +73,11 @@ krb5_get_cred_from_kdc (ccache, cred, tgts)
 {
     krb5_creds tgt, tgtq;
     krb5_creds **ret_tgts = 0;
-    krb5_principal *tgs_list, next_server;
+    krb5_principal *tgs_list, *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 */
@@ -81,24 +94,29 @@ krb5_get_cred_from_kdc (ccache, cred, tgts)
      * look for ticket with:
      * client == cred->client,
      * server == "krbtgt/realmof(cred->server)@realmof(cred->client)"
+     *
+     * (actually, the ticket may be issued by some other intermediate
+     *  realm's KDC; so we use KRB5_TC_MATCH_SRV_NAMEONLY below)
      */
 
     /*
-     * XXX we're sharing some substructure here, which is
-     * probably not safe...
+     * we're sharing some substructure here, which is dangerous.
+     * Be sure that if you muck with things here that tgtq.* doesn't share
+     * any substructure before you deallocate/clean up/whatever.
      */
+    memset((char *)&tgtq, 0, sizeof(tgtq));
     tgtq.client = cred->client;
 
-    if (retval = krb5_tgtname(krb5_princ_realm(cred->client),
-                             krb5_princ_realm(cred->server), &tgtq.server))
+    if (retval = krb5_tgtname(krb5_princ_realm(cred->server),
+                             krb5_princ_realm(cred->client), &final_server))
        return retval;
+    tgtq.server = final_server;
 
     /* try to fetch it directly */
     retval = krb5_cc_retrieve_cred (ccache,
-                                   0,  /* default is client & server */
+                                   KRB5_TC_MATCH_SRV_NAMEONLY,
                                    &tgtq,
                                    &tgt);
-    krb5_free_principal(tgtq.server);
 
     if (retval != 0) {
        if (retval != KRB5_CC_NOTFOUND)
@@ -107,21 +125,23 @@ krb5_get_cred_from_kdc (ccache, cred, tgts)
           across realms to get the right TGT. */
 
        /* get a list of realms to consult */
-       retval = krb5_walk_realm_tree(cred->client, cred->server, &tgs_list);
+       retval = krb5_walk_realm_tree(krb5_princ_realm(cred->client),
+                                     krb5_princ_realm(cred->server),
+                                     &tgs_list, KRB5_REALM_BRANCH_CHAR);
        if (retval)
            goto out;
        /* 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[0]; next_server; next_server++);
-       nservers = next_server - tgs_list[0];
+       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[0]; next_server--) {
-           tgtq.server = next_server;
+       for (; next_server >= tgs_list; next_server--) {
+           tgtq.server = *next_server;
            retval = krb5_cc_retrieve_cred (ccache,
-                                           0, /* default is client & server */
+                                           KRB5_TC_MATCH_SRV_NAMEONLY,
                                            &tgtq,
                                            &tgt);
            if (retval) {
@@ -134,25 +154,44 @@ krb5_get_cred_from_kdc (ccache, cred, tgts)
            next_server++;
            break;                      /* found one! */
        }
-       krb5_free_realm_tree(tgs_list);
+       if (next_server < tgs_list) {
+           /* didn't find any */
+           retval = KRB5_NO_TKT_IN_RLM;
+           krb5_free_realm_tree(tgs_list);
+           goto out;
+       }
        /* allocate storage for TGT pointers. */
        ret_tgts = (krb5_creds **)calloc(nservers+1, sizeof(krb5_creds));
        if (!ret_tgts) {
            retval = ENOMEM;
+           krb5_free_realm_tree(tgs_list);
            goto out;
        }
        *tgts = ret_tgts;
-       for (nservers = 0; next_server; next_server++, nservers++) {
+       for (nservers = 0; *next_server; next_server++, nservers++) {
+
+           krb5_data *tmpdata;
 
            if (!valid_keytype(tgt.keyblock.keytype)) {
                retval = KRB5_PROG_KEYTYPE_NOSUPP;
+               krb5_free_realm_tree(tgs_list);
                goto out;
            }
            /* now get the TGTs */
+           memset((char *)&tgtq, 0, sizeof(tgtq));
            tgtq.times = tgt.times;
            tgtq.client = tgt.client;
-           tgtq.server = next_server;
-           tgtq.is_skey = FALSE;       
+
+           /* ask each realm for a tgt to the end */
+           if (retval = krb5_copy_data(krb5_princ_realm(*next_server), &tmpdata)) {
+               krb5_free_realm_tree(tgs_list);
+               goto out;
+           }
+           krb5_free_data(krb5_princ_realm(final_server));
+           krb5_princ_set_realm(final_server, tmpdata);
+           tgtq.server = final_server;
+
+           tgtq.is_skey = FALSE;
            tgtq.ticket_flags = tgt.ticket_flags;
 
            etype = krb5_keytype_array[tgt.keyblock.keytype]->system->proto_enctype;
@@ -160,14 +199,34 @@ krb5_get_cred_from_kdc (ccache, cred, tgts)
                                               flags2options(tgtq.ticket_flags),
                                               etype,
                                               krb5_kdc_req_sumtype,
-                                              &tgtq))
+                                              &tgtq)) {
+               krb5_free_realm_tree(tgs_list);
+               goto out;
+           }
+           /* 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! */
+               krb5_free_realm_tree(tgs_list);
+               retval = KRB5_KDCREP_MODIFIED;
                goto out;
+           }
+                                                          
            /* save tgt in return array */
-           if (retval = krb5_copy_creds(&tgtq, &ret_tgts[nservers]))
+           if (retval = krb5_copy_creds(&tgtq, &ret_tgts[nservers])) {
+               krb5_free_realm_tree(tgs_list);
                goto out;
-           /* XXX need to clean up stuff pointed to by tgtq? */
-           tgt = tgtq;
+           }
+           tgt = *ret_tgts[nservers];
+           returning_tgt = 1;          /* don't free it below... */
+           tgtq.client = 0;
+           tgtq.server = 0;
+           krb5_free_cred_contents(&tgtq);
        }
+       krb5_free_realm_tree(tgs_list);
     }
     /* got/finally have tgt! */
     if (!valid_keytype(tgt.keyblock.keytype)) {
@@ -176,11 +235,21 @@ krb5_get_cred_from_kdc (ccache, cred, tgts)
     }
     etype = krb5_keytype_array[tgt.keyblock.keytype]->system->proto_enctype;
 
-    retval = krb5_get_cred_via_tgt(&tgt,
-                                  flags2options(tgt.ticket_flags),
-                                  etype,
-                                  krb5_kdc_req_sumtype,
-                                  cred);
+    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);
+    
+    if (!returning_tgt)
+       krb5_free_cred_contents(&tgt);
 out:
+    krb5_free_principal(final_server);
     return retval;
 }