1 /* 2 * Copyright (c) 1997 - 2001, 2003 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <krb5_locl.h> 35 36 RCSID("$Id: rd_req.c,v 1.47.8.3 2003/10/21 20:10:33 lha Exp $"); 37 38 static krb5_error_code 39 decrypt_tkt_enc_part (krb5_context context, 40 krb5_keyblock *key, 41 EncryptedData *enc_part, 42 EncTicketPart *decr_part) 43 { 44 krb5_error_code ret; 45 krb5_data plain; 46 size_t len; 47 krb5_crypto crypto; 48 49 ret = krb5_crypto_init(context, key, 0, &crypto); 50 if (ret) 51 return ret; 52 ret = krb5_decrypt_EncryptedData (context, 53 crypto, 54 KRB5_KU_TICKET, 55 enc_part, 56 &plain); 57 krb5_crypto_destroy(context, crypto); 58 if (ret) 59 return ret; 60 61 ret = krb5_decode_EncTicketPart(context, plain.data, plain.length, 62 decr_part, &len); 63 krb5_data_free (&plain); 64 return ret; 65 } 66 67 static krb5_error_code 68 decrypt_authenticator (krb5_context context, 69 EncryptionKey *key, 70 EncryptedData *enc_part, 71 Authenticator *authenticator, 72 krb5_key_usage usage) 73 { 74 krb5_error_code ret; 75 krb5_data plain; 76 size_t len; 77 krb5_crypto crypto; 78 79 ret = krb5_crypto_init(context, key, 0, &crypto); 80 if (ret) 81 return ret; 82 ret = krb5_decrypt_EncryptedData (context, 83 crypto, 84 usage /* KRB5_KU_AP_REQ_AUTH */, 85 enc_part, 86 &plain); 87 /* for backwards compatibility, also try the old usage */ 88 if (ret && usage == KRB5_KU_TGS_REQ_AUTH) 89 ret = krb5_decrypt_EncryptedData (context, 90 crypto, 91 KRB5_KU_AP_REQ_AUTH, 92 enc_part, 93 &plain); 94 krb5_crypto_destroy(context, crypto); 95 if (ret) 96 return ret; 97 98 ret = krb5_decode_Authenticator(context, plain.data, plain.length, 99 authenticator, &len); 100 krb5_data_free (&plain); 101 return ret; 102 } 103 104 krb5_error_code 105 krb5_decode_ap_req(krb5_context context, 106 const krb5_data *inbuf, 107 krb5_ap_req *ap_req) 108 { 109 krb5_error_code ret; 110 size_t len; 111 ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len); 112 if (ret) 113 return ret; 114 if (ap_req->pvno != 5){ 115 free_AP_REQ(ap_req); 116 krb5_clear_error_string (context); 117 return KRB5KRB_AP_ERR_BADVERSION; 118 } 119 if (ap_req->msg_type != krb_ap_req){ 120 free_AP_REQ(ap_req); 121 krb5_clear_error_string (context); 122 return KRB5KRB_AP_ERR_MSG_TYPE; 123 } 124 if (ap_req->ticket.tkt_vno != 5){ 125 free_AP_REQ(ap_req); 126 krb5_clear_error_string (context); 127 return KRB5KRB_AP_ERR_BADVERSION; 128 } 129 return 0; 130 } 131 132 static krb5_error_code 133 check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc) 134 { 135 char **realms; 136 int num_realms; 137 krb5_error_code ret; 138 139 if(enc->transited.tr_type != DOMAIN_X500_COMPRESS) 140 return KRB5KDC_ERR_TRTYPE_NOSUPP; 141 142 if(enc->transited.contents.length == 0) 143 return 0; 144 145 ret = krb5_domain_x500_decode(context, enc->transited.contents, 146 &realms, &num_realms, 147 enc->crealm, 148 ticket->realm); 149 if(ret) 150 return ret; 151 ret = krb5_check_transited(context, enc->crealm, 152 ticket->realm, 153 realms, num_realms, NULL); 154 free(realms); 155 return ret; 156 } 157 158 krb5_error_code 159 krb5_decrypt_ticket(krb5_context context, 160 Ticket *ticket, 161 krb5_keyblock *key, 162 EncTicketPart *out, 163 krb5_flags flags) 164 { 165 EncTicketPart t; 166 krb5_error_code ret; 167 ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t); 168 if (ret) 169 return ret; 170 171 { 172 krb5_timestamp now; 173 time_t start = t.authtime; 174 175 krb5_timeofday (context, &now); 176 if(t.starttime) 177 start = *t.starttime; 178 if(start - now > context->max_skew 179 || (t.flags.invalid 180 && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) { 181 free_EncTicketPart(&t); 182 krb5_clear_error_string (context); 183 return KRB5KRB_AP_ERR_TKT_NYV; 184 } 185 if(now - t.endtime > context->max_skew) { 186 free_EncTicketPart(&t); 187 krb5_clear_error_string (context); 188 return KRB5KRB_AP_ERR_TKT_EXPIRED; 189 } 190 191 if(!t.flags.transited_policy_checked) { 192 ret = check_transited(context, ticket, &t); 193 if(ret) { 194 free_EncTicketPart(&t); 195 return ret; 196 } 197 } 198 } 199 200 if(out) 201 *out = t; 202 else 203 free_EncTicketPart(&t); 204 return 0; 205 } 206 207 krb5_error_code 208 krb5_verify_authenticator_checksum(krb5_context context, 209 krb5_auth_context ac, 210 void *data, 211 size_t len) 212 { 213 krb5_error_code ret; 214 krb5_keyblock *key; 215 krb5_authenticator authenticator; 216 krb5_crypto crypto; 217 218 ret = krb5_auth_con_getauthenticator (context, 219 ac, 220 &authenticator); 221 if(ret) 222 return ret; 223 if(authenticator->cksum == NULL) 224 return -17; 225 ret = krb5_auth_con_getkey(context, ac, &key); 226 if(ret) { 227 krb5_free_authenticator(context, &authenticator); 228 return ret; 229 } 230 ret = krb5_crypto_init(context, key, 0, &crypto); 231 if(ret) 232 goto out; 233 ret = krb5_verify_checksum (context, 234 crypto, 235 KRB5_KU_AP_REQ_AUTH_CKSUM, 236 data, 237 len, 238 authenticator->cksum); 239 krb5_crypto_destroy(context, crypto); 240 out: 241 krb5_free_authenticator(context, &authenticator); 242 krb5_free_keyblock(context, key); 243 return ret; 244 } 245 246 247 krb5_error_code 248 krb5_verify_ap_req(krb5_context context, 249 krb5_auth_context *auth_context, 250 krb5_ap_req *ap_req, 251 krb5_const_principal server, 252 krb5_keyblock *keyblock, 253 krb5_flags flags, 254 krb5_flags *ap_req_options, 255 krb5_ticket **ticket) 256 { 257 return krb5_verify_ap_req2 (context, 258 auth_context, 259 ap_req, 260 server, 261 keyblock, 262 flags, 263 ap_req_options, 264 ticket, 265 KRB5_KU_AP_REQ_AUTH); 266 } 267 268 krb5_error_code 269 krb5_verify_ap_req2(krb5_context context, 270 krb5_auth_context *auth_context, 271 krb5_ap_req *ap_req, 272 krb5_const_principal server, 273 krb5_keyblock *keyblock, 274 krb5_flags flags, 275 krb5_flags *ap_req_options, 276 krb5_ticket **ticket, 277 krb5_key_usage usage) 278 { 279 krb5_ticket t; 280 krb5_auth_context ac; 281 krb5_error_code ret; 282 283 if (auth_context && *auth_context) { 284 ac = *auth_context; 285 } else { 286 ret = krb5_auth_con_init (context, &ac); 287 if (ret) 288 return ret; 289 } 290 291 if (ap_req->ap_options.use_session_key && ac->keyblock){ 292 ret = krb5_decrypt_ticket(context, &ap_req->ticket, 293 ac->keyblock, 294 &t.ticket, 295 flags); 296 krb5_free_keyblock(context, ac->keyblock); 297 ac->keyblock = NULL; 298 }else 299 ret = krb5_decrypt_ticket(context, &ap_req->ticket, 300 keyblock, 301 &t.ticket, 302 flags); 303 304 if(ret) 305 goto out; 306 307 principalname2krb5_principal(&t.server, ap_req->ticket.sname, 308 ap_req->ticket.realm); 309 principalname2krb5_principal(&t.client, t.ticket.cname, 310 t.ticket.crealm); 311 312 /* save key */ 313 314 krb5_copy_keyblock(context, &t.ticket.key, &ac->keyblock); 315 316 ret = decrypt_authenticator (context, 317 &t.ticket.key, 318 &ap_req->authenticator, 319 ac->authenticator, 320 usage); 321 if (ret) 322 goto out2; 323 324 { 325 krb5_principal p1, p2; 326 krb5_boolean res; 327 328 principalname2krb5_principal(&p1, 329 ac->authenticator->cname, 330 ac->authenticator->crealm); 331 principalname2krb5_principal(&p2, 332 t.ticket.cname, 333 t.ticket.crealm); 334 res = krb5_principal_compare (context, p1, p2); 335 krb5_free_principal (context, p1); 336 krb5_free_principal (context, p2); 337 if (!res) { 338 ret = KRB5KRB_AP_ERR_BADMATCH; 339 krb5_clear_error_string (context); 340 goto out2; 341 } 342 } 343 344 /* check addresses */ 345 346 if (t.ticket.caddr 347 && ac->remote_address 348 && !krb5_address_search (context, 349 ac->remote_address, 350 t.ticket.caddr)) { 351 ret = KRB5KRB_AP_ERR_BADADDR; 352 krb5_clear_error_string (context); 353 goto out2; 354 } 355 356 if (ac->authenticator->seq_number) 357 krb5_auth_con_setremoteseqnumber(context, ac, 358 *ac->authenticator->seq_number); 359 360 /* XXX - Xor sequence numbers */ 361 362 if (ac->authenticator->subkey) { 363 ret = krb5_auth_con_setremotesubkey(context, ac, 364 ac->authenticator->subkey); 365 if (ret) 366 goto out2; 367 } 368 369 if (ap_req_options) { 370 *ap_req_options = 0; 371 if (ap_req->ap_options.use_session_key) 372 *ap_req_options |= AP_OPTS_USE_SESSION_KEY; 373 if (ap_req->ap_options.mutual_required) 374 *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED; 375 } 376 377 if(ticket){ 378 *ticket = malloc(sizeof(**ticket)); 379 **ticket = t; 380 } else 381 krb5_free_ticket (context, &t); 382 if (auth_context) { 383 if (*auth_context == NULL) 384 *auth_context = ac; 385 } else 386 krb5_auth_con_free (context, ac); 387 return 0; 388 out2: 389 krb5_free_ticket (context, &t); 390 out: 391 if (auth_context == NULL || *auth_context == NULL) 392 krb5_auth_con_free (context, ac); 393 return ret; 394 } 395 396 397 krb5_error_code 398 krb5_rd_req_with_keyblock(krb5_context context, 399 krb5_auth_context *auth_context, 400 const krb5_data *inbuf, 401 krb5_const_principal server, 402 krb5_keyblock *keyblock, 403 krb5_flags *ap_req_options, 404 krb5_ticket **ticket) 405 { 406 krb5_error_code ret; 407 krb5_ap_req ap_req; 408 409 if (*auth_context == NULL) { 410 ret = krb5_auth_con_init(context, auth_context); 411 if (ret) 412 return ret; 413 } 414 415 ret = krb5_decode_ap_req(context, inbuf, &ap_req); 416 if(ret) 417 return ret; 418 419 ret = krb5_verify_ap_req(context, 420 auth_context, 421 &ap_req, 422 server, 423 keyblock, 424 0, 425 ap_req_options, 426 ticket); 427 428 free_AP_REQ(&ap_req); 429 return ret; 430 } 431 432 static krb5_error_code 433 get_key_from_keytab(krb5_context context, 434 krb5_auth_context *auth_context, 435 krb5_ap_req *ap_req, 436 krb5_const_principal server, 437 krb5_keytab keytab, 438 krb5_keyblock **out_key) 439 { 440 krb5_keytab_entry entry; 441 krb5_error_code ret; 442 int kvno; 443 krb5_keytab real_keytab; 444 445 if(keytab == NULL) 446 krb5_kt_default(context, &real_keytab); 447 else 448 real_keytab = keytab; 449 450 if (ap_req->ticket.enc_part.kvno) 451 kvno = *ap_req->ticket.enc_part.kvno; 452 else 453 kvno = 0; 454 455 ret = krb5_kt_get_entry (context, 456 real_keytab, 457 server, 458 kvno, 459 ap_req->ticket.enc_part.etype, 460 &entry); 461 if(ret) 462 goto out; 463 ret = krb5_copy_keyblock(context, &entry.keyblock, out_key); 464 krb5_kt_free_entry (context, &entry); 465 out: 466 if(keytab == NULL) 467 krb5_kt_close(context, real_keytab); 468 469 return ret; 470 } 471 472 krb5_error_code 473 krb5_rd_req(krb5_context context, 474 krb5_auth_context *auth_context, 475 const krb5_data *inbuf, 476 krb5_const_principal server, 477 krb5_keytab keytab, 478 krb5_flags *ap_req_options, 479 krb5_ticket **ticket) 480 { 481 krb5_error_code ret; 482 krb5_ap_req ap_req; 483 krb5_keyblock *keyblock = NULL; 484 krb5_principal service = NULL; 485 486 if (*auth_context == NULL) { 487 ret = krb5_auth_con_init(context, auth_context); 488 if (ret) 489 return ret; 490 } 491 492 ret = krb5_decode_ap_req(context, inbuf, &ap_req); 493 if(ret) 494 return ret; 495 496 if(server == NULL){ 497 principalname2krb5_principal(&service, 498 ap_req.ticket.sname, 499 ap_req.ticket.realm); 500 server = service; 501 } 502 if (ap_req.ap_options.use_session_key && 503 (*auth_context)->keyblock == NULL) { 504 krb5_set_error_string(context, "krb5_rd_req: user to user auth " 505 "without session key given"); 506 ret = KRB5KRB_AP_ERR_NOKEY; 507 goto out; 508 } 509 510 if((*auth_context)->keyblock == NULL){ 511 ret = get_key_from_keytab(context, 512 auth_context, 513 &ap_req, 514 server, 515 keytab, 516 &keyblock); 517 if(ret) 518 goto out; 519 } else { 520 ret = krb5_copy_keyblock(context, 521 (*auth_context)->keyblock, 522 &keyblock); 523 if (ret) 524 goto out; 525 } 526 527 ret = krb5_verify_ap_req(context, 528 auth_context, 529 &ap_req, 530 server, 531 keyblock, 532 0, 533 ap_req_options, 534 ticket); 535 536 if(keyblock != NULL) 537 krb5_free_keyblock(context, keyblock); 538 539 out: 540 free_AP_REQ(&ap_req); 541 if(service) 542 krb5_free_principal(context, service); 543 return ret; 544 } 545