Rewrite walk_rtree.c to handle hierarchical traversal better and to be
authorTom Yu <tlyu@mit.edu>
Fri, 2 Jan 2009 01:40:41 +0000 (01:40 +0000)
committerTom Yu <tlyu@mit.edu>
Fri, 2 Jan 2009 01:40:41 +0000 (01:40 +0000)
less convoluted.  Update test cases.

ticket: 5947

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

src/lib/krb5/krb/Makefile.in
src/lib/krb5/krb/walk_rtree.c
src/lib/krb5/krb/walktree-tests

index ce161666b57ec8da9fd92aff1a88760f492c6bda..1a204dfd270bba6ddf3225f88624add5d11220ce 100644 (file)
@@ -294,7 +294,7 @@ clean-unix:: clean-libobjs
 COMERRLIB=$(TOPLIBD)/libcom_err.a
 
 T_WALK_RTREE_OBJS= t_walk_rtree.o walk_rtree.o tgtname.o unparse.o \
-       free_rtree.o bld_pr_ext.o 
+       free_rtree.o bld_pr_ext.o copy_data.o
 
 T_KERB_OBJS= t_kerb.o conv_princ.o unparse.o set_realm.o str_conv.o
 
@@ -351,8 +351,8 @@ check-unix:: $(TEST_PROGS)
                $(RUN_SETUP) $(VALGRIND) ./t_ser
        $(RUN_SETUP) $(VALGRIND) ./t_deltat
        $(RUN_SETUP) $(VALGRIND) sh $(srcdir)/transit-tests
-       : known to fail "http://krbdev.mit.edu/rt/Ticket/Display.html?id=5947"
-       -$(RUN_SETUP) $(VALGRIND) sh $(srcdir)/walktree-tests
+       KRB5_CONFIG=$(srcdir)/t_krb5.conf ; export KRB5_CONFIG ;\
+               $(RUN_SETUP) $(VALGRIND) sh $(srcdir)/walktree-tests
 
 clean::
        $(RM) $(OUTPRE)t_walk_rtree$(EXEEXT) $(OUTPRE)t_walk_rtree.$(OBJEXT) \
index b1b2627c8dfe03c4217bfe80688316f20acf5a60..4cebce5267c19bb1688e845787fea6cfcda3984f 100644 (file)
@@ -1,14 +1,14 @@
 /*
  * lib/krb5/krb/walk_rtree.c
  *
- * Copyright 1990,1991,2008 by the Massachusetts Institute of Technology.
+ * Copyright 1990,1991,2008,2009 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
  *   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
  * 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.
- * 
  *
  * krb5_walk_realm_tree()
+ *
+ * internal function, used by krb5_get_cred_from_kdc()
  */
 
