svn rev #21659: trunk/src/lib/krb5/krb/

tlyu@MIT.EDU tlyu at MIT.EDU
Thu Jan 1 20:40:44 EST 2009


http://src.mit.edu/fisheye/changelog/krb5/?cs=21659
Commit By: tlyu
Log Message:
ticket: 5947

Rewrite walk_rtree.c to handle hierarchical traversal better and to be
less convoluted.  Update test cases.


Changed Files:
U   trunk/src/lib/krb5/krb/Makefile.in
U   trunk/src/lib/krb5/krb/walk_rtree.c
U   trunk/src/lib/krb5/krb/walktree-tests
Modified: trunk/src/lib/krb5/krb/Makefile.in
===================================================================
--- trunk/src/lib/krb5/krb/Makefile.in	2009-01-01 22:34:05 UTC (rev 21658)
+++ trunk/src/lib/krb5/krb/Makefile.in	2009-01-02 01:40:41 UTC (rev 21659)
@@ -294,7 +294,7 @@
 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 @@
 		$(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) \

Modified: trunk/src/lib/krb5/krb/walk_rtree.c
===================================================================
--- trunk/src/lib/krb5/krb/walk_rtree.c	2009-01-01 22:34:05 UTC (rev 21658)
+++ trunk/src/lib/krb5/krb/walk_rtree.c	2009-01-02 01:40:41 UTC (rev 21659)
@@ -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
@@ -22,11 +22,104 @@
  * 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
@@ -52,8 +145,8 @@
  *		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
@@ -62,7 +155,7 @@
  *		ANL.GOV = ES.NET
  * }
  * ES.NET = {
- * 		ANL.GOV = .
+ *		ANL.GOV = .
  * }
  * HAL.COM = {
  *		ANL.GOV = K5.JUPITER
@@ -82,326 +175,384 @@
  * 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.
  */
-
-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)
+static krb5_error_code
+rtree_capath_vals(
+    krb5_context context,
+    const krb5_data *client,
+    const krb5_data *server,
+    char ***vals)
 {
-    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;
+    krb5_error_code retval = 0;
+    /* null-terminated realm names */
+    char *clientz = NULL, *serverz = NULL;
+    const char *key[4];
 
-    const char *cap_names[4];
-    char *cap_client, *cap_server;
-    char **cap_nodes;
-    krb5_error_code cap_code;
+    *vals = NULL;
 
-#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
+    clientz = calloc(client->length + 1, 1);
+    if (clientz == NULL) {
+	retval = ENOMEM;
+	goto error;
+    }
+    memcpy(clientz, client->data, client->length);
 
-    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;
+    serverz = calloc(server->length + 1, 1);
+    if (clientz == 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]);
+    memcpy(serverz, server->data, server->length);
 
-	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;
+    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;
+}
 
-	clen = client->length;
-	slen = server->length;
+/*
+ * 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_data *realms;
+    const krb5_data *dstrealm, *srcrealm;
+    krb5_principal *tree, *pprinc;
+    size_t nrealms, nprincs, i;
 
-	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;
-	    }
-	}
+    *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;
+    }
+    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;
+    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;
 
-	    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 */
+    comtail(&c, &s, sep);
+    adjtail(&c, &s, sep);
 
-    if (!(rettree = (krb5_principal *)calloc(links+2,
-					     sizeof(krb5_principal)))) {
-	return ENOMEM;
+    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;
+
+    *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++;
+    /* 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);
 	}
-	/* 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++;
-	}
+	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++;
+/*
+ * 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;
 	}
-	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;
-	    }
-	}
+	tws = ntws;
+	tws[n].data = lp;
+	tws[n].length = &r[rlen] - lp;
+	n++;
+	if (lp == rtail)
+	    break;
+	lp = &p[1];
     }
-    *tree = rettree;
+    *tweens = tws;
+    *ntweens = n;
+    return 0;
+}
 
-#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");
+/*
+ * 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 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;
     }
-#endif
-    return 0;
+    c->tail = cp;
+    s->tail = sp;
 }
 
-#ifdef DEBUG_REFERRALS
-void krb5int_dbgref_dump_principal(char *d, krb5_principal p)
+/*
+ * 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)
 {
-    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);
+    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

Modified: trunk/src/lib/krb5/krb/walktree-tests
===================================================================
--- trunk/src/lib/krb5/krb/walktree-tests	2009-01-01 22:34:05 UTC (rev 21658)
+++ trunk/src/lib/krb5/krb/walktree-tests	2009-01-02 01:40:41 UTC (rev 21659)
@@ -68,4 +68,12 @@
 set A.EXAMPLE.COM EXAMPLE.COM "A.EXAMPLE.COM at A.EXAMPLE.COM EXAMPLE.COM at A.EXAMPLE.COM"
 eval $check
 
+echo CAPATH test
+set ATHENA.MIT.EDU KERBEROS.COM "ATHENA.MIT.EDU at ATHENA.MIT.EDU KERBEROS.COM at ATHENA.MIT.EDU"
+eval $check
+
+echo CAPATH test
+set LCS.MIT.EDU KABLOOEY.KERBEROS.COM "LCS.MIT.EDU at LCS.MIT.EDU ATHENA.MIT.EDU at LCS.MIT.EDU KERBEROS.COM at ATHENA.MIT.EDU KABLOOEY.KERBEROS.COM at KERBEROS.COM"
+eval $check
+
 exit $err




More information about the cvs-krb5 mailing list