1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/walk_rtree.c */
4 * Copyright 1990,1991,2008,2009 by the Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
28 * krb5_walk_realm_tree()
29 * krb5_free_realm_tree()
31 * internal function, used by krb5_get_cred_from_kdc()
35 #include "int-proto.h"
38 * Structure to help with finding the common suffix between client and
39 * server realm during hierarchical traversal.
48 static krb5_error_code
49 rtree_capath_tree(krb5_context context,
50 const krb5_data *client,
51 const krb5_data *server,
53 krb5_principal **tree);
55 static krb5_error_code
56 rtree_capath_vals(krb5_context context,
57 const krb5_data *client,
58 const krb5_data *server,
61 static krb5_error_code
62 rtree_hier_tree(krb5_context context,
63 const krb5_data *client,
64 const krb5_data *server,
65 krb5_principal **rettree,
68 static krb5_error_code
69 rtree_hier_realms(krb5_context context,
70 const krb5_data *client,
71 const krb5_data *server,
77 free_realmlist(krb5_context context,
81 static krb5_error_code
82 rtree_hier_tweens(krb5_context context,
90 adjtail(struct hstate *c, struct hstate *s, int sep);
93 comtail(struct hstate *c, struct hstate *s, int sep);
96 krb5_walk_realm_tree( krb5_context context,
97 const krb5_data *client,
98 const krb5_data *server,
99 krb5_principal **tree,
102 krb5_error_code retval = 0;
105 if (client->data == NULL || server->data == NULL)
106 return KRB5_NO_TKT_IN_RLM;
108 if (client->length == server->length &&
109 memcmp(client->data, server->data, server->length) == 0) {
110 return KRB5_NO_TKT_IN_RLM;
112 retval = rtree_capath_vals(context, client, server, &capvals);
116 if (capvals != NULL) {
117 retval = rtree_capath_tree(context, client, server, capvals, tree);
121 retval = rtree_hier_tree(context, client, server, tree, realm_sep);
126 k5_client_realm_path(krb5_context context, const krb5_data *client,
127 const krb5_data *server, krb5_data **rpath_out)
129 krb5_error_code retval;
132 krb5_data *rpath = NULL, d;
134 retval = rtree_capath_vals(context, client, server, &capvals);
138 /* Count capaths (if any) and allocate space. Leave room for the client
139 * realm, server realm, and terminator. */
140 for (i = 0; capvals != NULL && capvals[i] != NULL; i++);
141 rpath = calloc(i + 3, sizeof(*rpath));
145 /* Populate rpath with the client realm, capaths, and server realm. */
146 retval = krb5int_copy_data_contents(context, client, &rpath[0]);
149 for (i = 0; capvals != NULL && capvals[i] != NULL; i++) {
150 d = make_data(capvals[i], strcspn(capvals[i], "\t "));
151 retval = krb5int_copy_data_contents(context, &d, &rpath[i + 1]);
155 retval = krb5int_copy_data_contents(context, server, &rpath[i + 1]);
159 /* Terminate rpath and return it. */
160 rpath[i + 2] = empty_data();
165 krb5int_free_data_list(context, rpath);
169 /* ANL - Modified to allow Configurable Authentication Paths.
170 * This modification removes the restriction on the choice of realm
171 * names, i.e. they nolonger have to be hierarchical. This
172 * is allowed by RFC 1510: "If a hierarchical orginization is not used
173 * it may be necessary to consult some database in order to construct
174 * an authentication path between realms." The database is contained
175 * in the [capaths] section of the krb5.conf file.
176 * Client to server paths are defined. There are n**2 possible
177 * entries, but only those entries which are needed by the client
178 * or server need be present in its krb5.conf file. (n entries or 2*n
179 * entries if the same krb5.conf is used for clients and servers)
181 * for example: ESnet will be running a KDC which will share
182 * inter-realm keys with its many orginizations which include among
183 * other ANL, NERSC and PNL. Each of these orginizations wants to
184 * use its DNS name in the realm, ANL.GOV. In addition ANL wants
185 * to authenticatite to HAL.COM via a K5.MOON and K5.JUPITER
186 * A [capaths] section of the krb5.conf file for the ANL.GOV clients
187 * and servers would look like:
195 * HAL.COM = K5.JUPITER
207 * ANL.GOV = K5.JUPITER
211 * In the above a "." is used to mean directly connected since the
212 * the profile routines cannot handle a null entry.
214 * If no client-to-server path is found, the default hierarchical path
215 * is still generated.
217 * This version of the Configurable Authentication Path modification
218 * differs from the previous versions prior to K5 beta 5 in that
219 * the profile routines are used, and the explicite path from
220 * client's realm to server's realm must be given. The modifications
221 * will work together.
226 * Build a tree given a set of profile values retrieved by
227 * walk_rtree_capath_vals().
229 static krb5_error_code
230 rtree_capath_tree(krb5_context context,
231 const krb5_data *client,
232 const krb5_data *server,
234 krb5_principal **rettree)
236 krb5_error_code retval = 0;
237 unsigned int nvals, nlinks, nprincs, i;
238 krb5_data srcrealm, dstrealm;
239 krb5_principal *tree, *pprinc;
242 tree = pprinc = NULL;
243 for (nvals = 0; vals[nvals] != NULL; nvals++)
245 if (vals[0] != NULL && *vals[0] == '.') {
250 nprincs = nlinks + 2;
251 tree = calloc(nprincs + 1, sizeof(krb5_principal));
256 for (i = 0; i < nprincs + 1; i++)
258 /* Invariant: PPRINC points one past end of list. */
261 retval = krb5int_tgtname(context, client, client, pprinc++);
262 if (retval) goto error;
264 for (i = 0; i < nlinks; i++) {
265 dstrealm.data = vals[i];
266 dstrealm.length = strcspn(vals[i], "\t ");
267 retval = krb5int_tgtname(context, &dstrealm, &srcrealm, pprinc++);
268 if (retval) goto error;
271 retval = krb5int_tgtname(context, server, &srcrealm, pprinc++);
272 if (retval) goto error;
276 profile_free_list(vals);
278 while (pprinc != NULL && pprinc > &tree[0]) {
279 /* krb5_free_principal() correctly handles null input */
280 krb5_free_principal(context, *--pprinc);
289 * Get realm list from "capaths" section of the profile. Deliberately
290 * returns success but leaves VALS null if profile_get_values() fails
291 * by not finding anything.
293 static krb5_error_code
294 rtree_capath_vals(krb5_context context,
295 const krb5_data *client,
296 const krb5_data *server,
299 krb5_error_code retval = 0;
300 /* null-terminated realm names */
301 char *clientz = NULL, *serverz = NULL;
306 clientz = calloc(client->length + 1, 1);
307 if (clientz == NULL) {
311 memcpy(clientz, client->data, client->length);
313 serverz = calloc(server->length + 1, 1);
314 if (serverz == NULL) {
318 memcpy(serverz, server->data, server->length);
324 retval = profile_get_values(context->profile, key, vals);
326 case PROF_NO_SECTION:
327 case PROF_NO_RELATION:
329 * Not found; don't return an error.
343 * Build tree by hierarchical traversal.
345 static krb5_error_code
346 rtree_hier_tree(krb5_context context,
347 const krb5_data *client,
348 const krb5_data *server,
349 krb5_principal **rettree,
352 krb5_error_code retval;
354 const krb5_data *dstrealm, *srcrealm;
355 krb5_principal *tree, *pprinc;
356 size_t nrealms, nprincs, i;
359 retval = rtree_hier_realms(context, client, server,
360 &realms, &nrealms, sep);
364 pprinc = tree = calloc(nprincs + 1, sizeof(krb5_principal));
369 for (i = 0; i < nrealms; i++)
372 for (i = 0; i < nrealms; i++) {
373 dstrealm = &realms[i];
374 retval = krb5int_tgtname(context, dstrealm, srcrealm, pprinc++);
375 if (retval) goto error;
379 free_realmlist(context, realms, nrealms);
382 while (pprinc != NULL && pprinc > tree) {
383 krb5_free_principal(context, *--pprinc);
386 free_realmlist(context, realms, nrealms);
392 * Construct list of realms between client and server.
394 static krb5_error_code
395 rtree_hier_realms(krb5_context context,
396 const krb5_data *client,
397 const krb5_data *server,
402 krb5_error_code retval;
404 krb5_data *ctweens = NULL, *stweens = NULL, *twp, *r, *rp;
405 size_t nctween, nstween;
411 c.str = client->data;
412 c.len = client->length;
413 c.dot = c.tail = NULL;
414 s.str = server->data;
415 s.len = server->length;
416 s.dot = s.tail = NULL;
418 comtail(&c, &s, sep);
419 adjtail(&c, &s, sep);
421 retval = rtree_hier_tweens(context, &c, &ctweens, &nctween, 1, sep);
422 if (retval) goto error;
423 retval = rtree_hier_tweens(context, &s, &stweens, &nstween, 0, sep);
424 if (retval) goto error;
426 rp = r = calloc(nctween + nstween, sizeof(krb5_data));
431 /* Copy client realm "tweens" forward. */
432 for (twp = ctweens; twp < &ctweens[nctween]; twp++) {
433 retval = krb5int_copy_data_contents(context, twp, rp);
434 if (retval) goto error;
437 /* Copy server realm "tweens" backward. */
438 for (twp = &stweens[nstween]; twp-- > stweens;) {
439 retval = krb5int_copy_data_contents(context, twp, rp);
440 if (retval) goto error;
447 free_realmlist(context, r, rp - r);
456 free_realmlist(krb5_context context,
462 for (i = 0; i < nrealms; i++)
463 krb5_free_data_contents(context, &realms[i]);
468 * Build a list of realms between a given realm and the common
469 * suffix. The original realm is included, but the "tail" is only
470 * included if DOTAIL is true.
472 * Warning: This function intentionally aliases memory. Caller must
473 * make copies as needed and not call krb5_free_data_contents, etc.
475 static krb5_error_code
476 rtree_hier_tweens(krb5_context context,
477 struct hstate *realm,
483 char *p, *r, *rtail, *lp;
485 krb5_data *tws, *ntws;
490 *tweens = ntws = tws = NULL;
493 for (lp = p = r; p < &r[rlen]; p++) {
494 if (*p != sep && &p[1] != &r[rlen])
496 if (lp == rtail && !dotail)
498 ntws = realloc(tws, (n + 1) * sizeof(krb5_data));
505 tws[n].length = &r[rlen] - lp;
517 * Adjust suffixes that each starts at the beginning of a component,
518 * to avoid the problem where "BC.EXAMPLE.COM" is erroneously reported
519 * as a parent of "ABC.EXAMPLE.COM".
522 adjtail(struct hstate *c, struct hstate *s, int sep)
529 if (cp == NULL || sp == NULL)
532 * Is it a full component? Yes, if it's the beginning of the
533 * string or there's a separator to the left.
535 * The index of -1 is valid because it only gets evaluated if the
536 * pointer is not at the beginning of the string.
538 cfull = (cp == c->str || cp[-1] == sep);
539 sfull = (sp == s->str || sp[-1] == sep);
541 * If they're both full components, we're done.
543 if (cfull && sfull) {
545 } else if (c->dot != NULL && s->dot != NULL) {
549 * Out of bounds? Can only happen if there are trailing dots.
551 if (cp >= &c->str[c->len] || sp >= &s->str[s->len]) {
562 * Find common suffix of C and S.
564 * C->TAIL and S->TAIL will point to the respective suffixes. C->DOT
565 * and S->DOT will point to the nearest instances of SEP to the right
566 * of the start of each suffix. Caller must initialize TAIL and DOT
570 comtail(struct hstate *c, struct hstate *s, int sep)
572 char *cp, *sp, *cdot, *sdot;
574 if (c->len == 0 || s->len == 0)
579 * ANSI/ISO C allows a pointer one past the end but not one
580 * before the beginning of an array.
582 cp = &c->str[c->len];
583 sp = &s->str[s->len];
585 * Set CP and SP to point to the common suffix of each string.
586 * When we run into separators (dots, unless someone has a X.500
587 * style realm), keep pointers to the latest pair.
589 while (cp > c->str && sp > s->str) {
590 if (*--cp != *--sp) {
592 * Didn't match, so most recent match is one byte to the
593 * right (or not at all).
600 * Keep track of matching dots.
607 /* No match found at all. */
608 if (cp == &c->str[c->len])
617 krb5_free_realm_tree(krb5_context context, krb5_principal *realms)
619 register krb5_principal *nrealms = realms;
623 krb5_free_principal(context, *nrealms);