1 /* 2 * Copyright (c) 1997 - 2001 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 2001/06/18 02:48:18 assar 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 krb5_error_code 133 krb5_decrypt_ticket(krb5_context context, 134 Ticket *ticket, 135 krb5_keyblock *key, 136 EncTicketPart *out, 137 krb5_flags flags) 138 { 139 EncTicketPart t; 140 krb5_error_code ret; 141 ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t); 142 if (ret) 143 return ret; 144 145 { 146 krb5_timestamp now; 147 time_t start = t.authtime; 148 149 krb5_timeofday (context, &now); 150 if(t.starttime) 151 start = *t.starttime; 152 if(start - now > context->max_skew 153 || (t.flags.invalid 154 && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) { 155 free_EncTicketPart(&t); 156 krb5_clear_error_string (context); 157 return KRB5KRB_AP_ERR_TKT_NYV; 158 } 159 if(now - t.endtime > context->max_skew) { 160 free_EncTicketPart(&t); 161 krb5_clear_error_string (context); 162 return KRB5KRB_AP_ERR_TKT_EXPIRED; 163 } 164 } 165 166 if(out) 167 *out = t; 168 else 169 free_EncTicketPart(&t); 170 return 0; 171 } 172 173 krb5_error_code 174 krb5_verify_authenticator_checksum(krb5_context context, 175 krb5_auth_context ac, 176 void *data, 177 size_t len) 178 { 179 krb5_error_code ret; 180 krb5_keyblock *key; 181 krb5_authenticator authenticator; 182 krb5_crypto crypto; 183 184 ret = krb5_auth_con_getauthenticator (context, 185 ac, 186 &authenticator); 187 if(ret) 188 return ret; 189 if(authenticator->cksum == NULL) 190 return -17; 191 ret = krb5_auth_con_getkey(context, ac, &key); 192 if(ret) { 193 krb5_free_authenticator(context, &authenticator); 194 return ret; 195 } 196 ret = krb5_crypto_init(context, key, 0, &crypto); 197 if(ret) 198 goto out; 199 ret = krb5_verify_checksum (context, 200 crypto, 201 KRB5_KU_AP_REQ_AUTH_CKSUM, 202 data, 203 len, 204 authenticator->cksum); 205 krb5_crypto_destroy(context, crypto); 206 out: 207 krb5_free_authenticator(context, &authenticator); 208 krb5_free_keyblock(context, key); 209 return ret; 210 } 211 212 #if 0 213 static krb5_error_code 214 check_transited(krb5_context context, 215 krb5_ticket *ticket) 216 { 217 char **realms; 218 int num_realms; 219 krb5_error_code ret; 220 221 if(ticket->ticket.transited.tr_type != DOMAIN_X500_COMPRESS) 222 return KRB5KDC_ERR_TRTYPE_NOSUPP; 223 224 ret = krb5_domain_x500_decode(ticket->ticket.transited.contents, 225 &realms, &num_realms, 226 ticket->client->realm, 227 ticket->server->realm); 228 if(ret) 229 return ret; 230 ret = krb5_check_transited_realms(context, realms, num_realms, NULL); 231 free(realms); 232 return ret; 233 } 234 #endif 235 236 krb5_error_code 237 krb5_verify_ap_req(krb5_context context, 238 krb5_auth_context *auth_context, 239 krb5_ap_req *ap_req, 240 krb5_const_principal server, 241 krb5_keyblock *keyblock, 242 krb5_flags flags, 243 krb5_flags *ap_req_options, 244 krb5_ticket **ticket) 245 { 246 return krb5_verify_ap_req2 (context, 247 auth_context, 248 ap_req, 249 server, 250 keyblock, 251 flags, 252 ap_req_options, 253 ticket, 254 KRB5_KU_AP_REQ_AUTH); 255 } 256 257 krb5_error_code 258 krb5_verify_ap_req2(krb5_context context, 259 krb5_auth_context *auth_context, 260 krb5_ap_req *ap_req, 261 krb5_const_principal server, 262 krb5_keyblock *keyblock, 263 krb5_flags flags, 264 krb5_flags *ap_req_options, 265 krb5_ticket **ticket, 266 krb5_key_usage usage) 267 { 268 krb5_ticket t; 269 krb5_auth_context ac; 270 krb5_error_code ret; 271 272 if (auth_context && *auth_context) { 273 ac = *auth_context; 274 } else { 275 ret = krb5_auth_con_init (context, &ac); 276 if (ret) 277 return ret; 278 } 279 280 if (ap_req->ap_options.use_session_key && ac->keyblock){ 281 ret = krb5_decrypt_ticket(context, &ap_req->ticket, 282 ac->keyblock, 283 &t.ticket, 284 flags); 285 krb5_free_keyblock(context, ac->keyblock); 286 ac->keyblock = NULL; 287 }else 288 ret = krb5_decrypt_ticket(context, &ap_req->ticket, 289 keyblock, 290 &t.ticket, 291 flags); 292 293 if(ret) 294 goto out; 295 296 principalname2krb5_principal(&t.server, ap_req->ticket.sname, 297 ap_req->ticket.realm); 298 principalname2krb5_principal(&t.client, t.ticket.cname, 299 t.ticket.crealm); 300 301 /* save key */ 302 303 krb5_copy_keyblock(context, &t.ticket.key, &ac->keyblock); 304 305 ret = decrypt_authenticator (context, 306 &t.ticket.key, 307 &ap_req->authenticator, 308 ac->authenticator, 309 usage); 310 if (ret) 311 goto out2; 312 313 { 314 krb5_principal p1, p2; 315 krb5_boolean res; 316 317 principalname2krb5_principal(&p1, 318 ac->authenticator->cname, 319 ac->authenticator->crealm); 320 principalname2krb5_principal(&p2, 321 t.ticket.cname, 322 t.ticket.crealm); 323 res = krb5_principal_compare (context, p1, p2); 324 krb5_free_principal (context, p1); 325 krb5_free_principal (context, p2); 326 if (!res) { 327 ret = KRB5KRB_AP_ERR_BADMATCH; 328 krb5_clear_error_string (context); 329 goto out2; 330 } 331 } 332 333 /* check addresses */ 334 335 if (t.ticket.caddr 336 && ac->remote_address 337 && !krb5_address_search (context, 338 ac->remote_address, 339 t.ticket.caddr)) { 340 ret = KRB5KRB_AP_ERR_BADADDR; 341 krb5_clear_error_string (context); 342 goto out2; 343 } 344 345 if (ac->authenticator->seq_number) 346 krb5_auth_con_setremoteseqnumber(context, ac, 347 *ac->authenticator->seq_number); 348 349 /* XXX - Xor sequence numbers */ 350 351 if (ac->authenticator->subkey) { 352 ret = krb5_auth_con_setremotesubkey(context, ac, 353 ac->authenticator->subkey); 354 if (ret) 355 goto out2; 356 } 357 358 if (ap_req_options) { 359 *ap_req_options = 0; 360 if (ap_req->ap_options.use_session_key) 361 *ap_req_options |= AP_OPTS_USE_SESSION_KEY; 362 if (ap_req->ap_options.mutual_required) 363 *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED; 364 } 365 366 if(ticket){ 367 *ticket = malloc(sizeof(**ticket)); 368 **ticket = t; 369 } else 370 krb5_free_ticket (context, &t); 371 if (auth_context) { 372 if (*auth_context == NULL) 373 *auth_context = ac; 374 } else 375 krb5_auth_con_free (context, ac); 376 return 0; 377 out2: 378 krb5_free_ticket (context, &t); 379 out: 380 if (auth_context == NULL || *auth_context == NULL) 381 krb5_auth_con_free (context, ac); 382 return ret; 383 } 384 385 386 krb5_error_code 387 krb5_rd_req_with_keyblock(krb5_context context, 388 krb5_auth_context *auth_context, 389 const krb5_data *inbuf, 390 krb5_const_principal server, 391 krb5_keyblock *keyblock, 392 krb5_flags *ap_req_options, 393 krb5_ticket **ticket) 394 { 395 krb5_error_code ret; 396 krb5_ap_req ap_req; 397 398 if (*auth_context == NULL) { 399 ret = krb5_auth_con_init(context, auth_context); 400 if (ret) 401 return ret; 402 } 403 404 ret = krb5_decode_ap_req(context, inbuf, &ap_req); 405 if(ret) 406 return ret; 407 408 ret = krb5_verify_ap_req(context, 409 auth_context, 410 &ap_req, 411 server, 412 keyblock, 413 0, 414 ap_req_options, 415 ticket); 416 417 free_AP_REQ(&ap_req); 418 return ret; 419 } 420 421 static krb5_error_code 422 get_key_from_keytab(krb5_context context, 423 krb5_auth_context *auth_context, 424 krb5_ap_req *ap_req, 425 krb5_const_principal server, 426 krb5_keytab keytab, 427 krb5_keyblock **out_key) 428 { 429 krb5_keytab_entry entry; 430 krb5_error_code ret; 431 int kvno; 432 krb5_keytab real_keytab; 433 434 if(keytab == NULL) 435 krb5_kt_default(context, &real_keytab); 436 else 437 real_keytab = keytab; 438 439 if (ap_req->ticket.enc_part.kvno) 440 kvno = *ap_req->ticket.enc_part.kvno; 441 else 442 kvno = 0; 443 444 ret = krb5_kt_get_entry (context, 445 real_keytab, 446 server, 447 kvno, 448 ap_req->ticket.enc_part.etype, 449 &entry); 450 if(ret) 451 goto out; 452 ret = krb5_copy_keyblock(context, &entry.keyblock, out_key); 453 krb5_kt_free_entry (context, &entry); 454 out: 455 if(keytab == NULL) 456 krb5_kt_close(context, real_keytab); 457 458 return ret; 459 } 460 461 krb5_error_code 462 krb5_rd_req(krb5_context context, 463 krb5_auth_context *auth_context, 464 const krb5_data *inbuf, 465 krb5_const_principal server, 466 krb5_keytab keytab, 467 krb5_flags *ap_req_options, 468 krb5_ticket **ticket) 469 { 470 krb5_error_code ret; 471 krb5_ap_req ap_req; 472 krb5_keyblock *keyblock = NULL; 473 krb5_principal service = NULL; 474 475 if (*auth_context == NULL) { 476 ret = krb5_auth_con_init(context, auth_context); 477 if (ret) 478 return ret; 479 } 480 481 ret = krb5_decode_ap_req(context, inbuf, &ap_req); 482 if(ret) 483 return ret; 484 485 if(server == NULL){ 486 principalname2krb5_principal(&service, 487 ap_req.ticket.sname, 488 ap_req.ticket.realm); 489 server = service; 490 } 491 492 if(ap_req.ap_options.use_session_key == 0 || 493 (*auth_context)->keyblock == NULL){ 494 ret = get_key_from_keytab(context, 495 auth_context, 496 &ap_req, 497 server, 498 keytab, 499 &keyblock); 500 if(ret) 501 goto out; 502 } 503 504 505 ret = krb5_verify_ap_req(context, 506 auth_context, 507 &ap_req, 508 server, 509 keyblock, 510 0, 511 ap_req_options, 512 ticket); 513 514 if(keyblock != NULL) 515 krb5_free_keyblock(context, keyblock); 516 517 out: 518 free_AP_REQ(&ap_req); 519 if(service) 520 krb5_free_principal(context, service); 521 return ret; 522 } 523