+#include "k5-int.h"
+#include "int-proto.h"
+
+/*
+ * Structure to help with finding the common suffix between client and
+ * server realm during hierarchical traversal.
+ */
+struct hstate {
+    char *str;
+    size_t len;
+    char *tail;
+    char *dot;
+};
+
+static krb5_error_code
+rtree_capath_tree(
+    krb5_context context,
+    const krb5_data *client,
+    const krb5_data *server,
+    char **vals,
+    krb5_principal **tree);
+
+static krb5_error_code
+rtree_capath_vals(
+    krb5_context context,
+    const krb5_data *client,
+    const krb5_data *server,
+    char ***vals);
+
+static krb5_error_code
+rtree_hier_tree(
+    krb5_context context,
+    const krb5_data *client,
+    const krb5_data *server,
+    krb5_principal **rettree,
+    int sep);
+
+static krb5_error_code
+rtree_hier_realms(
+    krb5_context context,
+    const krb5_data *client,
+    const krb5_data *server,
+    krb5_data **realms,
+    size_t *nrealms,
+    int sep);
+
+static krb5_error_code
+rtree_hier_tweens(
+    krb5_context context,
+    struct hstate *realm,
+    krb5_data **tweens,
+    size_t *ntweens,
+    int dotail,
+    int sep);
+
+static void
+adjtail(struct hstate *c, struct hstate *s, int sep);
+
+static void
+comtail(struct hstate *c, struct hstate *s, int sep);
+
+krb5_error_code
+krb5_walk_realm_tree(
+    krb5_context context,
+    const krb5_data *client,
+    const krb5_data *server,
+    krb5_principal **tree,
+    int realm_sep)
+{
+    krb5_error_code retval = 0;
+    char **capvals;
+
+    if (client->data == NULL || server->data == NULL)
+       return KRB5_NO_TKT_IN_RLM;
+
+    if (client->length == server->length &&
+       memcmp(client->data, server->data, server->length) == 0) {
+       return KRB5_NO_TKT_IN_RLM;
+    }
+    retval = rtree_capath_vals(context, client, server, &capvals);
+    if (retval)
+       return retval;
+
+    if (capvals != NULL) {
+       retval = rtree_capath_tree(context, client, server, capvals, tree);
+       return retval;
+    }
+
+    retval = rtree_hier_tree(context, client, server, tree, realm_sep);
+    return retval;
+}
+
 /* ANL - Modified to allow Configurable Authentication Paths.
  * This modification removes the restriction on the choice of realm
  * names, i.e. they nolonger have to be hierarchical. This
  *             NERSC.GOV = ES.NET
  *             PNL.GOV = ES.NET
  *             ES.NET = .
- *             HAL.COM = K5.MOON
- *             HAL.COM = K5.JUPITER
+ *             HAL.COM = K5.MOON
+ *             HAL.COM = K5.JUPITER
  * }
  * NERSC.GOV = {
  *             ANL.GOV = ES.NET
  *             ANL.GOV = ES.NET
  * }
  * ES.NET = {
- *             ANL.GOV = .
+ *             ANL.GOV = .
  * }
  * HAL.COM = {
  *             ANL.GOV = K5.JUPITER
  * will work together.
  * DEE - 5/23/95
  */
-#include "k5-int.h"
-#include "int-proto.h"
 
-/* internal function, used by krb5_get_cred_from_kdc() */
+/*
+ * Build a tree given a set of profile values retrieved by
+ * walk_rtree_capath_vals().
+ */
+static krb5_error_code
+rtree_capath_tree(
+    krb5_context context,
+    const krb5_data *client,
+    const krb5_data *server,
+    char **vals,
+    krb5_principal **rettree)
+{
+    krb5_error_code retval = 0;
+    unsigned int nvals, nlinks, nprincs, i;
+    krb5_data srcrealm, dstrealm;
+    krb5_principal *tree, *pprinc;
 
-#ifndef min
-#define min(x,y) ((x) < (y) ? (x) : (y))
-#define max(x,y) ((x) > (y) ? (x) : (y))
-#endif
+    *rettree = NULL;
+    tree = pprinc = NULL;
+    for (nvals = 0; vals[nvals] != NULL; nvals++)
+       ;
+    if (vals[0] != NULL && *vals[0] == '.') {
+       nlinks = 0;
+    } else {
+       nlinks = nvals;
+    }
+    nprincs = nlinks + 2;
+    tree = calloc(nprincs + 1, sizeof(krb5_principal));
+    if (tree == NULL) {
+       retval = ENOMEM;
+       goto error;
+    }
+    for (i = 0; i < nprincs + 1; i++)
+       tree[i] = NULL;
+    /* Invariant: PPRINC points one past end of list. */
+    pprinc = &tree[0];
+    /* Local TGS name */
+    retval = krb5_tgtname(context, client, client, pprinc++);
+    if (retval) goto error;
+    srcrealm = *client;
+    for (i = 0; i < nlinks; i++) {
+       dstrealm.data = vals[i];
+       dstrealm.length = strcspn(vals[i], "\t ");
+       retval = krb5_tgtname(context, &dstrealm, &srcrealm, pprinc++);
+       if (retval) goto error;
+       srcrealm = dstrealm;
+    }
+    retval = krb5_tgtname(context, server, &srcrealm, pprinc++);
+    if (retval) goto error;
+    *rettree = tree;
+
+error:
+    profile_free_list(vals);
+    if (retval) {
+       while (pprinc != NULL && pprinc > &tree[0]) {
+           /* krb5_free_principal() correctly handles null input */
+           krb5_free_principal(context, *--pprinc);
+           *pprinc = NULL;
+       }
+       free(tree);
+    }
+    return retval;
+}
 
 /*
- * xxx The following function is very confusing to read and probably
- * is buggy.  It should be documented better.  Here is what I've
- * learned about it doing a quick bug fixing walk through.  The
- * function takes a client and server realm name and returns the set
- * of realms (in a field called tree) that you need to get tickets in
- * in order to get from the source realm to the destination realm.  It
- * takes a realm separater character (normally ., but presumably there
- * for all those X.500 realms) .  There are two modes it runs in: the
- * ANL krb5.conf mode and the hierarchy mode.  The ANL mode is
- * fairly obvious.  The hierarchy mode looks for common components in
- * both the client and server realms.  In general, the pointer scp and
- * ccp are used to walk through the client and server realms.  The
- * com_sdot and com_cdot pointers point to (I think) the beginning of
- * the common part of the realm names.  I.E. strcmp(com_cdot,
- * com_sdot) ==0 is roughly an invarient.  However, there are cases
- * where com_sdot and com_cdot are set to point before the start of
- * the client or server strings.  I think this only happens when there
- * are no common components.  --hartmans 2002/03/14
+ * Get realm list from "capaths" section of the profile.  Deliberately
+ * returns success but leaves VALS null if profile_get_values() fails
+ * by not finding anything.
  */
