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 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 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