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