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