1 /* 2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * lib/krb5/krb/rd_req_dec.c 6 * 7 * Copyright (c) 1994 CyberSAFE Corporation. 8 * Copyright 1990,1991 by the Massachusetts Institute of Technology. 9 * All Rights Reserved. 10 * 11 * Export of this software from the United States of America may 12 * require a specific license from the United States Government. 13 * It is the responsibility of any person or organization contemplating 14 * export to obtain such a license before exporting. 15 * 16 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 17 * distribute this software and its documentation for any purpose and 18 * without fee is hereby granted, provided that the above copyright 19 * notice appear in all copies and that both that copyright notice and 20 * this permission notice appear in supporting documentation, and that 21 * the name of M.I.T. not be used in advertising or publicity pertaining 22 * to distribution of the software without specific, written prior 23 * permission. Furthermore if you modify this software you must label 24 * your software as modified software and not distribute it in such a 25 * fashion that it might be confused with the original M.I.T. software. 26 * Neither M.I.T., the Open Computing Security Group, nor 27 * CyberSAFE Corporation make any representations about the suitability of 28 * this software for any purpose. It is provided "as is" without express 29 * or implied warranty. 30 * 31 * 32 * krb5_rd_req_decoded() 33 */ 34 35 #include "k5-int.h" 36 #include "auth_con.h" 37 #include <locale.h> 38 #include <syslog.h> 39 40 /* 41 * essentially the same as krb_rd_req, but uses a decoded AP_REQ as 42 * the input rather than an encoded input. 43 */ 44 /* 45 * Parses a KRB_AP_REQ message, returning its contents. 46 * 47 * server specifies the expected server's name for the ticket; if NULL, then 48 * any server will be accepted if the key can be found, and the caller should 49 * verify that the principal is something it trusts. 50 * 51 * rcache specifies a replay detection cache used to store authenticators and 52 * server names 53 * 54 * keyproc specifies a procedure to generate a decryption key for the 55 * ticket. If keyproc is non-NULL, keyprocarg is passed to it, and the result 56 * used as a decryption key. If keyproc is NULL, then fetchfrom is checked; 57 * if it is non-NULL, it specifies a parameter name from which to retrieve the 58 * decryption key. If fetchfrom is NULL, then the default key store is 59 * consulted. 60 * 61 * authdat is set to point at allocated storage structures; the caller 62 * should free them when finished. 63 * 64 * returns system errors, encryption errors, replay errors 65 */ 66 67 static krb5_error_code decrypt_authenticator 68 (krb5_context, const krb5_ap_req *, krb5_authenticator **, 69 int); 70 71 72 static krb5_error_code 73 krb5_rd_req_decrypt_tkt_part(krb5_context context, const krb5_ap_req *req, krb5_keytab keytab) 74 { 75 krb5_error_code retval; 76 krb5_enctype enctype; 77 krb5_keytab_entry ktent; 78 79 enctype = req->ticket->enc_part.enctype; 80 81 /* Solaris Kerberos: */ 82 memset(&ktent, 0, sizeof(krb5_keytab_entry)); 83 if ((retval = krb5_kt_get_entry(context, keytab, req->ticket->server, 84 req->ticket->enc_part.kvno, 85 enctype, &ktent))) 86 return retval; 87 88 89 /* 90 * Solaris Kerberos: 91 * If we get this far then we know that the enc types are similar, 92 * therefore we should change the enc type to match that of what 93 * we are decrypting. 94 */ 95 ktent.key.enctype = enctype; 96 97 retval = krb5_decrypt_tkt_part(context, &ktent.key, req->ticket); 98 /* Upon error, Free keytab entry first, then return */ 99 100 if (retval == KRB5KRB_AP_ERR_BAD_INTEGRITY) { 101 /* Solaris Kerberos: spruce-up the err msg */ 102 krb5_principal princ = (krb5_principal) req->ticket->server; 103 char *s_name = NULL; 104 int kret = krb5_unparse_name(context, princ, &s_name); 105 if (kret == 0) { 106 krb5_set_error_message(context, retval, 107 dgettext(TEXT_DOMAIN, 108 "AP Request ticket decrypt fail for principal '%s' (kvno=%d, enctype=%d)"), 109 s_name, 110 req->ticket->enc_part.kvno, 111 enctype); 112 krb5_free_unparsed_name(context, s_name); 113 } 114 } 115 116 (void) krb5_kt_free_entry(context, &ktent); 117 return retval; 118 } 119 120 /* 121 * Solaris Kerberos 122 * Same as krb5int_check_clockskew() plus return the skew in seconds. 123 */ 124 static krb5_error_code 125 krb5int_check_clockskew2(krb5_context context, 126 krb5_timestamp date, 127 krb5_timestamp *ret_skew) 128 { 129 krb5_timestamp currenttime, skew; 130 krb5_error_code retval; 131 132 retval = krb5_timeofday(context, ¤ttime); 133 if (retval) 134 return retval; 135 136 skew = labs((date)-currenttime); 137 if (!(skew < context->clockskew)) { 138 *ret_skew = skew; 139 return KRB5KRB_AP_ERR_SKEW; 140 } 141 142 return 0; 143 } 144 145 static krb5_error_code 146 krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context, 147 const krb5_ap_req *req, krb5_const_principal server, 148 krb5_keytab keytab, krb5_flags *ap_req_options, 149 krb5_ticket **ticket, int check_valid_flag) 150 { 151 krb5_error_code retval = 0; 152 krb5_principal_data princ_data; 153 krb5_timestamp skew = 0; /* Solaris Kerberos */ 154 155 req->ticket->enc_part2 == NULL; 156 if (server && krb5_is_referral_realm(&server->realm)) { 157 char *realm; 158 princ_data = *server; 159 server = &princ_data; 160 retval = krb5_get_default_realm(context, &realm); 161 if (retval) 162 return retval; 163 princ_data.realm.data = realm; 164 princ_data.realm.length = strlen(realm); 165 } 166 if (server && !krb5_principal_compare(context, server, req->ticket->server)) { 167 char *found_name = 0, *wanted_name = 0; 168 if (krb5_unparse_name(context, server, &wanted_name) == 0 169 && krb5_unparse_name(context, req->ticket->server, &found_name) == 0) 170 krb5_set_error_message(context, KRB5KRB_AP_WRONG_PRINC, 171 dgettext(TEXT_DOMAIN, 172 "Wrong principal in request (found %s, wanted %s)"), 173 found_name, wanted_name); 174 krb5_free_unparsed_name(context, wanted_name); 175 krb5_free_unparsed_name(context, found_name); 176 retval = KRB5KRB_AP_WRONG_PRINC; 177 goto cleanup; 178 } 179 180 /* if (req->ap_options & AP_OPTS_USE_SESSION_KEY) 181 do we need special processing here ? */ 182 183 /* decrypt the ticket */ 184 if ((*auth_context)->keyblock) { /* User to User authentication */ 185 if ((retval = krb5_decrypt_tkt_part(context, (*auth_context)->keyblock, 186 req->ticket))) 187 goto cleanup; 188 krb5_free_keyblock(context, (*auth_context)->keyblock); 189 (*auth_context)->keyblock = NULL; 190 } else { 191 if ((retval = krb5_rd_req_decrypt_tkt_part(context, req, keytab))) 192 goto cleanup; 193 } 194 195 /* XXX this is an evil hack. check_valid_flag is set iff the call 196 is not from inside the kdc. we can use this to determine which 197 key usage to use */ 198 if ((retval = decrypt_authenticator(context, req, 199 &((*auth_context)->authentp), 200 check_valid_flag))) 201 goto cleanup; 202 203 if (!krb5_principal_compare(context, (*auth_context)->authentp->client, 204 req->ticket->enc_part2->client)) { 205 retval = KRB5KRB_AP_ERR_BADMATCH; 206 goto cleanup; 207 } 208 209 if ((*auth_context)->remote_addr && 210 !krb5_address_search(context, (*auth_context)->remote_addr, 211 req->ticket->enc_part2->caddrs)) { 212 retval = KRB5KRB_AP_ERR_BADADDR; 213 goto cleanup; 214 } 215 216 /* okay, now check cross-realm policy */ 217 218 #if defined(_SINGLE_HOP_ONLY) 219 220 /* Single hop cross-realm tickets only */ 221 222 { 223 krb5_transited *trans = &(req->ticket->enc_part2->transited); 224 225 /* If the transited list is empty, then we have at most one hop */ 226 if (trans->tr_contents.data && trans->tr_contents.data[0]) 227 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 228 } 229 230 #elif defined(_NO_CROSS_REALM) 231 232 /* No cross-realm tickets */ 233 234 { 235 char * lrealm; 236 krb5_data * realm; 237 krb5_transited * trans; 238 239 realm = krb5_princ_realm(context, req->ticket->enc_part2->client); 240 trans = &(req->ticket->enc_part2->transited); 241 242 /* 243 * If the transited list is empty, then we have at most one hop 244 * So we also have to check that the client's realm is the local one 245 */ 246 krb5_get_default_realm(context, &lrealm); 247 if ((trans->tr_contents.data && trans->tr_contents.data[0]) || 248 strlen(lrealm) != realm->length || 249 memcmp(lrealm, realm->data, strlen(lrealm))) { 250 retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 251 } 252 free(lrealm); 253 } 254 255 #else 256 257 /* Hierarchical Cross-Realm */ 258 259 { 260 krb5_data * realm; 261 krb5_transited * trans; 262 263 realm = krb5_princ_realm(context, req->ticket->enc_part2->client); 264 trans = &(req->ticket->enc_part2->transited); 265 266 /* 267 * If the transited list is not empty, then check that all realms 268 * transited are within the hierarchy between the client's realm 269 * and the local realm. 270 */ 271 if (trans->tr_contents.data && trans->tr_contents.data[0]) { 272 retval = krb5_check_transited_list(context, &(trans->tr_contents), 273 realm, 274 krb5_princ_realm (context, 275 server)); 276 } 277 } 278 279 #endif 280 281 if (retval) goto cleanup; 282 283 /* only check rcache if sender has provided one---some services 284 may not be able to use replay caches (such as datagram servers) */ 285 286 if ((*auth_context)->rcache) { 287 krb5_donot_replay rep; 288 krb5_tkt_authent tktauthent; 289 290 tktauthent.ticket = req->ticket; 291 tktauthent.authenticator = (*auth_context)->authentp; 292 if (!(retval = krb5_auth_to_rep(context, &tktauthent, &rep))) { 293 retval = krb5_rc_store(context, (*auth_context)->rcache, &rep); 294 krb5_xfree(rep.server); 295 krb5_xfree(rep.client); 296 } 297 298 if (retval == KRB5KRB_AP_ERR_SKEW) 299 goto err_skew; 300 301 if (retval) 302 goto cleanup; 303 } 304 305 retval = krb5_validate_times(context, &req->ticket->enc_part2->times); 306 if (retval != 0) 307 goto cleanup; 308 309 err_skew: 310 if ((retval = krb5int_check_clockskew2(context, 311 (*auth_context)->authentp->ctime, 312 &skew))) { 313 /* Solaris Kerberos */ 314 char *s_name = NULL; 315 char *c_name = NULL; 316 krb5_error_code serr, cerr; 317 serr = krb5_unparse_name(context, req->ticket->server, &s_name); 318 cerr = krb5_unparse_name(context, req->ticket->enc_part2->client, 319 &c_name); 320 krb5_set_error_message(context, retval, 321 dgettext(TEXT_DOMAIN, 322 "Clock skew too great: client '%s' AP request with ticket for '%s'. Skew is %dm (allowable %dm)."), 323 cerr == 0 ? c_name : "unknown", 324 serr == 0 ? s_name : "unknown", 325 skew > 0 ? skew/60 : 0, 326 context->clockskew > 0 ? context->clockskew/60 : 0); 327 if (s_name) 328 krb5_free_unparsed_name(context, s_name); 329 if (c_name) 330 krb5_free_unparsed_name(context, c_name); 331 goto cleanup; 332 } 333 334 if (check_valid_flag) { 335 if (req->ticket->enc_part2->flags & TKT_FLG_INVALID) { 336 /* Solaris Kerberos */ 337 char *s_name = NULL; 338 int err = krb5_unparse_name(context, req->ticket->server, &s_name); 339 retval = KRB5KRB_AP_ERR_TKT_INVALID; 340 if (!err) { 341 krb5_set_error_message(context, retval, 342 dgettext(TEXT_DOMAIN, 343 "Ticket has invalid flag set for server '%s'"), 344 s_name); 345 krb5_free_unparsed_name(context, s_name); 346 } 347 goto cleanup; 348 } 349 } 350 351 /* check if the various etypes are permitted */ 352 353 if ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_PERMIT_ALL) { 354 /* no etype check needed */ 355 /*EMPTY*/ 356 ; 357 } else if ((*auth_context)->permitted_etypes == NULL) { 358 int etype; 359 /* check against the default set */ 360 if ((!krb5_is_permitted_enctype(context, 361 etype = req->ticket->enc_part.enctype)) || 362 (!krb5_is_permitted_enctype(context, 363 etype = req->ticket->enc_part2->session->enctype)) || 364 (((*auth_context)->authentp->subkey) && 365 !krb5_is_permitted_enctype(context, 366 etype = (*auth_context)->authentp->subkey->enctype))) { 367 char enctype_name[30]; 368 retval = KRB5_NOPERM_ETYPE; 369 if (krb5_enctype_to_string(etype, enctype_name, sizeof(enctype_name)) == 0) 370 krb5_set_error_message(context, retval, 371 dgettext(TEXT_DOMAIN, 372 "Encryption type %s not permitted"), 373 enctype_name); 374 goto cleanup; 375 } 376 } else { 377 /* check against the set in the auth_context */ 378 int i; 379 380 for (i=0; (*auth_context)->permitted_etypes[i]; i++) 381 if ((*auth_context)->permitted_etypes[i] == 382 req->ticket->enc_part.enctype) 383 break; 384 if (!(*auth_context)->permitted_etypes[i]) { 385 char enctype_name[30]; 386 retval = KRB5_NOPERM_ETYPE; 387 if (krb5_enctype_to_string(req->ticket->enc_part.enctype, 388 enctype_name, sizeof(enctype_name)) == 0) 389 krb5_set_error_message(context, retval, 390 dgettext(TEXT_DOMAIN, 391 "Encryption type %s not permitted"), 392 enctype_name); 393 goto cleanup; 394 } 395 396 for (i=0; (*auth_context)->permitted_etypes[i]; i++) 397 if ((*auth_context)->permitted_etypes[i] == 398 req->ticket->enc_part2->session->enctype) 399 break; 400 if (!(*auth_context)->permitted_etypes[i]) { 401 char enctype_name[30]; 402 retval = KRB5_NOPERM_ETYPE; 403 if (krb5_enctype_to_string(req->ticket->enc_part2->session->enctype, 404 enctype_name, sizeof(enctype_name)) == 0) 405 krb5_set_error_message(context, retval, 406 dgettext(TEXT_DOMAIN, 407 "Encryption type %s not permitted"), 408 enctype_name); 409 goto cleanup; 410 } 411 412 if ((*auth_context)->authentp->subkey) { 413 for (i=0; (*auth_context)->permitted_etypes[i]; i++) 414 if ((*auth_context)->permitted_etypes[i] == 415 (*auth_context)->authentp->subkey->enctype) 416 break; 417 if (!(*auth_context)->permitted_etypes[i]) { 418 char enctype_name[30]; 419 retval = KRB5_NOPERM_ETYPE; 420 if (krb5_enctype_to_string((*auth_context)->authentp->subkey->enctype, 421 enctype_name, 422 sizeof(enctype_name)) == 0) 423 krb5_set_error_message(context, retval, 424 dgettext(TEXT_DOMAIN, 425 "Encryption type %s not permitted"), 426 enctype_name); 427 goto cleanup; 428 } 429 } 430 } 431 432 (*auth_context)->remote_seq_number = (*auth_context)->authentp->seq_number; 433 if ((*auth_context)->authentp->subkey) { 434 /* Solaris Kerberos */ 435 if ((*auth_context)->recv_subkey != NULL) { 436 krb5_free_keyblock(context, (*auth_context)->recv_subkey); 437 (*auth_context)->recv_subkey = NULL; 438 } 439 440 if ((retval = krb5_copy_keyblock(context, 441 (*auth_context)->authentp->subkey, 442 &((*auth_context)->recv_subkey)))) 443 goto cleanup; 444 /* Solaris Kerberos */ 445 if ((*auth_context)->send_subkey != NULL) { 446 krb5_free_keyblock(context, (*auth_context)->send_subkey); 447 (*auth_context)->send_subkey = NULL; 448 } 449 450 retval = krb5_copy_keyblock(context, (*auth_context)->authentp->subkey, 451 &((*auth_context)->send_subkey)); 452 if (retval) { 453 krb5_free_keyblock(context, (*auth_context)->recv_subkey); 454 (*auth_context)->recv_subkey = NULL; 455 goto cleanup; 456 } 457 } else { 458 (*auth_context)->recv_subkey = 0; 459 (*auth_context)->send_subkey = 0; 460 } 461 /* Solaris Kerberos */ 462 if ((*auth_context)->keyblock != NULL) { 463 krb5_free_keyblock(context, (*auth_context)->keyblock); 464 (*auth_context)->keyblock = NULL; 465 } 466 if ((retval = krb5_copy_keyblock(context, req->ticket->enc_part2->session, 467 &((*auth_context)->keyblock)))) 468 goto cleanup; 469 470 /* 471 * If not AP_OPTS_MUTUAL_REQUIRED then and sequence numbers are used 472 * then the default sequence number is the one's complement of the 473 * sequence number sent ot us. 474 */ 475 if ((!(req->ap_options & AP_OPTS_MUTUAL_REQUIRED)) && 476 (*auth_context)->remote_seq_number) { 477 (*auth_context)->local_seq_number ^= 478 (*auth_context)->remote_seq_number; 479 } 480 481 if (ticket) 482 if ((retval = krb5_copy_ticket(context, req->ticket, ticket))) 483 goto cleanup; 484 if (ap_req_options) 485 *ap_req_options = req->ap_options; 486 retval = 0; 487 488 cleanup: 489 if (server == &princ_data) 490 krb5_free_default_realm(context, princ_data.realm.data); 491 if (retval) { 492 /* only free if we're erroring out...otherwise some 493 applications will need the output. */ 494 if (req->ticket->enc_part2) 495 krb5_free_enc_tkt_part(context, req->ticket->enc_part2); 496 req->ticket->enc_part2 = NULL; 497 } 498 return retval; 499 } 500 501 krb5_error_code 502 krb5_rd_req_decoded(krb5_context context, krb5_auth_context *auth_context, 503 const krb5_ap_req *req, krb5_const_principal server, 504 krb5_keytab keytab, krb5_flags *ap_req_options, 505 krb5_ticket **ticket) 506 { 507 krb5_error_code retval; 508 retval = krb5_rd_req_decoded_opt(context, auth_context, 509 req, server, keytab, 510 ap_req_options, ticket, 511 1); /* check_valid_flag */ 512 return retval; 513 } 514 515 krb5_error_code 516 krb5_rd_req_decoded_anyflag(krb5_context context, 517 krb5_auth_context *auth_context, 518 const krb5_ap_req *req, 519 krb5_const_principal server, krb5_keytab keytab, 520 krb5_flags *ap_req_options, krb5_ticket **ticket) 521 { 522 krb5_error_code retval; 523 retval = krb5_rd_req_decoded_opt(context, auth_context, 524 req, server, keytab, 525 ap_req_options, ticket, 526 0); /* don't check_valid_flag */ 527 return retval; 528 } 529 530 /*ARGSUSED*/ 531 static krb5_error_code 532 decrypt_authenticator(krb5_context context, const krb5_ap_req *request, 533 krb5_authenticator **authpp, int is_ap_req) 534 { 535 krb5_authenticator *local_auth; 536 krb5_error_code retval; 537 krb5_data scratch; 538 krb5_keyblock *sesskey; 539 540 sesskey = request->ticket->enc_part2->session; 541 542 scratch.length = request->authenticator.ciphertext.length; 543 if (!(scratch.data = malloc(scratch.length))) 544 return(ENOMEM); 545 546 if ((retval = krb5_c_decrypt(context, sesskey, 547 is_ap_req?KRB5_KEYUSAGE_AP_REQ_AUTH: 548 KRB5_KEYUSAGE_TGS_REQ_AUTH, 0, 549 &request->authenticator, &scratch))) { 550 free(scratch.data); 551 return(retval); 552 } 553 554 #define clean_scratch() {memset(scratch.data, 0, scratch.length); \ 555 free(scratch.data);} 556 557 /* now decode the decrypted stuff */ 558 if (!(retval = decode_krb5_authenticator(&scratch, &local_auth))) { 559 *authpp = local_auth; 560 } 561 clean_scratch(); 562 return retval; 563 } 564 565 krb5_error_code 566 krb5int_check_clockskew(krb5_context context, krb5_timestamp date) 567 { 568 krb5_timestamp currenttime; 569 krb5_error_code retval; 570 571 retval = krb5_timeofday(context, ¤ttime); 572 if (retval) 573 return retval; 574 if (!(labs((date)-currenttime) < context->clockskew)) 575 return KRB5KRB_AP_ERR_SKEW; 576 return 0; 577 } 578