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 /* A capaths value of "." means no intermediates. */
137 if (capvals != NULL && capvals[0] != NULL && *capvals[0] == '.') {
138 profile_free_list(capvals);
142 /* Count capaths (if any) and allocate space. Leave room for the client
143 * realm, server realm, and terminator. */
144 for (i = 0; capvals != NULL && capvals[i] != NULL; i++);
145 rpath = calloc(i + 3, sizeof(*rpath));
149 /* Populate rpath with the client realm, capaths, and server realm. */
150 retval = krb5int_copy_data_contents(context, client, &rpath[0]);
153 for (i = 0; capvals != NULL && capvals[i] != NULL; i++) {
154 d = make_data(capvals[i], strcspn(capvals[i], "\t "));
155 retval = krb5int_copy_data_contents(context, &d, &rpath[i + 1]);
159 retval = krb5int_copy_data_contents(context, server, &rpath[i + 1]);
163 /* Terminate rpath and return it. */
164 rpath[i + 2] = empty_data();
169 profile_free_list(capvals);
170 krb5int_free_data_list(context, rpath);
174 /* ANL - Modified to allow Configurable Authentication Paths.
175 * This modification removes the restriction on the choice of realm
176 * names, i.e. they nolonger have to be hierarchical. This
177 * is allowed by RFC 1510: "If a hierarchical organization is not used
178 * it may be necessary to consult some database in order to construct
179 * an authentication path between realms." The database is contained
180 * in the [capaths] section of the krb5.conf file.
181 * Client to server paths are defined. There are n**2 possible
182 * entries, but only those entries which are needed by the client
183 * or server need be present in its krb5.conf file. (n entries or 2*n
184 * entries if the same krb5.conf is used for clients and servers)
186 * for example: ESnet will be running a KDC which will share
187 * inter-realm keys with its many organizations which include among
188 * other ANL, NERSC and PNL. Each of these organizations wants to
189 * use its DNS name in the realm, ANL.GOV. In addition ANL wants
190 * to authenticatite to HAL.COM via a K5.MOON and K5.JUPITER
191 * A [capaths] section of the krb5.conf file for the ANL.GOV clients
192 * and servers would look like:
200 * HAL.COM = K5.JUPITER
212 * ANL.GOV = K5.JUPITER
216 * In the above a "." is used to mean directly connected since the
217 * the profile routines cannot handle a null entry.
219 * If no client-to-server path is found, the default hierarchical path
220 * is still generated.
222 * This version of the Configurable Authentication Path modification
223 * differs from the previous versions prior to K5 beta 5 in that
224 * the profile routines are used, and the explicit path from client's
225 * realm to server's realm must be given. The modifications will work
231 * Build a tree given a set of profile values retrieved by
232 * walk_rtree_capath_vals().
234 static krb5_error_code
235 rtree_capath_tree(krb5_context context,
236 const krb5_data *client,
237 const krb5_data *server,
239 krb5_principal **rettree)
241 krb5_error_code retval = 0;
242 unsigned int nvals, nlinks, nprincs, i;
243 krb5_data srcrealm, dstrealm;
244 krb5_principal *tree, *pprinc;
247 tree = pprinc = NULL;
248 for (nvals = 0; vals[nvals] != NULL; nvals++)
250 if (vals[0] != NULL && *vals[0] == '.') {
255 nprincs = nlinks + 2;
256 tree = calloc(nprincs + 1, sizeof(krb5_principal));
261 for (i = 0; i < nprincs + 1; i++)
263 /* Invariant: PPRINC points one past end of list. */
266 retval = krb5int_tgtname(context, client, client, pprinc++);
267 if (retval) goto error;
269 for (i = 0; i < nlinks; i++) {
270 dstrealm.data = vals[i];
271 dstrealm.length = strcspn(vals[i], "\t ");
272 retval = krb5int_tgtname(context, &dstrealm, &srcrealm, pprinc++);
273 if (retval) goto error;
276 retval = krb5int_tgtname(context, server, &srcrealm, pprinc++);
277 if (retval) goto error;
281 profile_free_list(vals);
283 while (pprinc != NULL && pprinc > &tree[0]) {
284 /* krb5_free_principal() correctly handles null input */
285 krb5_free_principal(context, *--pprinc);
294 * Get realm list from "capaths" section of the profile. Deliberately
295 * returns success but leaves VALS null if profile_get_values() fails
296 * by not finding anything.
298 static krb5_error_code
299 rtree_capath_vals(krb5_context context,
300 const krb5_data *client,
301 const krb5_data *server,
304 krb5_error_code retval = 0;
305 /* null-terminated realm names */
306 char *clientz = NULL, *serverz = NULL;
311 clientz = k5memdup0(client->data, client->length, &retval);
315 serverz = k5memdup0(server->data, server->length, &retval);
323 retval = profile_get_values(context->profile, key, vals);
325 case PROF_NO_SECTION:
326 case PROF_NO_RELATION:
328 * Not found; don't return an error.
342 * Build tree by hierarchical traversal.
344 static krb5_error_code
345 rtree_hier_tree(krb5_context context,
346 const krb5_data *client,
347 const krb5_data *server,
348 krb5_principal **rettree,
351 krb5_error_code retval;
353 const krb5_data *dstrealm, *srcrealm;
354 krb5_principal *tree, *pprinc;
355 size_t nrealms, nprincs, i;
358 retval = rtree_hier_realms(context, client, server,
359 &realms, &nrealms, sep);
363 pprinc = tree = calloc(nprincs + 1, sizeof(krb5_principal));
368 for (i = 0; i < nrealms; i++)
371 for (i = 0; i < nrealms; i++) {
372 dstrealm = &realms[i];
373 retval = krb5int_tgtname(context, dstrealm, srcrealm, pprinc++);
374 if (retval) goto error;
378 free_realmlist(context, realms, nrealms);
381 while (pprinc != NULL && pprinc > tree) {
382 krb5_free_principal(context, *--pprinc);
385 free_realmlist(context, realms, nrealms);
391 * Construct list of realms between client and server.
393 static krb5_error_code
394 rtree_hier_realms(krb5_context context,
395 const krb5_data *client,
396 const krb5_data *server,
401 krb5_error_code retval;
403 krb5_data *ctweens = NULL, *stweens = NULL, *twp, *r, *rp;
404 size_t nctween, nstween;
410 c.str = client->data;
411 c.len = client->length;
412 c.dot = c.tail = NULL;
413 s.str = server->data;
414 s.len = server->length;
415 s.dot = s.tail = NULL;
417 comtail(&c, &s, sep);
418 adjtail(&c, &s, sep);
420 retval = rtree_hier_tweens(context, &c, &ctweens, &nctween, 1, sep);
421 if (retval) goto error;
422 retval = rtree_hier_tweens(context, &s, &stweens, &nstween, 0, sep);
423 if (retval) goto error;
425 rp = r = calloc(nctween + nstween, sizeof(krb5_data));
430 /* Copy client realm "tweens" forward. */
431 for (twp = ctweens; twp < &ctweens[nctween]; twp++) {
432 retval = krb5int_copy_data_contents(context, twp, rp);
433 if (retval) goto error;
436 /* Copy server realm "tweens" backward. */
437 for (twp = &stweens[nstween]; twp-- > stweens;) {
438 retval = krb5int_copy_data_contents(context, twp, rp);
439 if (retval) goto error;
446 free_realmlist(context, r, rp - r);
455 free_realmlist(krb5_context context,
461 for (i = 0; i < nrealms; i++)
462 krb5_free_data_contents(context, &realms[i]);
467 * Build a list of realms between a given realm and the common
468 * suffix. The original realm is included, but the "tail" is only
469 * included if DOTAIL is true.
471 * Warning: This function intentionally aliases memory. Caller must
472 * make copies as needed and not call krb5_free_data_contents, etc.
474 static krb5_error_code
475 rtree_hier_tweens(krb5_context context,
476 struct hstate *realm,
482 char *p, *r, *rtail, *lp;
484 krb5_data *tws, *ntws;
489 *tweens = ntws = tws = NULL;
492 for (lp = p = r; p < &r[rlen]; p++) {
493 if (*p != sep && &p[1] != &r[rlen])
495 if (lp == rtail && !dotail)
497 ntws = realloc(tws, (n + 1) * sizeof(krb5_data));
504 tws[n].length = &r[rlen] - lp;
516 * Adjust suffixes that each starts at the beginning of a component,
517 * to avoid the problem where "BC.EXAMPLE.COM" is erroneously reported
518 * as a parent of "ABC.EXAMPLE.COM".
521 adjtail(struct hstate *c, struct hstate *s, int sep)
528 if (cp == NULL || sp == NULL)
531 * Is it a full component? Yes, if it's the beginning of the
532 * string or there's a separator to the left.
534 * The index of -1 is valid because it only gets evaluated if the
535 * pointer is not at the beginning of the string.
537 cfull = (cp == c->str || cp[-1] == sep);
538 sfull = (sp == s->str || sp[-1] == sep);
540 * If they're both full components, we're done.
542 if (cfull && sfull) {
544 } else if (c->dot != NULL && s->dot != NULL) {
548 * Out of bounds? Can only happen if there are trailing dots.
550 if (cp >= &c->str[c->len] || sp >= &s->str[s->len]) {
561 * Find common suffix of C and S.
563 * C->TAIL and S->TAIL will point to the respective suffixes. C->DOT
564 * and S->DOT will point to the nearest instances of SEP to the right
565 * of the start of each suffix. Caller must initialize TAIL and DOT
569 comtail(struct hstate *c, struct hstate *s, int sep)
571 char *cp, *sp, *cdot, *sdot;
573 if (c->len == 0 || s->len == 0)
578 * ANSI/ISO C allows a pointer one past the end but not one
579 * before the beginning of an array.
581 cp = &c->str[c->len];
582 sp = &s->str[s->len];
584 * Set CP and SP to point to the common suffix of each string.
585 * When we run into separators (dots, unless someone has a X.500
586 * style realm), keep pointers to the latest pair.
588 while (cp > c->str && sp > s->str) {
589 if (*--cp != *--sp) {
591 * Didn't match, so most recent match is one byte to the
592 * right (or not at all).
599 * Keep track of matching dots.
606 /* No match found at all. */
607 if (cp == &c->str[c->len])
616 krb5_free_realm_tree(krb5_context context, krb5_principal *realms)
618 krb5_principal *nrealms = realms;
622 krb5_free_principal(context, *nrealms);