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