+static krb5_error_code
+rtree_capath_vals(
+    krb5_context context,
+    const krb5_data *client,
+    const krb5_data *server,
+    char ***vals)
+{
+    krb5_error_code retval = 0;
+    /* null-terminated realm names */
+    char *clientz = NULL, *serverz = NULL;
+    const char *key[4];
 
-krb5_error_code
-krb5_walk_realm_tree(krb5_context context, const krb5_data *client, const krb5_data *server, krb5_principal **tree, int realm_branch_char)
+    *vals = NULL;
+
+    clientz = calloc(client->length + 1, 1);
+    if (clientz == NULL) {
+       retval = ENOMEM;
+       goto error;
+    }
+    memcpy(clientz, client->data, client->length);
+
+    serverz = calloc(server->length + 1, 1);
+    if (clientz == NULL) {
+       retval = ENOMEM;
+       goto error;
+    }
+    memcpy(serverz, server->data, server->length);
+
+    key[0] = "capaths";
+    key[1] = clientz;
+    key[2] = serverz;
+    key[3] = NULL;
+    retval = profile_get_values(context->profile, key, vals);
+    switch (retval) {
+    case PROF_NO_SECTION:
+    case PROF_NO_RELATION:
+       /*
+        * Not found; don't return an error.
+        */
+       retval = 0;
+       break;
+    default:
+       break;
+    }
+error:
+    free(clientz);
+    free(serverz);
+    return retval;
+}
+
+/*
+ * Build tree by hierarchical traversal.
+ */
+static krb5_error_code
+rtree_hier_tree(
+    krb5_context context,
+    const krb5_data *client,
+    const krb5_data *server,
+    krb5_principal **rettree,
+    int sep)
 {
     krb5_error_code retval;
-    krb5_principal *rettree;
-    register char *ccp, *scp;
-    register char *prevccp = 0, *prevscp = 0;
-    char *com_sdot = 0, *com_cdot = 0;
-    register int i, links = 0;
-    int clen, slen = -1;
-    krb5_data tmpcrealm, tmpsrealm;
-    int nocommon = 1;
-
-    const char *cap_names[4];
-    char *cap_client, *cap_server;
-    char **cap_nodes;
-    krb5_error_code cap_code;
-
-#ifdef DEBUG_REFERRALS
-    printf("krb5_walk_realm_tree starting\n");
-    printf("  client is %s\n",client->data);
-    printf("  server is %s\n",server->data);
-#endif
-
-    if (!(client->data &&server->data))
-      return KRB5_NO_TKT_IN_RLM;
-    if ((cap_client = (char *)malloc(client->length + 1)) == NULL)
-       return ENOMEM;
-    strncpy(cap_client, client->data, client->length);
-    cap_client[client->length] = '\0';
-    if ((cap_server = (char *)malloc(server->length + 1)) == NULL) {
-       krb5_xfree(cap_client);
-       return ENOMEM;
+    krb5_data *realms;
+    const krb5_data *dstrealm, *srcrealm;
+    krb5_principal *tree, *pprinc;
+    size_t nrealms, nprincs, i;
+
+    *rettree = NULL;
+    retval = rtree_hier_realms(context, client, server,
+                              &realms, &nrealms, sep);
+    if (retval)
+       return retval;
+    nprincs = nrealms;
+    pprinc = tree = calloc(nprincs + 1, sizeof(krb5_principal));
+    if (tree == NULL) {
+       retval = ENOMEM;
+       goto error;
     }
-    strncpy(cap_server, server->data, server->length);
-    cap_server[server->length] = '\0';
-    cap_names[0] = "capaths";
-    cap_names[1] = cap_client;
-    cap_names[2] = cap_server;
-    cap_names[3] = 0;
-    cap_code = profile_get_values(context->profile, cap_names, &cap_nodes);
-    krb5_xfree(cap_client);  /* done with client string */
-    cap_names[1] = 0;
-    if (cap_code == 0) {     /* found a path, so lets use it */
-       links = 0;
-       if (*cap_nodes[0] != '.') { /* a link of . means direct */
-           while(cap_nodes[links]) {
-               links++;
-           }
-       }
-       if (cap_nodes[links] != NULL)
-           krb5_xfree(cap_nodes[links]);
-
-       cap_nodes[links] = cap_server; /* put server on end of list */
-       /* this simplifies the code later and make */
-       /* cleanup eaiser as well */
-       links++;                /* count the null entry at end */
-    } else {                   /* no path use hierarchical method */
-       krb5_xfree(cap_server); /* failed, don't need server string */
-       cap_names[2] = 0;
-
-       clen = client->length;
-       slen = server->length;
-
-       for (com_cdot = ccp = client->data + clen - 1,
-                com_sdot = scp = server->data + slen - 1;
-            clen && slen && *ccp == *scp ;
-            ccp--, scp--,      clen--, slen--) {
-           if (*ccp == realm_branch_char) {
-               com_cdot = ccp;
-               com_sdot = scp;
-               nocommon = 0;
-           }
-       }
+    for (i = 0; i < nrealms; i++)
+       tree[i] = NULL;
+    srcrealm = client;
+    for (i = 0; i < nrealms; i++) {
+       dstrealm = &realms[i];
+       retval = krb5_tgtname(context, dstrealm, srcrealm, pprinc++);
+       if (retval) goto error;
+       srcrealm = dstrealm;
+    }
+    *rettree = tree;
+    return 0;
+error:
+    while (pprinc != NULL && pprinc > tree) {
+       krb5_free_principal(context, *--pprinc);
+       *pprinc = NULL;
+    }
+    free(tree);
+    return retval;
+}
 
-       /* ccp, scp point to common root.
-          com_cdot, com_sdot point to common components. */
-       /* handle case of one ran out */
-       if (!clen) {
-           /* construct path from client to server, down the tree */
-           if (!slen)
-               /* in the same realm--this means there is no ticket
-                  in this realm. */
-               return KRB5_NO_TKT_IN_RLM;
-           if (*scp == realm_branch_char) {
-               /* one is a subdomain of the other */
-               com_cdot = client->data;
-               com_sdot = scp;
-               nocommon = 0;
-           } /* else normal case of two sharing parents */
-       }
-       if (!slen) {
-           /* construct path from client to server, up the tree */
-           if (*ccp == realm_branch_char) {
-               /* one is a subdomain of the other */
-               com_sdot = server->data;
-               com_cdot = ccp;
-               nocommon = 0;
-           } /* else normal case of two sharing parents */
-       }
-       /* determine #links to/from common ancestor */
-       if (nocommon)
-           links = 1;
-       else
-           links = 2;
-       /* if no common ancestor, artificially set up common root at the last
-          component, then join with special code */
-       for (ccp = client->data; ccp < com_cdot; ccp++) {
-           if (*ccp == realm_branch_char) {
-               links++;
-               if (nocommon)
-                   prevccp = ccp;
-           }
-       }
+/*
+ * Construct list of realms between client and server.
+ */
+static krb5_error_code
+rtree_hier_realms(
+    krb5_context context,
+    const krb5_data *client,
+    const krb5_data *server,
+    krb5_data **realms,
+    size_t *nrealms,
+    int sep)
+{
+    krb5_error_code retval;
+    struct hstate c, s;
+    krb5_data *ctweens, *stweens, *twp, *r, *rp;
+    size_t nctween, nstween;
 
-       for (scp = server->data; scp < com_sdot; scp++) {
-           if (*scp == realm_branch_char) {
-               links++;
-               if (nocommon)
-                   prevscp = scp;
-           }
-       }
-       if (nocommon) {
-           if (prevccp)
-               com_cdot = prevccp;
-           if (prevscp)
-               com_sdot = prevscp;
-
-           if(com_cdot == client->data + client->length -1)
-               com_cdot = client->data - 1 ;
-           if(com_sdot == server->data + server->length -1)
-               com_sdot = server->data - 1 ;
-       }
-    }          /* end of if use hierarchical method */
+    r = rp = NULL;
+    c.str = client->data;
+    c.len = client->length;
+    c.dot = c.tail = NULL;
+    s.str = server->data;
+    s.len = server->length;
+    s.dot = s.tail = NULL;
+
+    comtail(&c, &s, sep);
+    adjtail(&c, &s, sep);
+
+    retval = rtree_hier_tweens(context, &c, &ctweens, &nctween, 1, sep);
+    if (retval) goto error;
+    retval = rtree_hier_tweens(context, &s, &stweens, &nstween, 0, sep);
+    if (retval) goto error;
 
-    if (!(rettree = (krb5_principal *)calloc(links+2,
-                                            sizeof(krb5_principal)))) {
-       return ENOMEM;
+    *nrealms = nctween + nstween;
+    rp = r = calloc(*nrealms, sizeof(krb5_data));
+    if (r == NULL) {
+       retval = ENOMEM;
+       goto error;
     }
-    i = 1;
-    if ((retval = krb5_tgtname(context, client, client, &rettree[0]))) {
-       krb5_xfree(rettree);
-       return retval;
+    /* Copy client realm "tweens" forward. */
+    for (twp = ctweens; twp < &ctweens[nctween]; twp++) {
+       retval = krb5int_copy_data_contents(context, twp, rp++);
+       if (retval) goto error;
     }
-    links--;                           /* dont count the null entry on end */
-    if (cap_code == 0) {    /* found a path above */
-       tmpcrealm.data = client->data;
-       tmpcrealm.length = client->length;
-       while( i-1 <= links) {
-                       
-           tmpsrealm.data = cap_nodes[i-1];
-           /* don't count trailing whitespace from profile_get */
-           tmpsrealm.length = strcspn(cap_nodes[i-1],"\t ");
-           if ((retval = krb5_tgtname(context,
-                                      &tmpsrealm,
-                                      &tmpcrealm,
-                                      &rettree[i]))) {
-               while (i) {
-                   krb5_free_principal(context, rettree[i-1]);
-                   i--;
-               }
-               krb5_xfree(rettree);
-                               /* cleanup the cap_nodes from profile_get */
-               for (i = 0; i<=links; i++) {
-                   krb5_xfree(cap_nodes[i]);
-               }
-               krb5_xfree((char *)cap_nodes);
-               return retval;
-           }
-           tmpcrealm.data = tmpsrealm.data;    
-           tmpcrealm.length = tmpsrealm.length;
-           i++;
-       }
-       /* cleanup the cap_nodes from profile_get last one has server */
-       for (i = 0; i<=links; i++) {
-           krb5_xfree(cap_nodes[i]);
-       }
-       krb5_xfree((char *)cap_nodes);
-    } else {  /* if not cap then use hierarchical method */
-       for (prevccp = ccp = client->data;
-            ccp <= com_cdot;
-            ccp++) {
-           if (*ccp != realm_branch_char)
-               continue;
-           ++ccp;                              /* advance past dot */
-           tmpcrealm.data = prevccp;
-           tmpcrealm.length = client->length -
-               (prevccp - client->data);
-           tmpsrealm.data = ccp;
-           tmpsrealm.length = client->length -
-               (ccp - client->data);
-           if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
-                                      &rettree[i]))) {
-               while (i) {
-                   krb5_free_principal(context, rettree[i-1]);
-                   i--;
-               }
-               krb5_xfree(rettree);
-               return retval;
-           }
-           prevccp = ccp;
-           i++;
-       }
-       if (nocommon) {
-           tmpcrealm.data = com_cdot + 1;
-           tmpcrealm.length = client->length -
-               (com_cdot + 1 - client->data);
-           tmpsrealm.data = com_sdot + 1;
-           tmpsrealm.length = server->length -
-               (com_sdot + 1 - server->data);
-           if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
-                                      &rettree[i]))) {
-               while (i) {
-                   krb5_free_principal(context, rettree[i-1]);
-                   i--;
-               }
-               krb5_xfree(rettree);
-               return retval;
-           }
-           i++;
+    /* Copy server realm "tweens" backward. */
+    for (twp = &stweens[nstween]; twp-- > stweens;) {
+       krb5int_copy_data_contents(context, twp, rp++);
+       if (retval) goto error;
+    }
+error:
+    if (retval) {
+       *nrealms = 0;
+       while (rp > r) {
+           krb5_free_data_contents(context, --rp);
        }
+       free(r);
+       r = NULL;
+    }
+    free(ctweens);
+    free(stweens);
+    *realms = r;
+    return retval;
+}
 
