xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/walk_rtree.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
2 /*
3  * lib/krb5/krb/walk_rtree.c
4  *
5  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
6  * All Rights Reserved.
7  *
8  * Export of this software from the United States of America may
9  *   require a specific license from the United States Government.
10  *   It is the responsibility of any person or organization contemplating
11  *   export to obtain such a license before exporting.
12  *
13  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14  * distribute this software and its documentation for any purpose and
15  * without fee is hereby granted, provided that the above copyright
16  * notice appear in all copies and that both that copyright notice and
17  * this permission notice appear in supporting documentation, and that
18  * the name of M.I.T. not be used in advertising or publicity pertaining
19  * to distribution of the software without specific, written prior
20  * permission.  Furthermore if you modify this software you must label
21  * your software as modified software and not distribute it in such a
22  * fashion that it might be confused with the original M.I.T. software.
23  * M.I.T. makes no representations about the suitability of
24  * this software for any purpose.  It is provided "as is" without express
25  * or implied warranty.
26  *
27  *
28  * krb5_walk_realm_tree()
29  */
30 
31 /* ANL - Modified to allow Configurable Authentication Paths.
32  * This modification removes the restriction on the choice of realm
33  * names, i.e. they nolonger have to be hierarchical. This
34  * is allowed by RFC 1510: "If a hierarchical orginization is not used
35  * it may be necessary to consult some database in order to construct
36  * an authentication path between realms."  The database is contained
37  * in the [capaths] section of the krb5.conf file.
38  * Client to server paths are defined. There are n**2 possible
39  * entries, but only those entries which are needed by the client
40  * or server need be present in its krb5.conf file. (n entries or 2*n
41  * entries if the same krb5.conf is used for clients and servers)
42  *
43  * for example: ESnet will be running a KDC which will share
44  * inter-realm keys with its many orginizations which include among
45  * other ANL, NERSC and PNL. Each of these orginizations wants to
46  * use its DNS name in the realm, ANL.GOV. In addition ANL wants
47  * to authenticatite to HAL.COM via a K5.MOON and K5.JUPITER
48  * A [capaths] section of the krb5.conf file for the ANL.GOV clients
49  * and servers would look like:
50  *
51  * [capaths]
52  * ANL.GOV = {
53  *		NERSC.GOV = ES.NET
54  *		PNL.GOV = ES.NET
55  *		ES.NET = .
56  * 		HAL.COM = K5.MOON
57  * 		HAL.COM = K5.JUPITER
58  * }
59  * NERSC.GOV = {
60  *		ANL.GOV = ES.NET
61  * }
62  * PNL.GOV = {
63  *		ANL.GOV = ES.NET
64  * }
65  * ES.NET = {
66  * 		ANL.GOV = .
67  * }
68  * HAL.COM = {
69  *		ANL.GOV = K5.JUPITER
70  *		ANL.GOV = K5.MOON
71  * }
72  *
73  * In the above a "." is used to mean directly connected since the
74  * the profile routines cannot handle a null entry.
75  *
76  * If no client-to-server path is found, the default hierarchical path
77  * is still generated.
78  *
79  * This version of the Configurable Authentication Path modification
80  * differs from the previous versions prior to K5 beta 5 in that
81  * the profile routines are used, and the explicite path from
82  * client's realm to server's realm must be given. The modifications
83  * will work together.
84  * DEE - 5/23/95
85  */
86 #define CONFIGURABLE_AUTHENTICATION_PATH
87 #include "k5-int.h"
88 #include "int-proto.h"
89 
90 /* internal function, used by krb5_get_cred_from_kdc() */
91 
92 #ifndef min
93 #define min(x,y) ((x) < (y) ? (x) : (y))
94 #define max(x,y) ((x) > (y) ? (x) : (y))
95 #endif
96 
97 /*
98  * xxx The following function is very confusing to read and probably
99  * is buggy.  It should be documented better.  Here is what I've
100  * learned about it doing a quick bug fixing walk through.  The
101  * function takes a client and server realm name and returns the set
102  * of realms (in a field called tree) that you need to get tickets in
103  * in order to get from the source realm to the destination realm.  It
104  * takes a realm separater character (normally ., but presumably there
105  * for all those X.500 realms) .  There are two modes it runs in: the
106  * ANL krb5.conf mode and the hierarchy mode.  The ANL mode is
107  * fairly obvious.  The hierarchy mode looks for common components in
108  * both the client and server realms.  In general, the pointer scp and
109  * ccp are used to walk through the client and server realms.  The
110  * com_sdot and com_cdot pointers point to (I think) the beginning of
111  * the common part of the realm names.  I.E. strcmp(com_cdot,
112  * com_sdot) ==0 is roughly an invarient.  However, there are cases
113  * where com_sdot and com_cdot are set to point before the start of
114  * the client or server strings.  I think this only happens when there
115  * are no common components.  --hartmans 2002/03/14
116  */
117 
118 krb5_error_code
119 krb5_walk_realm_tree(krb5_context context, const krb5_data *client, const krb5_data *server, krb5_principal **tree, int realm_branch_char)
120 {
121     krb5_error_code retval;
122     krb5_principal *rettree;
123     register char *ccp, *scp;
124     register char *prevccp = 0, *prevscp = 0;
125     char *com_sdot = 0, *com_cdot = 0;
126     register int i, links = 0;
127     int clen, slen = -1;
128     krb5_data tmpcrealm, tmpsrealm;
129     int nocommon = 1;
130 
131 #ifdef CONFIGURABLE_AUTHENTICATION_PATH
132     const char *cap_names[4];
133     char *cap_client, *cap_server;
134     char **cap_nodes;
135     krb5_error_code cap_code;
136 #endif
137 
138 #ifdef DEBUG_REFERRALS
139     printf("krb5_walk_realm_tree starting\n");
140     printf("  client is %s\n",client->data);
141     printf("  server is %s\n",server->data);
142 #endif
143 
144     if (!(client->data &&server->data))
145       return KRB5_NO_TKT_IN_RLM;
146 #ifdef CONFIGURABLE_AUTHENTICATION_PATH
147     if ((cap_client = (char *)malloc(client->length + 1)) == NULL)
148 	return ENOMEM;
149     strncpy(cap_client, client->data, client->length);
150     cap_client[client->length] = '\0';
151     if ((cap_server = (char *)malloc(server->length + 1)) == NULL) {
152 	krb5_xfree(cap_client);
153 	return ENOMEM;
154     }
155     strncpy(cap_server, server->data, server->length);
156     cap_server[server->length] = '\0';
157     cap_names[0] = "capaths";
158     cap_names[1] = cap_client;
159     cap_names[2] = cap_server;
160     cap_names[3] = 0;
161     cap_code = profile_get_values(context->profile, cap_names, &cap_nodes);
162     krb5_xfree(cap_client);  /* done with client string */
163     cap_names[1] = 0;
164     if (cap_code == 0) {     /* found a path, so lets use it */
165 	links = 0;
166 	if (*cap_nodes[0] != '.') { /* a link of . means direct */
167 	    while(cap_nodes[links]) {
168 		links++;
169 	    }
170 	}
171 	if (cap_nodes[links] != NULL)
172 	    krb5_xfree(cap_nodes[links]);
173 
174 	cap_nodes[links] = cap_server; /* put server on end of list */
175 	/* this simplifies the code later and make */
176 	/* cleanup eaiser as well */
177 	links++;		/* count the null entry at end */
178     } else {			/* no path use hierarchical method */
179 	krb5_xfree(cap_server); /* failed, don't need server string */
180 	cap_names[2] = 0;
181 #endif
182 	clen = client->length;
183 	slen = server->length;
184 
185 	for (com_cdot = ccp = client->data + clen - 1,
186 		 com_sdot = scp = server->data + slen - 1;
187 	     clen && slen && *ccp == *scp ;
188 	     ccp--, scp--, 	clen--, slen--) {
189 	    if (*ccp == realm_branch_char) {
190 		com_cdot = ccp;
191 		com_sdot = scp;
192 		nocommon = 0;
193 	    }
194 	}
195 
196 	/* ccp, scp point to common root.
197 	   com_cdot, com_sdot point to common components. */
198 	/* handle case of one ran out */
199 	if (!clen) {
200 	    /* construct path from client to server, down the tree */
201 	    if (!slen)
202 		/* in the same realm--this means there is no ticket
203 		   in this realm. */
204 		return KRB5_NO_TKT_IN_RLM;
205 	    if (*scp == realm_branch_char) {
206 		/* one is a subdomain of the other */
207 		com_cdot = client->data;
208 		com_sdot = scp;
209 		nocommon = 0;
210 	    } /* else normal case of two sharing parents */
211 	}
212 	if (!slen) {
213 	    /* construct path from client to server, up the tree */
214 	    if (*ccp == realm_branch_char) {
215 		/* one is a subdomain of the other */
216 		com_sdot = server->data;
217 		com_cdot = ccp;
218 		nocommon = 0;
219 	    } /* else normal case of two sharing parents */
220 	}
221 	/* determine #links to/from common ancestor */
222 	if (nocommon)
223 	    links = 1;
224 	else
225 	    links = 2;
226 	/* if no common ancestor, artificially set up common root at the last
227 	   component, then join with special code */
228 	for (ccp = client->data; ccp < com_cdot; ccp++) {
229 	    if (*ccp == realm_branch_char) {
230 		links++;
231 		if (nocommon)
232 		    prevccp = ccp;
233 	    }
234 	}
235 
236 	for (scp = server->data; scp < com_sdot; scp++) {
237 	    if (*scp == realm_branch_char) {
238 		links++;
239 		if (nocommon)
240 		    prevscp = scp;
241 	    }
242 	}
243 	if (nocommon) {
244 	    if (prevccp)
245 		com_cdot = prevccp;
246 	    if (prevscp)
247 		com_sdot = prevscp;
248 
249 	    if(com_cdot == client->data + client->length -1)
250 		com_cdot = client->data - 1 ;
251 	    if(com_sdot == server->data + server->length -1)
252 		com_sdot = server->data - 1 ;
253 	}
254 #ifdef CONFIGURABLE_AUTHENTICATION_PATH
255     }		/* end of if use hierarchical method */
256 #endif
257 
258     if (!(rettree = (krb5_principal *)calloc(links+2,
259 					     sizeof(krb5_principal)))) {
260 	return ENOMEM;
261     }
262     i = 1;
263     if ((retval = krb5_tgtname(context, client, client, &rettree[0]))) {
264 	krb5_xfree(rettree);
265 	return retval;
266     }
267 #ifdef CONFIGURABLE_AUTHENTICATION_PATH
268     links--;				/* dont count the null entry on end */
269     if (cap_code == 0) {    /* found a path above */
270 	tmpcrealm.data = client->data;
271 	tmpcrealm.length = client->length;
272 	while( i-1 <= links) {
273 
274 	    tmpsrealm.data = cap_nodes[i-1];
275 	    /* don't count trailing whitespace from profile_get */
276 	    tmpsrealm.length = strcspn(cap_nodes[i-1],"\t ");
277 	    if ((retval = krb5_tgtname(context,
278 				       &tmpsrealm,
279 				       &tmpcrealm,
280 				       &rettree[i]))) {
281 		while (i) {
282 		    krb5_free_principal(context, rettree[i-1]);
283 		    i--;
284 		}
285 		krb5_xfree(rettree);
286 				/* cleanup the cap_nodes from profile_get */
287 		for (i = 0; i<=links; i++) {
288 		    krb5_xfree(cap_nodes[i]);
289 		}
290 		krb5_xfree((char *)cap_nodes);
291 		return retval;
292 	    }
293 	    tmpcrealm.data = tmpsrealm.data;
294 	    tmpcrealm.length = tmpsrealm.length;
295 	    i++;
296 	}
297 	/* cleanup the cap_nodes from profile_get last one has server */
298 	for (i = 0; i<=links; i++) {
299 	    krb5_xfree(cap_nodes[i]);
300 	}
301 	krb5_xfree((char *)cap_nodes);
302     } else {  /* if not cap then use hierarchical method */
303 #endif
304 	for (prevccp = ccp = client->data;
305 	     ccp <= com_cdot;
306 	     ccp++) {
307 	    if (*ccp != realm_branch_char)
308 		continue;
309 	    ++ccp;				/* advance past dot */
310 	    tmpcrealm.data = prevccp;
311 	    tmpcrealm.length = client->length -
312 		(prevccp - client->data);
313 	    tmpsrealm.data = ccp;
314 	    tmpsrealm.length = client->length -
315 		(ccp - client->data);
316 	    if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
317 				       &rettree[i]))) {
318 		while (i) {
319 		    krb5_free_principal(context, rettree[i-1]);
320 		    i--;
321 		}
322 		krb5_xfree(rettree);
323 		return retval;
324 	    }
325 	    prevccp = ccp;
326 	    i++;
327 	}
328 	if (nocommon) {
329 	    tmpcrealm.data = com_cdot + 1;
330 	    tmpcrealm.length = client->length -
331 		(com_cdot + 1 - client->data);
332 	    tmpsrealm.data = com_sdot + 1;
333 	    tmpsrealm.length = server->length -
334 		(com_sdot + 1 - server->data);
335 	    if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
336 				       &rettree[i]))) {
337 		while (i) {
338 		    krb5_free_principal(context, rettree[i-1]);
339 		    i--;
340 		}
341 		krb5_xfree(rettree);
342 		return retval;
343 	    }
344 	    i++;
345 	}
346 
347 	for (prevscp = com_sdot + 1, scp = com_sdot - 1;
348 	     scp > server->data;
349 	     scp--) {
350 	    if (*scp != realm_branch_char)
351 		continue;
352 	    if (scp - 1 < server->data)
353 		break;			/* XXX only if . starts realm? */
354 	    tmpcrealm.data = prevscp;
355 	    tmpcrealm.length = server->length -
356 		(prevscp - server->data);
357 	    tmpsrealm.data = scp + 1;
358 	    tmpsrealm.length = server->length -
359 		(scp + 1 - server->data);
360 	    if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
361 				       &rettree[i]))) {
362 		while (i) {
363 		    krb5_free_principal(context, rettree[i-1]);
364 		    i--;
365 		}
366 		krb5_xfree(rettree);
367 		return retval;
368 	    }
369 	    prevscp = scp + 1;
370 	    i++;
371 	}
372 	if (slen && com_sdot >= server->data) {
373 	    /* only necessary if building down tree from ancestor or client */
374 	    /* however, we can get here if we have only one component
375 	       in the server realm name, hence we make sure we found a component
376 	       separator there... */
377 	    tmpcrealm.data = prevscp;
378 	    tmpcrealm.length = server->length -
379 		(prevscp - server->data);
380 	    if ((retval = krb5_tgtname(context, server, &tmpcrealm,
381 				       &rettree[i]))) {
382 		while (i) {
383 		    krb5_free_principal(context, rettree[i-1]);
384 		    i--;
385 		}
386 		krb5_xfree(rettree);
387 		return retval;
388 	    }
389 	}
390 #ifdef CONFIGURABLE_AUTHENTICATION_PATH
391     }
392 #endif
393     *tree = rettree;
394 
395 #ifdef DEBUG_REFERRALS
396     printf("krb5_walk_realm_tree ending; tree (length %d) is:\n",links);
397     for(i=0;i<links+2;i++) {
398         if ((*tree)[i])
399 	    krb5int_dbgref_dump_principal("krb5_walk_realm_tree tree",(*tree)[i]);
400 	else
401 	    printf("tree element %i null\n");
402     }
403 #endif
404     return 0;
405 }
406 
407 #ifdef DEBUG_REFERRALS
408 void krb5int_dbgref_dump_principal(char *d, krb5_principal p)
409 {
410     int n;
411 
412     printf("  **%s: ",d);
413     for (n=0;n<p->length;n++)
414 	printf("%s<%.*s>",(n>0)?"/":"",p->data[n].length,p->data[n].data);
415     printf("@<%.*s>  (length %d, type %d)\n",p->realm.length,p->realm.data,
416 	   p->length, p->type);
417 }
418 #endif
419