1 /* 2 * lib/krb5/krb/chk_trans.c 3 * 4 * Copyright 2001 by the Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 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. 11 * 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. 25 * 26 * 27 * krb5_check_transited_list() 28 */ 29 #include "k5-int.h" 30 #include <stdarg.h> 31 32 #if defined (TEST) || defined (TEST2) 33 # undef DEBUG 34 # define DEBUG 35 #endif 36 37 #ifdef DEBUG 38 #define verbose krb5int_chk_trans_verbose 39 static int verbose = 0; 40 # define Tprintf(ARGS) if (verbose) printf ARGS 41 #else 42 # define Tprintf(ARGS) (void)(0) 43 #endif 44 45 #define MAXLEN 512 46 47 static krb5_error_code 48 process_intermediates (krb5_error_code (*fn)(krb5_data *, void *), void *data, 49 const krb5_data *n1, const krb5_data *n2) { 50 unsigned int len1, len2, i; 51 char *p1, *p2; 52 53 Tprintf (("process_intermediates(%.*s,%.*s)\n", 54 (int) n1->length, n1->data, (int) n2->length, n2->data)); 55 56 len1 = n1->length; 57 len2 = n2->length; 58 59 Tprintf (("(walking intermediates now)\n")); 60 /* Simplify... */ 61 if (len1 > len2) { 62 const krb5_data *p; 63 int tmp = len1; 64 len1 = len2; 65 len2 = tmp; 66 p = n1; 67 n1 = n2; 68 n2 = p; 69 } 70 /* Okay, now len1 is always shorter or equal. */ 71 if (len1 == len2) { 72 if (memcmp (n1->data, n2->data, len1)) { 73 Tprintf (("equal length but different strings in path: '%.*s' '%.*s'\n", 74 (int) n1->length, n1->data, (int) n2->length, n2->data)); 75 return KRB5KRB_AP_ERR_ILL_CR_TKT; 76 } 77 Tprintf (("(end intermediates)\n")); 78 return 0; 79 } 80 /* Now len1 is always shorter. */ 81 if (len1 == 0) 82 /* Shouldn't be possible. Internal error? */ 83 return KRB5KRB_AP_ERR_ILL_CR_TKT; 84 p1 = n1->data; 85 p2 = n2->data; 86 if (p1[0] == '/') { 87 /* X.500 style names, with common prefix. */ 88 if (p2[0] != '/') { 89 Tprintf (("mixed name formats in path: x500='%.*s' domain='%.*s'\n", 90 (int) len1, p1, (int) len2, p2)); 91 return KRB5KRB_AP_ERR_ILL_CR_TKT; 92 } 93 if (memcmp (p1, p2, len1)) { 94 Tprintf (("x500 names with different prefixes '%.*s' '%.*s'\n", 95 (int) len1, p1, (int) len2, p2)); 96 return KRB5KRB_AP_ERR_ILL_CR_TKT; 97 } 98 for (i = len1 + 1; i < len2; i++) 99 if (p2[i] == '/') { 100 krb5_data d; 101 krb5_error_code r; 102 103 d.data = p2; 104 d.length = i; 105 r = (*fn) (&d, data); 106 if (r) 107 return r; 108 } 109 } else { 110 /* Domain style names, with common suffix. */ 111 if (p2[0] == '/') { 112 Tprintf (("mixed name formats in path: domain='%.*s' x500='%.*s'\n", 113 (int) len1, p1, (int) len2, p2)); 114 return KRB5KRB_AP_ERR_ILL_CR_TKT; 115 } 116 if (memcmp (p1, p2 + (len2 - len1), len1)) { 117 Tprintf (("domain names with different suffixes '%.*s' '%.*s'\n", 118 (int) len1, p1, (int) len2, p2)); 119 return KRB5KRB_AP_ERR_ILL_CR_TKT; 120 } 121 for (i = len2 - len1 - 1; i > 0; i--) { 122 Tprintf (("looking at '%.*s'\n", (int) (len2 - i), p2+i)); 123 if (p2[i-1] == '.') { 124 krb5_data d; 125 krb5_error_code r; 126 127 d.data = p2+i; 128 d.length = len2 - i; 129 r = (*fn) (&d, data); 130 if (r) 131 return r; 132 } 133 } 134 } 135 Tprintf (("(end intermediates)\n")); 136 return 0; 137 } 138 139 static krb5_error_code 140 maybe_join (krb5_data *last, krb5_data *buf, int bufsiz) 141 { 142 if (buf->length == 0) 143 return 0; 144 if (buf->data[0] == '/') { 145 if (last->length + buf->length > bufsiz) { 146 Tprintf (("too big: last=%d cur=%d max=%d\n", last->length, buf->length, bufsiz)); 147 return KRB5KRB_AP_ERR_ILL_CR_TKT; 148 } 149 memmove (buf->data+last->length, buf->data, buf->length); 150 memcpy (buf->data, last->data, last->length); 151 buf->length += last->length; 152 } else if (buf->data[buf->length-1] == '.') { 153 /* We can ignore the case where the previous component was 154 empty; the strcat will be a no-op. It should probably 155 be an error case, but let's be flexible. */ 156 if (last->length+buf->length > bufsiz) { 157 Tprintf (("too big\n")); 158 return KRB5KRB_AP_ERR_ILL_CR_TKT; 159 } 160 memcpy (buf->data + buf->length, last->data, last->length); 161 buf->length += last->length; 162 } 163 /* Otherwise, do nothing. */ 164 return 0; 165 } 166 167 /* The input strings cannot contain any \0 bytes, according to the 168 spec, but our API is such that they may not be \0 terminated 169 either. Thus we keep on treating them as krb5_data objects instead 170 of C strings. */ 171 static krb5_error_code 172 foreach_realm (krb5_error_code (*fn)(krb5_data *comp,void *data), void *data, 173 const krb5_data *crealm, const krb5_data *srealm, 174 const krb5_data *transit) 175 { 176 char buf[MAXLEN], last[MAXLEN]; 177 char *p, *bufp; 178 int next_lit, intermediates, l; 179 krb5_data this_component; 180 krb5_error_code r; 181 krb5_data last_component; 182 183 /* Invariants: 184 - last_component points to last[] 185 - this_component points to buf[] 186 - last_component has length of last 187 - this_component has length of buf when calling out 188 Keep these consistent, and we should be okay. */ 189 190 next_lit = 0; 191 intermediates = 0; 192 memset (buf, 0, sizeof (buf)); 193 194 this_component.data = buf; 195 last_component.data = last; 196 last_component.length = 0; 197 198 #define print_data(fmt,d) Tprintf((fmt,(int)(d)->length,(d)->data)) 199 print_data ("client realm: %.*s\n", crealm); 200 print_data ("server realm: %.*s\n", srealm); 201 print_data ("transit enc.: %.*s\n", transit); 202 203 if (transit->length == 0) { 204 Tprintf (("no other realms transited\n")); 205 return 0; 206 } 207 208 bufp = buf; 209 for (p = transit->data, l = transit->length; l; p++, l--) { 210 if (next_lit) { 211 *bufp++ = *p; 212 if (bufp == buf+sizeof(buf)) 213 return KRB5KRB_AP_ERR_ILL_CR_TKT; 214 next_lit = 0; 215 } else if (*p == '\\') { 216 next_lit = 1; 217 } else if (*p == ',') { 218 if (bufp != buf) { 219 this_component.length = bufp - buf; 220 r = maybe_join (&last_component, &this_component, sizeof(buf)); 221 if (r) 222 return r; 223 r = (*fn) (&this_component, data); 224 if (r) 225 return r; 226 if (intermediates) { 227 if (p == transit->data) 228 r = process_intermediates (fn, data, 229 &this_component, crealm); 230 else { 231 r = process_intermediates (fn, data, &this_component, 232 &last_component); 233 } 234 if (r) 235 return r; 236 } 237 intermediates = 0; 238 memcpy (last, buf, sizeof (buf)); 239 last_component.length = this_component.length; 240 memset (buf, 0, sizeof (buf)); 241 bufp = buf; 242 } else { 243 intermediates = 1; 244 if (p == transit->data) { 245 if (crealm->length >= MAXLEN) 246 return KRB5KRB_AP_ERR_ILL_CR_TKT; 247 memcpy (last, crealm->data, crealm->length); 248 last[crealm->length] = '\0'; 249 last_component.length = crealm->length; 250 } 251 } 252 } else if (*p == ' ' && bufp == buf) { 253 /* This next component stands alone, even if it has a 254 trailing dot or leading slash. */ 255 memset (last, 0, sizeof (last)); 256 last_component.length = 0; 257 } else { 258 /* Not a special character; literal. */ 259 *bufp++ = *p; 260 if (bufp == buf+sizeof(buf)) 261 return KRB5KRB_AP_ERR_ILL_CR_TKT; 262 } 263 } 264 /* At end. Must be normal state. */ 265 if (next_lit) 266 Tprintf (("ending in next-char-literal state\n")); 267 /* Process trailing element or comma. */ 268 if (bufp == buf) { 269 /* Trailing comma. */ 270 r = process_intermediates (fn, data, &last_component, srealm); 271 } else { 272 /* Trailing component. */ 273 this_component.length = bufp - buf; 274 r = maybe_join (&last_component, &this_component, sizeof(buf)); 275 if (r) 276 return r; 277 r = (*fn) (&this_component, data); 278 if (r) 279 return r; 280 if (intermediates) 281 r = process_intermediates (fn, data, &this_component, 282 &last_component); 283 } 284 if (r != 0) 285 return r; 286 return 0; 287 } 288 289 290 struct check_data { 291 krb5_context ctx; 292 krb5_principal *tgs; 293 }; 294 295 static int 296 same_data (krb5_data *d1, krb5_data *d2) 297 { 298 return (d1->length == d2->length 299 && !memcmp (d1->data, d2->data, d1->length)); 300 } 301 302 static krb5_error_code 303 check_realm_in_list (krb5_data *realm, void *data) 304 { 305 struct check_data *cdata = data; 306 int i; 307 308 Tprintf ((".. checking '%.*s'\n", (int) realm->length, realm->data)); 309 for (i = 0; cdata->tgs[i]; i++) { 310 if (same_data (krb5_princ_realm (cdata->ctx, cdata->tgs[i]), realm)) 311 return 0; 312 } 313 Tprintf (("BAD!\n")); 314 return KRB5KRB_AP_ERR_ILL_CR_TKT; 315 } 316 317 krb5_error_code 318 krb5_check_transited_list (krb5_context ctx, const krb5_data *trans_in, 319 const krb5_data *crealm, const krb5_data *srealm) 320 { 321 krb5_data trans; 322 struct check_data cdata; 323 krb5_error_code r; 324 325 trans.length = trans_in->length; 326 trans.data = (char *) trans_in->data; 327 if (trans.length && (trans.data[trans.length-1] == '\0')) 328 trans.length--; 329 330 Tprintf (("krb5_check_transited_list(trans=\"%.*s\", crealm=\"%.*s\", srealm=\"%.*s\")\n", 331 (int) trans.length, trans.data, 332 (int) crealm->length, crealm->data, 333 (int) srealm->length, srealm->data)); 334 if (trans.length == 0) 335 return 0; 336 r = krb5_walk_realm_tree (ctx, crealm, srealm, &cdata.tgs, 337 KRB5_REALM_BRANCH_CHAR); 338 if (r) { 339 Tprintf (("error %ld\n", (long) r)); 340 return r; 341 } 342 #ifdef DEBUG /* avoid compiler warning about 'd' unused */ 343 { 344 int i; 345 Tprintf (("tgs list = {\n")); 346 for (i = 0; cdata.tgs[i]; i++) { 347 char *name; 348 r = krb5_unparse_name (ctx, cdata.tgs[i], &name); 349 Tprintf (("\t'%s'\n", name)); 350 free (name); 351 } 352 Tprintf (("}\n")); 353 } 354 #endif 355 cdata.ctx = ctx; 356 r = foreach_realm (check_realm_in_list, &cdata, crealm, srealm, &trans); 357 krb5_free_realm_tree (ctx, cdata.tgs); 358 return r; 359 } 360 361 #ifdef TEST 362 363 static krb5_error_code 364 print_a_realm (krb5_data *realm, void *data) 365 { 366 printf ("%.*s\n", (int) realm->length, realm->data); 367 return 0; 368 } 369 370 int main (int argc, char *argv[]) { 371 const char *me; 372 krb5_data crealm, srealm, transit; 373 krb5_error_code r; 374 int expand_only = 0; 375 376 me = strrchr (argv[0], '/'); 377 me = me ? me+1 : argv[0]; 378 379 while (argc > 3 && argv[1][0] == '-') { 380 if (!strcmp ("-v", argv[1])) 381 verbose++, argc--, argv++; 382 else if (!strcmp ("-x", argv[1])) 383 expand_only++, argc--, argv++; 384 else 385 goto usage; 386 } 387 388 if (argc != 4) { 389 usage: 390 printf ("usage: %s [-v] [-x] clientRealm serverRealm transitEncoding\n", 391 me); 392 return 1; 393 } 394 395 crealm.data = argv[1]; 396 crealm.length = strlen(argv[1]); 397 srealm.data = argv[2]; 398 srealm.length = strlen(argv[2]); 399 transit.data = argv[3]; 400 transit.length = strlen(argv[3]); 401 402 if (expand_only) { 403 404 printf ("client realm: %s\n", argv[1]); 405 printf ("server realm: %s\n", argv[2]); 406 printf ("transit enc.: %s\n", argv[3]); 407 408 if (argv[3][0] == 0) { 409 printf ("no other realms transited\n"); 410 return 0; 411 } 412 413 r = foreach_realm (print_a_realm, NULL, &crealm, &srealm, &transit); 414 if (r) 415 printf ("--> returned error %ld\n", (long) r); 416 return r != 0; 417 418 } else { 419 420 /* Actually check the values against the supplied krb5.conf file. */ 421 krb5_context ctx; 422 r = krb5_init_context (&ctx); 423 if (r) { 424 com_err (me, r, "initializing krb5 context"); 425 return 1; 426 } 427 r = krb5_check_transited_list (ctx, &transit, &crealm, &srealm); 428 if (r == KRB5KRB_AP_ERR_ILL_CR_TKT) { 429 printf ("NO\n"); 430 } else if (r == 0) { 431 printf ("YES\n"); 432 } else { 433 printf ("kablooey!\n"); 434 com_err (me, r, "checking transited-realm list"); 435 return 1; 436 } 437 return 0; 438 } 439 } 440 441 #endif /* TEST */ 442