-       for (prevscp = com_sdot + 1, scp = com_sdot - 1;
-            scp > server->data;
-            scp--) {
-           if (*scp != realm_branch_char)
-               continue;
-           if (scp - 1 < server->data)
-               break;                  /* XXX only if . starts realm? */
-           tmpcrealm.data = prevscp;
-           tmpcrealm.length = server->length -
-               (prevscp - server->data);
-           tmpsrealm.data = scp + 1;
-           tmpsrealm.length = server->length -
-               (scp + 1 - server->data);
-           if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
-                                      &rettree[i]))) {
-               while (i) {
-                   krb5_free_principal(context, rettree[i-1]);
-                   i--;
-               }
-               krb5_xfree(rettree);
-               return retval;
-           }
-           prevscp = scp + 1;
-           i++;
-       }
-       if (slen && com_sdot >= server->data) {
-           /* only necessary if building down tree from ancestor or client */
-           /* however, we can get here if we have only one component
-              in the server realm name, hence we make sure we found a component
-              separator there... */
-           tmpcrealm.data = prevscp;
-           tmpcrealm.length = server->length -
-               (prevscp - server->data);
-           if ((retval = krb5_tgtname(context, server, &tmpcrealm,
-                                      &rettree[i]))) {
-               while (i) {
-                   krb5_free_principal(context, rettree[i-1]);
-                   i--;
-               }
-               krb5_xfree(rettree);
-               return retval;
-           }
+/*
+ * Build a list of realms between a given realm and the common
+ * suffix.  The original realm is included, but the "tail" is only
+ * included if DOTAIL is true.
+ *
+ * Warning: This function intentionally aliases memory.  Caller must
+ * make copies as needed and not call krb5_free_data_contents, etc.
+ */
+static krb5_error_code
+rtree_hier_tweens(
+    krb5_context context,
+    struct hstate *realm,
+    krb5_data **tweens,
+    size_t *ntweens,
+    int dotail,
+    int sep)
+{
+    char *p, *r, *rtail, *lp;
+    size_t rlen, n;
+    krb5_data *tws, *ntws;
+
+    r = realm->str;
+    rlen = realm->len;
+    rtail = realm->tail;
+    *tweens = ntws = tws = NULL;
+    *ntweens = n = 0;
+
+    for (lp = p = r; p < &r[rlen]; p++) {
+       if (*p != sep && &p[1] != &r[rlen])
+           continue;
+       if (lp == rtail && !dotail)
+           break;
+       ntws = realloc(tws, (n + 1) * sizeof(krb5_data));
+       if (ntws == NULL) {
+           free(tws);
+           return ENOMEM;
        }
+       tws = ntws;
+       tws[n].data = lp;
+       tws[n].length = &r[rlen] - lp;
+       n++;
+       if (lp == rtail)
+           break;
+       lp = &p[1];
     }
-    *tree = rettree;
-
-#ifdef DEBUG_REFERRALS
-    printf("krb5_walk_realm_tree ending; tree (length %d) is:\n",links);
-    for(i=0;i<links+2;i++) {
-        if ((*tree)[i])
-           krb5int_dbgref_dump_principal("krb5_walk_realm_tree tree",(*tree)[i]);
-       else
-           printf("tree element %i null\n");
-    }
-#endif
+    *tweens = tws;
+    *ntweens = n;
     return 0;
 }
 
-#ifdef DEBUG_REFERRALS
-void krb5int_dbgref_dump_principal(char *d, krb5_principal p)
+/*
+ * Adjust suffixes that each starts at the beginning of a component,
+ * to avoid the problem where "BC.EXAMPLE.COM" is erroneously reported
+ * as a parent of "ABC.EXAMPLE.COM".
+ */
+static void
+adjtail(struct hstate *c, struct hstate *s, int sep)
 {
-    int n;
-             
-    printf("  **%s: ",d);
-    for (n=0;n<p->length;n++)
-       printf("%s<%.*s>",(n>0)?"/":"",p->data[n].length,p->data[n].data);
-    printf("@<%.*s>  (length %d, type %d)\n",p->realm.length,p->realm.data,
-          p->length, p->type);
+    int cfull, sfull;
+    char *cp, *sp;
+
+    cp = c->tail;
+    sp = s->tail;
+    if (cp == NULL || sp == NULL)
+       return;
+    /*
+     * Is it a full component?  Yes, if it's the beginning of the
+     * string or there's a separator to the left.
+     *
+     * The index of -1 is valid because it only gets evaluated if the
+     * pointer is not at the beginning of the string.
+     */
+    cfull = (cp == c->str || cp[-1] == sep);
+    sfull = (sp == s->str || sp[-1] == sep);
+    /*
+     * If they're both full components, we're done.
+     */
+    if (cfull && sfull) {
+       return;
+    } else if (c->dot != NULL && s->dot != NULL) {
+       cp = c->dot + 1;
+       sp = s->dot + 1;
+       /*
+        * Out of bounds? Can only happen if there are trailing dots.
+        */
+       if (cp >= &c->str[c->len] || sp >= &s->str[s->len]) {
+           cp = sp = NULL;
+       }
+    } else {
+       cp = sp = NULL;
+    }
+    c->tail = cp;
+    s->tail = sp;
+}
+
+/*
+ * Find common suffix of C and S.
+ *
+ * C->TAIL and S->TAIL will point to the respective suffixes.  C->DOT
+ * and S->DOT will point to the nearest instances of SEP to the right
+ * of the start of each suffix.  Caller must initialize TAIL and DOT
+ * pointers to null.
+ */
+static void
+comtail(struct hstate *c, struct hstate *s, int sep)
+{
+    char *cp, *sp, *cdot, *sdot;
+
+    if (c->len == 0 || s->len == 0)
+       return;
+
+    cdot = sdot = NULL;
+    /*
+     * ANSI/ISO C allows a pointer one past the end but not one
+     * before the beginning of an array.
+     */
+    cp = &c->str[c->len];
+    sp = &s->str[s->len];
+    /*
+     * Set CP and SP to point to the common suffix of each string.
+     * When we run into separators (dots, unless someone has a X.500
+     * style realm), keep pointers to the latest pair.
+     */
+    while (cp > c->str && sp > s->str) {
+       if (*--cp != *--sp) {
+           /*
+            * Didn't match, so most recent match is one byte to the
+            * right (or not at all).
+            */
+           cp++;
+           sp++;
+           break;
+       }
+       /*
+        * Keep track of matching dots.
+        */
+       if (*cp == sep) {
+           cdot = cp;
+           sdot = sp;
+       }
+    }
+    /* No match found at all. */
+    if (cp == &c->str[c->len])
+       return;
+    c->tail = cp;
+    s->tail = sp;
+    c->dot = cdot;
+    s->dot = sdot;
 }
-#endif
index 99561c54795820dd37a39ca41691db6471251acd..17f6eae115c37ec6dfac3da31fccbc6fdd3bf664 100644 (file)
@@ -68,4 +68,12 @@ eval $check
 set A.EXAMPLE.COM EXAMPLE.COM "A.EXAMPLE.COM@A.EXAMPLE.COM EXAMPLE.COM@A.EXAMPLE.COM"
 eval $check
 
+echo CAPATH test
+set ATHENA.MIT.EDU KERBEROS.COM "ATHENA.MIT.EDU@ATHENA.MIT.EDU KERBEROS.COM@ATHENA.MIT.EDU"
+eval $check
+
+echo CAPATH test
+set LCS.MIT.EDU KABLOOEY.KERBEROS.COM "LCS.MIT.EDU@LCS.MIT.EDU ATHENA.MIT.EDU@LCS.MIT.EDU KERBEROS.COM@ATHENA.MIT.EDU KABLOOEY.KERBEROS.COM@KERBEROS.COM"
+eval $check
+
 exit $err