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.41 2000/02/07 13:31:55 joda 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 krb5_crypto_init(context, key, 0, &crypto); 50 ret = krb5_decrypt_EncryptedData (context, 51 crypto, 52 KRB5_KU_TICKET, 53 enc_part, 54 &plain); 55 krb5_crypto_destroy(context, crypto); 56 if (ret) 57 return ret; 58 59 ret = krb5_decode_EncTicketPart(context, plain.data, plain.length, 60 decr_part, &len); 61 krb5_data_free (&plain); 62 return ret; 63 } 64 65 static krb5_error_code 66 decrypt_authenticator (krb5_context context, 67 EncryptionKey *key, 68 EncryptedData *enc_part, 69 Authenticator *authenticator) 70 { 71 krb5_error_code ret; 72 krb5_data plain; 73 size_t len; 74 krb5_crypto crypto; 75 76 krb5_crypto_init(context, key, 0, &crypto); 77 ret = krb5_decrypt_EncryptedData (context, 78 crypto, 79 KRB5_KU_AP_REQ_AUTH, 80 enc_part, 81 &plain); 82 krb5_crypto_destroy(context, crypto); 83 if (ret) 84 return ret; 85 86 ret = krb5_decode_Authenticator(context, plain.data, plain.length, 87 authenticator, &len); 88 krb5_data_free (&plain); 89 return ret; 90 } 91 92 krb5_error_code 93 krb5_decode_ap_req(krb5_context context, 94 const krb5_data *inbuf, 95 krb5_ap_req *ap_req) 96 { 97 krb5_error_code ret; 98 size_t len; 99 ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len); 100 if (ret) 101 return ret; 102 if (ap_req->pvno != 5){ 103 free_AP_REQ(ap_req); 104 return KRB5KRB_AP_ERR_BADVERSION; 105 } 106 if (ap_req->msg_type != krb_ap_req){ 107 free_AP_REQ(ap_req); 108 return KRB5KRB_AP_ERR_MSG_TYPE; 109 } 110 if (ap_req->ticket.tkt_vno != 5){ 111 free_AP_REQ(ap_req); 112 return KRB5KRB_AP_ERR_BADVERSION; 113 } 114 return 0; 115 } 116 117 krb5_error_code 118 krb5_decrypt_ticket(krb5_context context, 119 Ticket *ticket, 120 krb5_keyblock *key, 121 EncTicketPart *out, 122 krb5_flags flags) 123 { 124 EncTicketPart t; 125 krb5_error_code ret; 126 ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t); 127 if (ret) 128 return ret; 129 130 { 131 krb5_timestamp now; 132 time_t start = t.authtime; 133 134 krb5_timeofday (context, &now); 135 if(t.starttime) 136 start = *t.starttime; 137 if(start - now > context->max_skew 138 || (t.flags.invalid 139 && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) 140 return KRB5KRB_AP_ERR_TKT_NYV; 141 if(now - t.endtime > context->max_skew) 142 return KRB5KRB_AP_ERR_TKT_EXPIRED; 143 } 144 145 if(out) 146 *out = t; 147 else 148 free_EncTicketPart(&t); 149 return 0; 150 } 151 152 krb5_error_code 153 krb5_verify_authenticator_checksum(krb5_context context, 154 krb5_auth_context ac, 155 void *data, 156 size_t len) 157 { 158 krb5_error_code ret; 159 krb5_keyblock *key; 160 krb5_authenticator authenticator; 161 krb5_crypto crypto; 162 163 ret = krb5_auth_getauthenticator (context, 164 ac, 165 &authenticator); 166 if(ret) 167 return ret; 168 if(authenticator->cksum == NULL) 169 return -17; 170 ret = krb5_auth_con_getkey(context, ac, &key); 171 if(ret) { 172 krb5_free_authenticator(context, &authenticator); 173 return ret; 174 } 175 ret = krb5_crypto_init(context, key, 0, &crypto); 176 if(ret) 177 goto out; 178 ret = krb5_verify_checksum (context, 179 crypto, 180 KRB5_KU_AP_REQ_AUTH_CKSUM, 181 data, 182 len, 183 authenticator->cksum); 184 krb5_crypto_destroy(context, crypto); 185 out: 186 krb5_free_authenticator(context, &authenticator); 187 krb5_free_keyblock(context, key); 188 return ret; 189 } 190 191 #if 0 192 static krb5_error_code 193 check_transited(krb5_context context, 194 krb5_ticket *ticket) 195 { 196 char **realms; 197 int num_realms; 198 krb5_error_code ret; 199 200 if(ticket->ticket.transited.tr_type != DOMAIN_X500_COMPRESS) 201 return KRB5KDC_ERR_TRTYPE_NOSUPP; 202 203 ret = krb5_domain_x500_decode(ticket->ticket.transited.contents, 204 &realms, &num_realms, 205 ticket->client->realm, 206 ticket->server->realm); 207 if(ret) 208 return ret; 209 ret = krb5_check_transited_realms(context, realms, num_realms, NULL); 210 free(realms); 211 return ret; 212 } 213 #endif 214 215 krb5_error_code 216 krb5_verify_ap_req(krb5_context context, 217 krb5_auth_context *auth_context, 218 krb5_ap_req *ap_req, 219 krb5_const_principal server, 220 krb5_keyblock *keyblock, 221 krb5_flags flags, 222 krb5_flags *ap_req_options, 223 krb5_ticket **ticket) 224 { 225 krb5_ticket t; 226 krb5_auth_context ac; 227 krb5_error_code ret; 228 229 if(auth_context) { 230 if(*auth_context == NULL){ 231 krb5_auth_con_init(context, &ac); 232 *auth_context = ac; 233 }else 234 ac = *auth_context; 235 } else 236 krb5_auth_con_init(context, &ac); 237 238 if (ap_req->ap_options.use_session_key && ac->keyblock){ 239 ret = krb5_decrypt_ticket(context, &ap_req->ticket, 240 ac->keyblock, 241 &t.ticket, 242 flags); 243 krb5_free_keyblock(context, ac->keyblock); 244 ac->keyblock = NULL; 245 }else 246 ret = krb5_decrypt_ticket(context, &ap_req->ticket, 247 keyblock, 248 &t.ticket, 249 flags); 250 251 if(ret) 252 return ret; 253 254 principalname2krb5_principal(&t.server, ap_req->ticket.sname, 255 ap_req->ticket.realm); 256 principalname2krb5_principal(&t.client, t.ticket.cname, 257 t.ticket.crealm); 258 259 /* save key */ 260 261 krb5_copy_keyblock(context, &t.ticket.key, &ac->keyblock); 262 263 ret = decrypt_authenticator (context, 264 &t.ticket.key, 265 &ap_req->authenticator, 266 ac->authenticator); 267 if (ret){ 268 /* XXX free data */ 269 return ret; 270 } 271 272 { 273 krb5_principal p1, p2; 274 krb5_boolean res; 275 276 principalname2krb5_principal(&p1, 277 ac->authenticator->cname, 278 ac->authenticator->crealm); 279 principalname2krb5_principal(&p2, 280 t.ticket.cname, 281 t.ticket.crealm); 282 res = krb5_principal_compare (context, p1, p2); 283 krb5_free_principal (context, p1); 284 krb5_free_principal (context, p2); 285 if (!res) 286 return KRB5KRB_AP_ERR_BADMATCH; 287 } 288 289 /* check addresses */ 290 291 if (t.ticket.caddr 292 && ac->remote_address 293 && !krb5_address_search (context, 294 ac->remote_address, 295 t.ticket.caddr)) 296 return KRB5KRB_AP_ERR_BADADDR; 297 298 if (ac->authenticator->seq_number) 299 ac->remote_seqnumber = *ac->authenticator->seq_number; 300 301 /* XXX - Xor sequence numbers */ 302 303 /* XXX - subkeys? */ 304 /* And where should it be stored? */ 305 306 if (ac->authenticator->subkey) { 307 krb5_copy_keyblock(context, 308 ac->authenticator->subkey, 309 &ac->remote_subkey); 310 } 311 312 if (ap_req_options) { 313 *ap_req_options = 0; 314 if (ap_req->ap_options.use_session_key) 315 *ap_req_options |= AP_OPTS_USE_SESSION_KEY; 316 if (ap_req->ap_options.mutual_required) 317 *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED; 318 } 319 320 if(ticket){ 321 *ticket = malloc(sizeof(**ticket)); 322 **ticket = t; 323 } else 324 krb5_free_ticket (context, &t); 325 return 0; 326 } 327 328 329 krb5_error_code 330 krb5_rd_req_with_keyblock(krb5_context context, 331 krb5_auth_context *auth_context, 332 const krb5_data *inbuf, 333 krb5_const_principal server, 334 krb5_keyblock *keyblock, 335 krb5_flags *ap_req_options, 336 krb5_ticket **ticket) 337 { 338 krb5_error_code ret; 339 krb5_ap_req ap_req; 340 341 if (*auth_context == NULL) { 342 ret = krb5_auth_con_init(context, auth_context); 343 if (ret) 344 return ret; 345 } 346 347 ret = krb5_decode_ap_req(context, inbuf, &ap_req); 348 if(ret) 349 return ret; 350 351 ret = krb5_verify_ap_req(context, 352 auth_context, 353 &ap_req, 354 server, 355 keyblock, 356 0, 357 ap_req_options, 358 ticket); 359 360 free_AP_REQ(&ap_req); 361 return ret; 362 } 363 364 static krb5_error_code 365 get_key_from_keytab(krb5_context context, 366 krb5_auth_context *auth_context, 367 krb5_ap_req *ap_req, 368 krb5_const_principal server, 369 krb5_keytab keytab, 370 krb5_keyblock **out_key) 371 { 372 krb5_keytab_entry entry; 373 krb5_error_code ret; 374 int kvno; 375 krb5_keytab real_keytab; 376 377 if(keytab == NULL) 378 krb5_kt_default(context, &real_keytab); 379 else 380 real_keytab = keytab; 381 382 if (ap_req->ticket.enc_part.kvno) 383 kvno = *ap_req->ticket.enc_part.kvno; 384 else 385 kvno = 0; 386 387 ret = krb5_kt_get_entry (context, 388 real_keytab, 389 server, 390 kvno, 391 ap_req->ticket.enc_part.etype, 392 &entry); 393 if(ret) 394 goto out; 395 ret = krb5_copy_keyblock(context, &entry.keyblock, out_key); 396 krb5_kt_free_entry (context, &entry); 397 out: 398 if(keytab == NULL) 399 krb5_kt_close(context, real_keytab); 400 401 return ret; 402 } 403 404 krb5_error_code 405 krb5_rd_req(krb5_context context, 406 krb5_auth_context *auth_context, 407 const krb5_data *inbuf, 408 krb5_const_principal server, 409 krb5_keytab keytab, 410 krb5_flags *ap_req_options, 411 krb5_ticket **ticket) 412 { 413 krb5_error_code ret; 414 krb5_ap_req ap_req; 415 krb5_keyblock *keyblock = NULL; 416 krb5_principal service = NULL; 417 418 if (*auth_context == NULL) { 419 ret = krb5_auth_con_init(context, auth_context); 420 if (ret) 421 return ret; 422 } 423 424 ret = krb5_decode_ap_req(context, inbuf, &ap_req); 425 if(ret) 426 return ret; 427 428 if(server == NULL){ 429 principalname2krb5_principal(&service, 430 ap_req.ticket.sname, 431 ap_req.ticket.realm); 432 server = service; 433 } 434 435 if(ap_req.ap_options.use_session_key == 0 || 436 (*auth_context)->keyblock == NULL){ 437 ret = get_key_from_keytab(context, 438 auth_context, 439 &ap_req, 440 server, 441 keytab, 442 &keyblock); 443 if(ret) 444 goto out; 445 } 446 447 448 ret = krb5_verify_ap_req(context, 449 auth_context, 450 &ap_req, 451 server, 452 keyblock, 453 0, 454 ap_req_options, 455 ticket); 456 457 if(keyblock != NULL) 458 krb5_free_keyblock(context, keyblock); 459 460 out: 461 free_AP_REQ(&ap_req); 462 if(service) 463 krb5_free_principal(context, service); 464 return ret; 465 } 466