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