xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/walk_rtree.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 [capath] 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 [capath] section of the krb5.conf file for the ANL.GOV clients
49  * and servers would look like:
50  *
51  * [capath]
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.confmode 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(context, client, server, tree, realm_branch_char)
120     krb5_context context;
121     const krb5_data *client, *server;
122     krb5_principal **tree;
123     int realm_branch_char;
124 {
125     krb5_error_code retval;
126     krb5_principal *rettree;
127     register char *ccp, *scp;
128     register char *prevccp = 0, *prevscp = 0;
129     char *com_sdot = 0, *com_cdot = 0;
130     register int i, links = 0;
131     int clen, slen;
132     krb5_data tmpcrealm, tmpsrealm;
133     int nocommon = 1;
134 
135 #ifdef CONFIGURABLE_AUTHENTICATION_PATH
136 	const char *cap_names[4];
137 	char *cap_client, *cap_server;
138 	char **cap_nodes;
139         krb5_error_code cap_code;
140 #endif
141 	if (!(client->data &&server->data))
142 	  return KRB5_NO_TKT_IN_RLM;
143 #ifdef CONFIGURABLE_AUTHENTICATION_PATH
144 	if ((cap_client = (char *)malloc(client->length + 1)) == NULL)
145 		return ENOMEM;
146 	strncpy(cap_client, client->data, client->length);
147 	cap_client[client->length] = '\0';
148 	if ((cap_server = (char *)malloc(server->length + 1)) == NULL) {
149 		krb5_xfree(cap_client);
150 		return ENOMEM;
151 	}
152 	strncpy(cap_server, server->data, server->length);
153 	cap_server[server->length] = '\0';
154 	cap_names[0] = "capaths";
155 	cap_names[1] = cap_client;
156 	cap_names[2] = cap_server;
157 	cap_names[3] = 0;
158 	cap_code = profile_get_values(context->profile, cap_names, &cap_nodes);
159 	krb5_xfree(cap_names[1]);    /* done with client string */
160 	if (cap_code == 0) {     /* found a path, so lets use it */
161 		links = 0;
162 		if (*cap_nodes[0] != '.') { /* a link of . means direct */
163 		 	while(cap_nodes[links]) {
164 				links++;
165 			}
166 		}
167 		cap_nodes[links] = cap_server; /* put server on end of list */
168 						/* this simplifies the code later and make */
169 						/* cleanup eaiser as well */
170 		links++;		/* count the null entry at end */
171 	} else {			/* no path use hierarchical method */
172 	krb5_xfree(cap_names[2]); /* failed, don't need server string */
173 #endif
174     clen = client->length;
175     slen = server->length;
176 
177     for (com_cdot = ccp = client->data + clen - 1,
178 	 com_sdot = scp = server->data + slen - 1;
179 	 clen && slen && *ccp == *scp ;
180 	 ccp--, scp--, 	clen--, slen--) {
181 	if (*ccp == realm_branch_char) {
182 	    com_cdot = ccp;
183 	    com_sdot = scp;
184 	    nocommon = 0;
185 	}
186     }
187 
188     /* ccp, scp point to common root.
189        com_cdot, com_sdot point to common components. */
190     /* handle case of one ran out */
191     if (!clen) {
192 	/* construct path from client to server, down the tree */
193 	if (!slen)
194 	    /* in the same realm--this means there is no ticket
195 	       in this realm. */
196 	    return KRB5_NO_TKT_IN_RLM;
197 	if (*scp == realm_branch_char) {
198 	    /* one is a subdomain of the other */
199 	    com_cdot = client->data;
200 	    com_sdot = scp;
201 	    nocommon = 0;
202 	} /* else normal case of two sharing parents */
203     }
204     if (!slen) {
205 	/* construct path from client to server, up the tree */
206 	if (*ccp == realm_branch_char) {
207 	    /* one is a subdomain of the other */
208 	    com_sdot = server->data;
209 	    com_cdot = ccp;
210 	    nocommon = 0;
211 	} /* else normal case of two sharing parents */
212     }
213     /* determine #links to/from common ancestor */
214     if (nocommon)
215 	links = 1;
216     else
217 	links = 2;
218     /* if no common ancestor, artificially set up common root at the last
219        component, then join with special code */
220     for (ccp = client->data; ccp < com_cdot; ccp++) {
221 	if (*ccp == realm_branch_char) {
222 	    links++;
223 	    if (nocommon)
224 		prevccp = ccp;
225 	}
226     }
227 
228     for (scp = server->data; scp < com_sdot; scp++) {
229 	if (*scp == realm_branch_char) {
230 	    links++;
231 	    if (nocommon)
232 		prevscp = scp;
233 	}
234     }
235     if (nocommon) {
236 	if (prevccp)
237 	    com_cdot = prevccp;
238 	if (prevscp)
239 	    com_sdot = prevscp;
240 
241 	if(com_cdot == client->data + client->length -1)
242 	   com_cdot = client->data - 1 ;
243 	if(com_sdot == server->data + server->length -1)
244 	   com_sdot = server->data - 1 ;
245     }
246 #ifdef CONFIGURABLE_AUTHENTICATION_PATH
247 	}		/* end of if use hierarchical method */
248 #endif
249 
250     if (!(rettree = (krb5_principal *)calloc(links+2,
251 					     sizeof(krb5_principal)))) {
252 	return ENOMEM;
253     }
254     i = 1;
255     if ((retval = krb5_tgtname(context, client, client, &rettree[0]))) {
256 	krb5_xfree(rettree);
257 	return retval;
258     }
259 #ifdef CONFIGURABLE_AUTHENTICATION_PATH
260 	links--;				/* dont count the null entry on end */
261 	if (cap_code == 0) {    /* found a path above */
262 		tmpcrealm.data = client->data;
263 		tmpcrealm.length = client->length;
264 		while( i-1 <= links) {
265 
266 			tmpsrealm.data = cap_nodes[i-1];
267 			/* don't count trailing whitespace from profile_get */
268 			tmpsrealm.length = strcspn(cap_nodes[i-1],"\t ");
269 			if ((retval = krb5_tgtname(context,
270 						   &tmpsrealm,
271 						   &tmpcrealm,
272 						   &rettree[i]))) {
273 				while (i) {
274 					krb5_free_principal(context, rettree[i-1]);
275 					i--;
276 	    		}
277 	    		krb5_xfree(rettree);
278 				/* cleanup the cap_nodes from profile_get */
279 				for (i = 0; i<=links; i++) {
280 					krb5_xfree(cap_nodes[i]);
281 				}
282 				krb5_xfree((char *)cap_nodes);
283 	    		return retval;
284 			}
285 			tmpcrealm.data = tmpsrealm.data;
286 			tmpcrealm.length = tmpsrealm.length;
287 			i++;
288 		}
289 		/* cleanup the cap_nodes from profile_get last one has server */
290 		for (i = 0; i<=links; i++) {
291 			krb5_xfree(cap_nodes[i]);
292 		}
293 		krb5_xfree((char *)cap_nodes);
294 	} else {  /* if not cap then use hierarchical method */
295 #endif
296     for (prevccp = ccp = client->data;
297 	 ccp <= com_cdot;
298 	 ccp++) {
299 	if (*ccp != realm_branch_char)
300 	    continue;
301 	++ccp;				/* advance past dot */
302 	tmpcrealm.data = prevccp;
303 	tmpcrealm.length = client->length -
304 	    (prevccp - client->data);
305 	tmpsrealm.data = ccp;
306 	tmpsrealm.length = client->length -
307 	    (ccp - client->data);
308 	if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
309 				   &rettree[i]))) {
310 	    while (i) {
311 		krb5_free_principal(context, rettree[i-1]);
312 		i--;
313 	    }
314 	    krb5_xfree(rettree);
315 	    return retval;
316 	}
317 	prevccp = ccp;
318 	i++;
319     }
320     if (nocommon) {
321 	tmpcrealm.data = com_cdot + 1;
322 	tmpcrealm.length = client->length -
323 	    (com_cdot + 1 - client->data);
324 	tmpsrealm.data = com_sdot + 1;
325 	tmpsrealm.length = server->length -
326 	    (com_sdot + 1 - server->data);
327 	if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
328 				   &rettree[i]))) {
329 	    while (i) {
330 		krb5_free_principal(context, rettree[i-1]);
331 		i--;
332 	    }
333 	    krb5_xfree(rettree);
334 	    return retval;
335 	}
336 	i++;
337     }
338 
339     for (prevscp = com_sdot + 1, scp = com_sdot - 1;
340 	 scp > server->data;
341 	 scp--) {
342 	if (*scp != realm_branch_char)
343 	    continue;
344 	if (scp - 1 < server->data)
345 	    break;			/* XXX only if . starts realm? */
346 	tmpcrealm.data = prevscp;
347 	tmpcrealm.length = server->length -
348 	    (prevscp - server->data);
349 	tmpsrealm.data = scp + 1;
350 	tmpsrealm.length = server->length -
351 	    (scp + 1 - server->data);
352 	if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm,
353 				   &rettree[i]))) {
354 	    while (i) {
355 		krb5_free_principal(context, rettree[i-1]);
356 		i--;
357 	    }
358 	    krb5_xfree(rettree);
359 	    return retval;
360 	}
361 	prevscp = scp + 1;
362 	i++;
363     }
364     if (slen && com_sdot >= server->data) {
365 	/* only necessary if building down tree from ancestor or client */
366 	/* however, we can get here if we have only one component
367 	   in the server realm name, hence we make sure we found a component
368 	   separator there... */
369 	tmpcrealm.data = prevscp;
370 	tmpcrealm.length = server->length -
371 	    (prevscp - server->data);
372 	if ((retval = krb5_tgtname(context, server, &tmpcrealm,
373 				   &rettree[i]))) {
374 	    while (i) {
375 		krb5_free_principal(context, rettree[i-1]);
376 		i--;
377 	    }
378 	    krb5_xfree(rettree);
379 	    return retval;
380 	}
381     }
382 #ifdef CONFIGURABLE_AUTHENTICATION_PATH
383 	}
384 #endif
385     *tree = rettree;
386     return 0;
387 }
388