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 (data_eq(*client, *server))
109 return KRB5_NO_TKT_IN_RLM;
110 retval = rtree_capath_vals(context, client, server, &capvals);
114 if (capvals != NULL) {
115 retval = rtree_capath_tree(context, client, server, capvals, tree);
119 retval = rtree_hier_tree(context, client, server, tree, realm_sep);
124 k5_client_realm_path(krb5_context context, const krb5_data *client,
125 const krb5_data *server, krb5_data **rpath_out)
127 krb5_error_code retval;
128 char **capvals = NULL;
130 krb5_data *rpath = NULL, d;
132 retval = rtree_capath_vals(context, client, server, &capvals);
136 /* Count capaths (if any) and allocate space. Leave room for the client
137 * realm, server realm, and terminator. */
138 for (i = 0; capvals != NULL && capvals[i] != NULL; i++);
139 rpath = calloc(i + 3, sizeof(*rpath));
143 /* Populate rpath with the client realm, capaths, and server realm. */
144 retval = krb5int_copy_data_contents(context, client, &rpath[0]);
147 for (i = 0; capvals != NULL && capvals[i] != NULL; i++) {
148 d = make_data(capvals[i], strcspn(capvals[i], "\t "));
149 retval = krb5int_copy_data_contents(context, &d, &rpath[i + 1]);
153 retval = krb5int_copy_data_contents(context, server, &rpath[i + 1]);
157 /* Terminate rpath and return it. */
158 rpath[i + 2] = empty_data();
163 profile_free_list(capvals);
164 krb5int_free_data_list(context, rpath);
168 /* ANL - Modified to allow Configurable Authentication Paths.
169 * This modification removes the restriction on the choice of realm
170 * names, i.e. they nolonger have to be hierarchical. This
171 * is allowed by RFC 1510: "If a hierarchical orginization is not used
172 * it may be necessary to consult some database in order to construct
173 * an authentication path between realms." The database is contained
174 * in the [capaths] section of the krb5.conf file.
175 * Client to server paths are defined. There are n**2 possible
176 * entries, but only those entries which are needed by the client
177 * or server need be present in its krb5.conf file. (n entries or 2*n
178 * entries if the same krb5.conf is used for clients and servers)
180 * for example: ESnet will be running a KDC which will share
181 * inter-realm keys with its many orginizations which include among
182 * other ANL, NERSC and PNL. Each of these orginizations wants to
183 * use its DNS name in the realm, ANL.GOV. In addition ANL wants
184 * to authenticatite to HAL.COM via a K5.MOON and K5.JUPITER
185 * A [capaths] section of the krb5.conf file for the ANL.GOV clients
186 * and servers would look like:
194 * HAL.COM = K5.JUPITER
206 * ANL.GOV = K5.JUPITER
210 * In the above a "." is used to mean directly connected since the
211 * the profile routines cannot handle a null entry.
213 * If no client-to-server path is found, the default hierarchical path
214 * is still generated.
216 * This version of the Configurable Authentication Path modification
217 * differs from the previous versions prior to K5 beta 5 in that
218 * the profile routines are used, and the explicite path from
219 * client's realm to server's realm must be given. The modifications
220 * will work together.
225 * Build a tree given a set of profile values retrieved by
226 * walk_rtree_capath_vals().
228 static krb5_error_code
229 rtree_capath_tree(krb5_context context,
230 const krb5_data *client,
231 const krb5_data *server,
233 krb5_principal **rettree)
235 krb5_error_code retval = 0;
236 unsigned int nvals, nlinks, nprincs, i;
237 krb5_data srcrealm, dstrealm;
238 krb5_principal *tree, *pprinc;
241 tree = pprinc = NULL;
242 for (nvals = 0; vals[nvals] != NULL; nvals++)
244 if (vals[0] != NULL && *vals[0] == '.') {
249 nprincs = nlinks + 2;
250 tree = calloc(nprincs + 1, sizeof(krb5_principal));
255 for (i = 0; i < nprincs + 1; i++)
257 /* Invariant: PPRINC points one past end of list. */
260 retval = krb5int_tgtname(context, client, client, pprinc++);
261 if (retval) goto error;
263 for (i = 0; i < nlinks; i++) {
264 dstrealm.data = vals[i];
265 dstrealm.length = strcspn(vals[i], "\t ");
266 retval = krb5int_tgtname(context, &dstrealm, &srcrealm, pprinc++);
267 if (retval) goto error;
270 retval = krb5int_tgtname(context, server, &srcrealm, pprinc++);
271 if (retval) goto error;
275 profile_free_list(vals);
277 while (pprinc != NULL && pprinc > &tree[0]) {
278 /* krb5_free_principal() correctly handles null input */
279 krb5_free_principal(context, *--pprinc);
288 * Get realm list from "capaths" section of the profile. Deliberately
289 * returns success but leaves VALS null if profile_get_values() fails
290 * by not finding anything.
292 static krb5_error_code
293 rtree_capath_vals(krb5_context context,
294 const krb5_data *client,
295 const krb5_data *server,
298 krb5_error_code retval = 0;
299 /* null-terminated realm names */
300 char *clientz = NULL, *serverz = NULL;
305 clientz = k5memdup0(client->data, client->length, &retval);
309 serverz = k5memdup0(server->data, server->length, &retval);
317 retval = profile_get_values(context->profile, key, vals);
319 case PROF_NO_SECTION:
320 case PROF_NO_RELATION:
322 * Not found; don't return an error.
336 * Build tree by hierarchical traversal.
338 static krb5_error_code
339 rtree_hier_tree(krb5_context context,
340 const krb5_data *client,
341 const krb5_data *server,
342 krb5_principal **rettree,
345 krb5_error_code retval;
347 const krb5_data *dstrealm, *srcrealm;
348 krb5_principal *tree, *pprinc;
349 size_t nrealms, nprincs, i;
352 retval = rtree_hier_realms(context, client, server,
353 &realms, &nrealms, sep);
357 pprinc = tree = calloc(nprincs + 1, sizeof(krb5_principal));
362 for (i = 0; i < nrealms; i++)
365 for (i = 0; i < nrealms; i++) {
366 dstrealm = &realms[i];
367 retval = krb5int_tgtname(context, dstrealm, srcrealm, pprinc++);
368 if (retval) goto error;
372 free_realmlist(context, realms, nrealms);
375 while (pprinc != NULL && pprinc > tree) {
376 krb5_free_principal(context, *--pprinc);
379 free_realmlist(context, realms, nrealms);
385 * Construct list of realms between client and server.
387 static krb5_error_code
388 rtree_hier_realms(krb5_context context,
389 const krb5_data *client,
390 const krb5_data *server,
395 krb5_error_code retval;
397 krb5_data *ctweens = NULL, *stweens = NULL, *twp, *r, *rp;
398 size_t nctween, nstween;
404 c.str = client->data;
405 c.len = client->length;
406 c.dot = c.tail = NULL;
407 s.str = server->data;
408 s.len = server->length;
409 s.dot = s.tail = NULL;
411 comtail(&c, &s, sep);
412 adjtail(&c, &s, sep);
414 retval = rtree_hier_tweens(context, &c, &ctweens, &nctween, 1, sep);
415 if (retval) goto error;
416 retval = rtree_hier_tweens(context, &s, &stweens, &nstween, 0, sep);
417 if (retval) goto error;
419 rp = r = calloc(nctween + nstween, sizeof(krb5_data));
424 /* Copy client realm "tweens" forward. */
425 for (twp = ctweens; twp < &ctweens[nctween]; twp++) {
426 retval = krb5int_copy_data_contents(context, twp, rp);
427 if (retval) goto error;
430 /* Copy server realm "tweens" backward. */
431 for (twp = &stweens[nstween]; twp-- > stweens;) {
432 retval = krb5int_copy_data_contents(context, twp, rp);
433 if (retval) goto error;
440 free_realmlist(context, r, rp - r);
449 free_realmlist(krb5_context context,
455 for (i = 0; i < nrealms; i++)
456 krb5_free_data_contents(context, &realms[i]);
461 * Build a list of realms between a given realm and the common
462 * suffix. The original realm is included, but the "tail" is only
463 * included if DOTAIL is true.
465 * Warning: This function intentionally aliases memory. Caller must
466 * make copies as needed and not call krb5_free_data_contents, etc.
468 static krb5_error_code
469 rtree_hier_tweens(krb5_context context,
470 struct hstate *realm,
476 char *p, *r, *rtail, *lp;
478 krb5_data *tws, *ntws;
483 *tweens = ntws = tws = NULL;
486 for (lp = p = r; p < &r[rlen]; p++) {
487 if (*p != sep && &p[1] != &r[rlen])
489 if (lp == rtail && !dotail)
491 ntws = realloc(tws, (n + 1) * sizeof(krb5_data));
498 tws[n].length = &r[rlen] - lp;
510 * Adjust suffixes that each starts at the beginning of a component,
511 * to avoid the problem where "BC.EXAMPLE.COM" is erroneously reported
512 * as a parent of "ABC.EXAMPLE.COM".
515 adjtail(struct hstate *c, struct hstate *s, int sep)
522 if (cp == NULL || sp == NULL)
525 * Is it a full component? Yes, if it's the beginning of the
526 * string or there's a separator to the left.
528 * The index of -1 is valid because it only gets evaluated if the
529 * pointer is not at the beginning of the string.
531 cfull = (cp == c->str || cp[-1] == sep);
532 sfull = (sp == s->str || sp[-1] == sep);
534 * If they're both full components, we're done.
536 if (cfull && sfull) {
538 } else if (c->dot != NULL && s->dot != NULL) {
542 * Out of bounds? Can only happen if there are trailing dots.
544 if (cp >= &c->str[c->len] || sp >= &s->str[s->len]) {
555 * Find common suffix of C and S.
557 * C->TAIL and S->TAIL will point to the respective suffixes. C->DOT
558 * and S->DOT will point to the nearest instances of SEP to the right
559 * of the start of each suffix. Caller must initialize TAIL and DOT
563 comtail(struct hstate *c, struct hstate *s, int sep)
565 char *cp, *sp, *cdot, *sdot;
567 if (c->len == 0 || s->len == 0)
572 * ANSI/ISO C allows a pointer one past the end but not one
573 * before the beginning of an array.
575 cp = &c->str[c->len];
576 sp = &s->str[s->len];
578 * Set CP and SP to point to the common suffix of each string.
579 * When we run into separators (dots, unless someone has a X.500
580 * style realm), keep pointers to the latest pair.
582 while (cp > c->str && sp > s->str) {
583 if (*--cp != *--sp) {
585 * Didn't match, so most recent match is one byte to the
586 * right (or not at all).
593 * Keep track of matching dots.
600 /* No match found at all. */
601 if (cp == &c->str[c->len])
610 krb5_free_realm_tree(krb5_context context, krb5_principal *realms)
612 register krb5_principal *nrealms = realms;
616 krb5_free_principal(context, *nrealms);