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 [capath] 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 [capath] section of the krb5.conf file for the ANL.GOV clients 49 * and servers would look like: 50 * 51 * [capath] 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.confmode 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(context, client, server, tree, realm_branch_char) 120 krb5_context context; 121 const krb5_data *client, *server; 122 krb5_principal **tree; 123 int realm_branch_char; 124 { 125 krb5_error_code retval; 126 krb5_principal *rettree; 127 register char *ccp, *scp; 128 register char *prevccp = 0, *prevscp = 0; 129 char *com_sdot = 0, *com_cdot = 0; 130 register int i, links = 0; 131 int clen, slen; 132 krb5_data tmpcrealm, tmpsrealm; 133 int nocommon = 1; 134 135 #ifdef CONFIGURABLE_AUTHENTICATION_PATH 136 const char *cap_names[4]; 137 char *cap_client, *cap_server; 138 char **cap_nodes; 139 krb5_error_code cap_code; 140 #endif 141 if (!(client->data &&server->data)) 142 return KRB5_NO_TKT_IN_RLM; 143 #ifdef CONFIGURABLE_AUTHENTICATION_PATH 144 if ((cap_client = (char *)malloc(client->length + 1)) == NULL) 145 return ENOMEM; 146 strncpy(cap_client, client->data, client->length); 147 cap_client[client->length] = '\0'; 148 if ((cap_server = (char *)malloc(server->length + 1)) == NULL) { 149 krb5_xfree(cap_client); 150 return ENOMEM; 151 } 152 strncpy(cap_server, server->data, server->length); 153 cap_server[server->length] = '\0'; 154 cap_names[0] = "capaths"; 155 cap_names[1] = cap_client; 156 cap_names[2] = cap_server; 157 cap_names[3] = 0; 158 cap_code = profile_get_values(context->profile, cap_names, &cap_nodes); 159 krb5_xfree(cap_names[1]); /* done with client string */ 160 if (cap_code == 0) { /* found a path, so lets use it */ 161 links = 0; 162 if (*cap_nodes[0] != '.') { /* a link of . means direct */ 163 while(cap_nodes[links]) { 164 links++; 165 } 166 } 167 cap_nodes[links] = cap_server; /* put server on end of list */ 168 /* this simplifies the code later and make */ 169 /* cleanup eaiser as well */ 170 links++; /* count the null entry at end */ 171 } else { /* no path use hierarchical method */ 172 krb5_xfree(cap_names[2]); /* failed, don't need server string */ 173 #endif 174 clen = client->length; 175 slen = server->length; 176 177 for (com_cdot = ccp = client->data + clen - 1, 178 com_sdot = scp = server->data + slen - 1; 179 clen && slen && *ccp == *scp ; 180 ccp--, scp--, clen--, slen--) { 181 if (*ccp == realm_branch_char) { 182 com_cdot = ccp; 183 com_sdot = scp; 184 nocommon = 0; 185 } 186 } 187 188 /* ccp, scp point to common root. 189 com_cdot, com_sdot point to common components. */ 190 /* handle case of one ran out */ 191 if (!clen) { 192 /* construct path from client to server, down the tree */ 193 if (!slen) 194 /* in the same realm--this means there is no ticket 195 in this realm. */ 196 return KRB5_NO_TKT_IN_RLM; 197 if (*scp == realm_branch_char) { 198 /* one is a subdomain of the other */ 199 com_cdot = client->data; 200 com_sdot = scp; 201 nocommon = 0; 202 } /* else normal case of two sharing parents */ 203 } 204 if (!slen) { 205 /* construct path from client to server, up the tree */ 206 if (*ccp == realm_branch_char) { 207 /* one is a subdomain of the other */ 208 com_sdot = server->data; 209 com_cdot = ccp; 210 nocommon = 0; 211 } /* else normal case of two sharing parents */ 212 } 213 /* determine #links to/from common ancestor */ 214 if (nocommon) 215 links = 1; 216 else 217 links = 2; 218 /* if no common ancestor, artificially set up common root at the last 219 component, then join with special code */ 220 for (ccp = client->data; ccp < com_cdot; ccp++) { 221 if (*ccp == realm_branch_char) { 222 links++; 223 if (nocommon) 224 prevccp = ccp; 225 } 226 } 227 228 for (scp = server->data; scp < com_sdot; scp++) { 229 if (*scp == realm_branch_char) { 230 links++; 231 if (nocommon) 232 prevscp = scp; 233 } 234 } 235 if (nocommon) { 236 if (prevccp) 237 com_cdot = prevccp; 238 if (prevscp) 239 com_sdot = prevscp; 240 241 if(com_cdot == client->data + client->length -1) 242 com_cdot = client->data - 1 ; 243 if(com_sdot == server->data + server->length -1) 244 com_sdot = server->data - 1 ; 245 } 246 #ifdef CONFIGURABLE_AUTHENTICATION_PATH 247 } /* end of if use hierarchical method */ 248 #endif 249 250 if (!(rettree = (krb5_principal *)calloc(links+2, 251 sizeof(krb5_principal)))) { 252 return ENOMEM; 253 } 254 i = 1; 255 if ((retval = krb5_tgtname(context, client, client, &rettree[0]))) { 256 krb5_xfree(rettree); 257 return retval; 258 } 259 #ifdef CONFIGURABLE_AUTHENTICATION_PATH 260 links--; /* dont count the null entry on end */ 261 if (cap_code == 0) { /* found a path above */ 262 tmpcrealm.data = client->data; 263 tmpcrealm.length = client->length; 264 while( i-1 <= links) { 265 266 tmpsrealm.data = cap_nodes[i-1]; 267 /* don't count trailing whitespace from profile_get */ 268 tmpsrealm.length = strcspn(cap_nodes[i-1],"\t "); 269 if ((retval = krb5_tgtname(context, 270 &tmpsrealm, 271 &tmpcrealm, 272 &rettree[i]))) { 273 while (i) { 274 krb5_free_principal(context, rettree[i-1]); 275 i--; 276 } 277 krb5_xfree(rettree); 278 /* cleanup the cap_nodes from profile_get */ 279 for (i = 0; i<=links; i++) { 280 krb5_xfree(cap_nodes[i]); 281 } 282 krb5_xfree((char *)cap_nodes); 283 return retval; 284 } 285 tmpcrealm.data = tmpsrealm.data; 286 tmpcrealm.length = tmpsrealm.length; 287 i++; 288 } 289 /* cleanup the cap_nodes from profile_get last one has server */ 290 for (i = 0; i<=links; i++) { 291 krb5_xfree(cap_nodes[i]); 292 } 293 krb5_xfree((char *)cap_nodes); 294 } else { /* if not cap then use hierarchical method */ 295 #endif 296 for (prevccp = ccp = client->data; 297 ccp <= com_cdot; 298 ccp++) { 299 if (*ccp != realm_branch_char) 300 continue; 301 ++ccp; /* advance past dot */ 302 tmpcrealm.data = prevccp; 303 tmpcrealm.length = client->length - 304 (prevccp - client->data); 305 tmpsrealm.data = ccp; 306 tmpsrealm.length = client->length - 307 (ccp - client->data); 308 if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm, 309 &rettree[i]))) { 310 while (i) { 311 krb5_free_principal(context, rettree[i-1]); 312 i--; 313 } 314 krb5_xfree(rettree); 315 return retval; 316 } 317 prevccp = ccp; 318 i++; 319 } 320 if (nocommon) { 321 tmpcrealm.data = com_cdot + 1; 322 tmpcrealm.length = client->length - 323 (com_cdot + 1 - client->data); 324 tmpsrealm.data = com_sdot + 1; 325 tmpsrealm.length = server->length - 326 (com_sdot + 1 - server->data); 327 if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm, 328 &rettree[i]))) { 329 while (i) { 330 krb5_free_principal(context, rettree[i-1]); 331 i--; 332 } 333 krb5_xfree(rettree); 334 return retval; 335 } 336 i++; 337 } 338 339 for (prevscp = com_sdot + 1, scp = com_sdot - 1; 340 scp > server->data; 341 scp--) { 342 if (*scp != realm_branch_char) 343 continue; 344 if (scp - 1 < server->data) 345 break; /* XXX only if . starts realm? */ 346 tmpcrealm.data = prevscp; 347 tmpcrealm.length = server->length - 348 (prevscp - server->data); 349 tmpsrealm.data = scp + 1; 350 tmpsrealm.length = server->length - 351 (scp + 1 - server->data); 352 if ((retval = krb5_tgtname(context, &tmpsrealm, &tmpcrealm, 353 &rettree[i]))) { 354 while (i) { 355 krb5_free_principal(context, rettree[i-1]); 356 i--; 357 } 358 krb5_xfree(rettree); 359 return retval; 360 } 361 prevscp = scp + 1; 362 i++; 363 } 364 if (slen && com_sdot >= server->data) { 365 /* only necessary if building down tree from ancestor or client */ 366 /* however, we can get here if we have only one component 367 in the server realm name, hence we make sure we found a component 368 separator there... */ 369 tmpcrealm.data = prevscp; 370 tmpcrealm.length = server->length - 371 (prevscp - server->data); 372 if ((retval = krb5_tgtname(context, server, &tmpcrealm, 373 &rettree[i]))) { 374 while (i) { 375 krb5_free_principal(context, rettree[i-1]); 376 i--; 377 } 378 krb5_xfree(rettree); 379 return retval; 380 } 381 } 382 #ifdef CONFIGURABLE_AUTHENTICATION_PATH 383 } 384 #endif 385 *tree = rettree; 386 return 0; 387 } 388