1 /* $NetBSD: transited.c,v 1.1.1.2 2014/04/24 12:45:51 pettai Exp $ */ 2 3 /* 4 * Copyright (c) 1997 - 2001, 2003 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "krb5_locl.h" 37 38 /* this is an attempt at one of the most horrible `compression' 39 schemes that has ever been invented; it's so amazingly brain-dead 40 that words can not describe it, and all this just to save a few 41 silly bytes */ 42 43 struct tr_realm { 44 char *realm; 45 unsigned leading_space:1; 46 unsigned leading_slash:1; 47 unsigned trailing_dot:1; 48 struct tr_realm *next; 49 }; 50 51 static void 52 free_realms(struct tr_realm *r) 53 { 54 struct tr_realm *p; 55 while(r){ 56 p = r; 57 r = r->next; 58 free(p->realm); 59 free(p); 60 } 61 } 62 63 static int 64 make_path(krb5_context context, struct tr_realm *r, 65 const char *from, const char *to) 66 { 67 struct tr_realm *tmp; 68 const char *p; 69 70 if(strlen(from) < strlen(to)){ 71 const char *str; 72 str = from; 73 from = to; 74 to = str; 75 } 76 77 if(strcmp(from + strlen(from) - strlen(to), to) == 0){ 78 p = from; 79 while(1){ 80 p = strchr(p, '.'); 81 if(p == NULL) { 82 krb5_clear_error_message (context); 83 return KRB5KDC_ERR_POLICY; 84 } 85 p++; 86 if(strcmp(p, to) == 0) 87 break; 88 tmp = calloc(1, sizeof(*tmp)); 89 if(tmp == NULL) 90 return krb5_enomem(context); 91 tmp->next = r->next; 92 r->next = tmp; 93 tmp->realm = strdup(p); 94 if(tmp->realm == NULL){ 95 r->next = tmp->next; 96 free(tmp); 97 return krb5_enomem(context); 98 } 99 } 100 }else if(strncmp(from, to, strlen(to)) == 0){ 101 p = from + strlen(from); 102 while(1){ 103 while(p >= from && *p != '/') p--; 104 if(p == from) 105 return KRB5KDC_ERR_POLICY; 106 107 if(strncmp(to, from, p - from) == 0) 108 break; 109 tmp = calloc(1, sizeof(*tmp)); 110 if(tmp == NULL) 111 return krb5_enomem(context); 112 tmp->next = r->next; 113 r->next = tmp; 114 tmp->realm = malloc(p - from + 1); 115 if(tmp->realm == NULL){ 116 r->next = tmp->next; 117 free(tmp); 118 return krb5_enomem(context); 119 } 120 memcpy(tmp->realm, from, p - from); 121 tmp->realm[p - from] = '\0'; 122 p--; 123 } 124 } else { 125 krb5_clear_error_message (context); 126 return KRB5KDC_ERR_POLICY; 127 } 128 129 return 0; 130 } 131 132 static int 133 make_paths(krb5_context context, 134 struct tr_realm *realms, const char *client_realm, 135 const char *server_realm) 136 { 137 struct tr_realm *r; 138 int ret; 139 const char *prev_realm = client_realm; 140 const char *next_realm = NULL; 141 for(r = realms; r; r = r->next){ 142 /* it *might* be that you can have more than one empty 143 component in a row, at least that's how I interpret the 144 "," exception in 1510 */ 145 if(r->realm[0] == '\0'){ 146 while(r->next && r->next->realm[0] == '\0') 147 r = r->next; 148 if(r->next) 149 next_realm = r->next->realm; 150 else 151 next_realm = server_realm; 152 ret = make_path(context, r, prev_realm, next_realm); 153 if(ret){ 154 free_realms(realms); 155 return ret; 156 } 157 } 158 prev_realm = r->realm; 159 } 160 return 0; 161 } 162 163 static int 164 expand_realms(krb5_context context, 165 struct tr_realm *realms, const char *client_realm) 166 { 167 struct tr_realm *r; 168 const char *prev_realm = NULL; 169 for(r = realms; r; r = r->next){ 170 if(r->trailing_dot){ 171 char *tmp; 172 size_t len; 173 174 if(prev_realm == NULL) 175 prev_realm = client_realm; 176 177 len = strlen(r->realm) + strlen(prev_realm) + 1; 178 179 tmp = realloc(r->realm, len); 180 if(tmp == NULL){ 181 free_realms(realms); 182 return krb5_enomem(context); 183 } 184 r->realm = tmp; 185 strlcat(r->realm, prev_realm, len); 186 }else if(r->leading_slash && !r->leading_space && prev_realm){ 187 /* yet another exception: if you use x500-names, the 188 leading realm doesn't have to be "quoted" with a space */ 189 char *tmp; 190 size_t len = strlen(r->realm) + strlen(prev_realm) + 1; 191 192 tmp = malloc(len); 193 if(tmp == NULL){ 194 free_realms(realms); 195 return krb5_enomem(context); 196 } 197 strlcpy(tmp, prev_realm, len); 198 strlcat(tmp, r->realm, len); 199 free(r->realm); 200 r->realm = tmp; 201 } 202 prev_realm = r->realm; 203 } 204 return 0; 205 } 206 207 static struct tr_realm * 208 make_realm(char *realm) 209 { 210 struct tr_realm *r; 211 char *p, *q; 212 int quote = 0; 213 r = calloc(1, sizeof(*r)); 214 if(r == NULL){ 215 free(realm); 216 return NULL; 217 } 218 r->realm = realm; 219 for(p = q = r->realm; *p; p++){ 220 if(p == r->realm && *p == ' '){ 221 r->leading_space = 1; 222 continue; 223 } 224 if(q == r->realm && *p == '/') 225 r->leading_slash = 1; 226 if(quote){ 227 *q++ = *p; 228 quote = 0; 229 continue; 230 } 231 if(*p == '\\'){ 232 quote = 1; 233 continue; 234 } 235 if(p[0] == '.' && p[1] == '\0') 236 r->trailing_dot = 1; 237 *q++ = *p; 238 } 239 *q = '\0'; 240 return r; 241 } 242 243 static struct tr_realm* 244 append_realm(struct tr_realm *head, struct tr_realm *r) 245 { 246 struct tr_realm *p; 247 if(head == NULL){ 248 r->next = NULL; 249 return r; 250 } 251 p = head; 252 while(p->next) p = p->next; 253 p->next = r; 254 return head; 255 } 256 257 static int 258 decode_realms(krb5_context context, 259 const char *tr, int length, struct tr_realm **realms) 260 { 261 struct tr_realm *r = NULL; 262 263 char *tmp; 264 int quote = 0; 265 const char *start = tr; 266 int i; 267 268 for(i = 0; i < length; i++){ 269 if(quote){ 270 quote = 0; 271 continue; 272 } 273 if(tr[i] == '\\'){ 274 quote = 1; 275 continue; 276 } 277 if(tr[i] == ','){ 278 tmp = malloc(tr + i - start + 1); 279 if(tmp == NULL) 280 return krb5_enomem(context); 281 memcpy(tmp, start, tr + i - start); 282 tmp[tr + i - start] = '\0'; 283 r = make_realm(tmp); 284 if(r == NULL){ 285 free_realms(*realms); 286 return krb5_enomem(context); 287 } 288 *realms = append_realm(*realms, r); 289 start = tr + i + 1; 290 } 291 } 292 tmp = malloc(tr + i - start + 1); 293 if(tmp == NULL){ 294 free(*realms); 295 return krb5_enomem(context); 296 } 297 memcpy(tmp, start, tr + i - start); 298 tmp[tr + i - start] = '\0'; 299 r = make_realm(tmp); 300 if(r == NULL){ 301 free_realms(*realms); 302 return krb5_enomem(context); 303 } 304 *realms = append_realm(*realms, r); 305 306 return 0; 307 } 308 309 310 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 311 krb5_domain_x500_decode(krb5_context context, 312 krb5_data tr, char ***realms, unsigned int *num_realms, 313 const char *client_realm, const char *server_realm) 314 { 315 struct tr_realm *r = NULL; 316 struct tr_realm *p, **q; 317 int ret; 318 319 if(tr.length == 0) { 320 *realms = NULL; 321 *num_realms = 0; 322 return 0; 323 } 324 325 /* split string in components */ 326 ret = decode_realms(context, tr.data, tr.length, &r); 327 if(ret) 328 return ret; 329 330 /* apply prefix rule */ 331 ret = expand_realms(context, r, client_realm); 332 if(ret) 333 return ret; 334 335 ret = make_paths(context, r, client_realm, server_realm); 336 if(ret) 337 return ret; 338 339 /* remove empty components and count realms */ 340 *num_realms = 0; 341 for(q = &r; *q; ){ 342 if((*q)->realm[0] == '\0'){ 343 p = *q; 344 *q = (*q)->next; 345 free(p->realm); 346 free(p); 347 }else{ 348 q = &(*q)->next; 349 (*num_realms)++; 350 } 351 } 352 if (*num_realms + 1 > UINT_MAX/sizeof(**realms)) 353 return ERANGE; 354 355 { 356 char **R; 357 R = malloc((*num_realms + 1) * sizeof(*R)); 358 if (R == NULL) 359 return krb5_enomem(context); 360 *realms = R; 361 while(r){ 362 *R++ = r->realm; 363 p = r->next; 364 free(r); 365 r = p; 366 } 367 } 368 return 0; 369 } 370 371 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 372 krb5_domain_x500_encode(char **realms, unsigned int num_realms, 373 krb5_data *encoding) 374 { 375 char *s = NULL; 376 int len = 0; 377 unsigned int i; 378 krb5_data_zero(encoding); 379 if (num_realms == 0) 380 return 0; 381 for(i = 0; i < num_realms; i++){ 382 len += strlen(realms[i]); 383 if(realms[i][0] == '/') 384 len++; 385 } 386 len += num_realms - 1; 387 s = malloc(len + 1); 388 if (s == NULL) 389 return ENOMEM; 390 *s = '\0'; 391 for(i = 0; i < num_realms; i++){ 392 if(i) 393 strlcat(s, ",", len + 1); 394 if(realms[i][0] == '/') 395 strlcat(s, " ", len + 1); 396 strlcat(s, realms[i], len + 1); 397 } 398 encoding->data = s; 399 encoding->length = strlen(s); 400 return 0; 401 } 402 403 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 404 krb5_check_transited(krb5_context context, 405 krb5_const_realm client_realm, 406 krb5_const_realm server_realm, 407 krb5_realm *realms, 408 unsigned int num_realms, 409 int *bad_realm) 410 { 411 char **tr_realms; 412 char **p; 413 size_t i; 414 415 if(num_realms == 0) 416 return 0; 417 418 tr_realms = krb5_config_get_strings(context, NULL, 419 "capaths", 420 client_realm, 421 server_realm, 422 NULL); 423 for(i = 0; i < num_realms; i++) { 424 for(p = tr_realms; p && *p; p++) { 425 if(strcmp(*p, realms[i]) == 0) 426 break; 427 } 428 if(p == NULL || *p == NULL) { 429 krb5_config_free_strings(tr_realms); 430 krb5_set_error_message (context, KRB5KRB_AP_ERR_ILL_CR_TKT, 431 N_("no transit allowed " 432 "through realm %s", ""), 433 realms[i]); 434 if(bad_realm) 435 *bad_realm = i; 436 return KRB5KRB_AP_ERR_ILL_CR_TKT; 437 } 438 } 439 krb5_config_free_strings(tr_realms); 440 return 0; 441 } 442 443 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 444 krb5_check_transited_realms(krb5_context context, 445 const char *const *realms, 446 unsigned int num_realms, 447 int *bad_realm) 448 { 449 size_t i; 450 int ret = 0; 451 char **bad_realms = krb5_config_get_strings(context, NULL, 452 "libdefaults", 453 "transited_realms_reject", 454 NULL); 455 if(bad_realms == NULL) 456 return 0; 457 458 for(i = 0; i < num_realms; i++) { 459 char **p; 460 for(p = bad_realms; *p; p++) 461 if(strcmp(*p, realms[i]) == 0) { 462 ret = KRB5KRB_AP_ERR_ILL_CR_TKT; 463 krb5_set_error_message (context, ret, 464 N_("no transit allowed " 465 "through realm %s", ""), 466 *p); 467 if(bad_realm) 468 *bad_realm = i; 469 break; 470 } 471 } 472 krb5_config_free_strings(bad_realms); 473 return ret; 474 } 475 476 #if 0 477 int 478 main(int argc, char **argv) 479 { 480 krb5_data x; 481 char **r; 482 int num, i; 483 x.data = argv[1]; 484 x.length = strlen(x.data); 485 if(domain_expand(x, &r, &num, argv[2], argv[3])) 486 exit(1); 487 for(i = 0; i < num; i++) 488 printf("%s\n", r[i]); 489 return 0; 490 } 491 #endif 492 493