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