Home | History | Annotate | Line # | Download | only in krb5
      1 /*	$NetBSD: rd_req.c,v 1.3 2023/06/19 21:41:44 christos Exp $	*/
      2 
      3 
      4 /*
      5  * Copyright (c) 1997 - 2007 Kungliga Tekniska Hgskolan
      6  * (Royal Institute of Technology, Stockholm, Sweden).
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  *
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  *
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  *
     20  * 3. Neither the name of the Institute nor the names of its contributors
     21  *    may be used to endorse or promote products derived from this software
     22  *    without specific prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  */
     36 
     37 #include "krb5_locl.h"
     38 
     39 static krb5_error_code
     40 decrypt_tkt_enc_part (krb5_context context,
     41 		      krb5_keyblock *key,
     42 		      EncryptedData *enc_part,
     43 		      EncTicketPart *decr_part)
     44 {
     45     krb5_error_code ret;
     46     krb5_data plain;
     47     size_t len;
     48     krb5_crypto crypto;
     49 
     50     ret = krb5_crypto_init(context, key, 0, &crypto);
     51     if (ret)
     52 	return ret;
     53     ret = krb5_decrypt_EncryptedData (context,
     54 				      crypto,
     55 				      KRB5_KU_TICKET,
     56 				      enc_part,
     57 				      &plain);
     58     krb5_crypto_destroy(context, crypto);
     59     if (ret)
     60 	return ret;
     61 
     62     ret = decode_EncTicketPart(plain.data, plain.length, decr_part, &len);
     63     if (ret)
     64         krb5_set_error_message(context, ret,
     65 			       N_("Failed to decode encrypted "
     66 				  "ticket part", ""));
     67     krb5_data_free (&plain);
     68     return ret;
     69 }
     70 
     71 static krb5_error_code
     72 decrypt_authenticator (krb5_context context,
     73 		       EncryptionKey *key,
     74 		       EncryptedData *enc_part,
     75 		       Authenticator *authenticator,
     76 		       krb5_key_usage usage)
     77 {
     78     krb5_error_code ret;
     79     krb5_data plain;
     80     size_t len;
     81     krb5_crypto crypto;
     82 
     83     ret = krb5_crypto_init(context, key, 0, &crypto);
     84     if (ret)
     85 	return ret;
     86     ret = krb5_decrypt_EncryptedData (context,
     87 				      crypto,
     88 				      usage /* KRB5_KU_AP_REQ_AUTH */,
     89 				      enc_part,
     90 				      &plain);
     91     /* for backwards compatibility, also try the old usage */
     92     if (ret && usage == KRB5_KU_TGS_REQ_AUTH)
     93 	ret = krb5_decrypt_EncryptedData (context,
     94 					  crypto,
     95 					  KRB5_KU_AP_REQ_AUTH,
     96 					  enc_part,
     97 					  &plain);
     98     krb5_crypto_destroy(context, crypto);
     99     if (ret)
    100 	return ret;
    101 
    102     ret = decode_Authenticator(plain.data, plain.length,
    103 			       authenticator, &len);
    104     krb5_data_free (&plain);
    105     return ret;
    106 }
    107 
    108 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    109 krb5_decode_ap_req(krb5_context context,
    110 		   const krb5_data *inbuf,
    111 		   krb5_ap_req *ap_req)
    112 {
    113     krb5_error_code ret;
    114     size_t len;
    115     ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len);
    116     if (ret)
    117 	return ret;
    118     if (ap_req->pvno != 5){
    119 	free_AP_REQ(ap_req);
    120 	krb5_clear_error_message (context);
    121 	return KRB5KRB_AP_ERR_BADVERSION;
    122     }
    123     if (ap_req->msg_type != krb_ap_req){
    124 	free_AP_REQ(ap_req);
    125 	krb5_clear_error_message (context);
    126 	return KRB5KRB_AP_ERR_MSG_TYPE;
    127     }
    128     if (ap_req->ticket.tkt_vno != 5){
    129 	free_AP_REQ(ap_req);
    130 	krb5_clear_error_message (context);
    131 	return KRB5KRB_AP_ERR_BADVERSION;
    132     }
    133     return 0;
    134 }
    135 
    136 static krb5_error_code
    137 check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
    138 {
    139     char **realms;
    140     unsigned int num_realms, n;
    141     krb5_error_code ret;
    142 
    143     /*
    144      * Windows 2000 and 2003 uses this inside their TGT so it's normaly
    145      * not seen by others, however, samba4 joined with a Windows AD as
    146      * a Domain Controller gets exposed to this.
    147      */
    148     if(enc->transited.tr_type == 0 && enc->transited.contents.length == 0)
    149 	return 0;
    150 
    151     if(enc->transited.tr_type != DOMAIN_X500_COMPRESS)
    152 	return KRB5KDC_ERR_TRTYPE_NOSUPP;
    153 
    154     if(enc->transited.contents.length == 0)
    155 	return 0;
    156 
    157     ret = krb5_domain_x500_decode(context, enc->transited.contents,
    158 				  &realms, &num_realms,
    159 				  enc->crealm,
    160 				  ticket->realm);
    161     if(ret)
    162 	return ret;
    163     ret = krb5_check_transited(context, enc->crealm,
    164 			       ticket->realm,
    165 			       realms, num_realms, NULL);
    166     for (n = 0; n < num_realms; n++)
    167 	free(realms[n]);
    168     free(realms);
    169     return ret;
    170 }
    171 
    172 static krb5_error_code
    173 find_etypelist(krb5_context context,
    174 	       krb5_auth_context auth_context,
    175 	       EtypeList *etypes)
    176 {
    177     krb5_error_code ret;
    178     krb5_data data;
    179 
    180     ret = _krb5_get_ad(context, auth_context->authenticator->authorization_data, NULL, KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION, &data);
    181     if (ret)
    182   	return 0;
    183 
    184     ret = decode_EtypeList(data.data, data.length, etypes, NULL);
    185     krb5_data_free(&data);
    186     if (ret)
    187   	krb5_clear_error_message(context);
    188 
    189     return ret;
    190 }
    191 
    192 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    193 krb5_decrypt_ticket(krb5_context context,
    194 		    Ticket *ticket,
    195 		    krb5_keyblock *key,
    196 		    EncTicketPart *out,
    197 		    krb5_flags flags)
    198 {
    199     EncTicketPart t;
    200     krb5_error_code ret;
    201     ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t);
    202     if (ret)
    203 	return ret;
    204 
    205     {
    206 	krb5_timestamp now;
    207 	time_t start = t.authtime;
    208 
    209 	krb5_timeofday (context, &now);
    210 	if(t.starttime)
    211 	    start = *t.starttime;
    212 	if(start - now > context->max_skew
    213 	   || (t.flags.invalid
    214 	       && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) {
    215 	    free_EncTicketPart(&t);
    216 	    krb5_clear_error_message (context);
    217 	    return KRB5KRB_AP_ERR_TKT_NYV;
    218 	}
    219 	if(now - t.endtime > context->max_skew) {
    220 	    free_EncTicketPart(&t);
    221 	    krb5_clear_error_message (context);
    222 	    return KRB5KRB_AP_ERR_TKT_EXPIRED;
    223 	}
    224 
    225 	if(!t.flags.transited_policy_checked) {
    226 	    ret = check_transited(context, ticket, &t);
    227 	    if(ret) {
    228 		free_EncTicketPart(&t);
    229 		return ret;
    230 	    }
    231 	}
    232     }
    233 
    234     if(out)
    235 	*out = t;
    236     else
    237 	free_EncTicketPart(&t);
    238     return 0;
    239 }
    240 
    241 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    242 krb5_verify_authenticator_checksum(krb5_context context,
    243 				   krb5_auth_context ac,
    244 				   void *data,
    245 				   size_t len)
    246 {
    247     krb5_error_code ret;
    248     krb5_keyblock *key = NULL;
    249     krb5_authenticator authenticator;
    250     krb5_crypto crypto;
    251 
    252     ret = krb5_auth_con_getauthenticator(context, ac, &authenticator);
    253     if (ret)
    254 	return ret;
    255     if (authenticator->cksum == NULL) {
    256 	ret = -17;
    257         goto out;
    258     }
    259     ret = krb5_auth_con_getkey(context, ac, &key);
    260     if (ret)
    261         goto out;
    262     ret = krb5_crypto_init(context, key, 0, &crypto);
    263     if (ret)
    264 	goto out;
    265     ret = krb5_verify_checksum(context, crypto,
    266                                KRB5_KU_AP_REQ_AUTH_CKSUM,
    267                                data, len, authenticator->cksum);
    268     krb5_crypto_destroy(context, crypto);
    269 out:
    270     krb5_free_authenticator(context, &authenticator);
    271     krb5_free_keyblock(context, key);
    272     return ret;
    273 }
    274 
    275 
    276 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    277 krb5_verify_ap_req(krb5_context context,
    278 		   krb5_auth_context *auth_context,
    279 		   krb5_ap_req *ap_req,
    280 		   krb5_const_principal server,
    281 		   krb5_keyblock *keyblock,
    282 		   krb5_flags flags,
    283 		   krb5_flags *ap_req_options,
    284 		   krb5_ticket **ticket)
    285 {
    286     return krb5_verify_ap_req2 (context,
    287 				auth_context,
    288 				ap_req,
    289 				server,
    290 				keyblock,
    291 				flags,
    292 				ap_req_options,
    293 				ticket,
    294 				KRB5_KU_AP_REQ_AUTH);
    295 }
    296 
    297 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    298 krb5_verify_ap_req2(krb5_context context,
    299 		    krb5_auth_context *auth_context,
    300 		    krb5_ap_req *ap_req,
    301 		    krb5_const_principal server,
    302 		    krb5_keyblock *keyblock,
    303 		    krb5_flags flags,
    304 		    krb5_flags *ap_req_options,
    305 		    krb5_ticket **ticket,
    306 		    krb5_key_usage usage)
    307 {
    308     krb5_ticket *t;
    309     krb5_auth_context ac;
    310     krb5_error_code ret;
    311     EtypeList etypes;
    312 
    313     memset(&etypes, 0, sizeof(etypes));
    314 
    315     if (ticket)
    316 	*ticket = NULL;
    317 
    318     if (auth_context && *auth_context) {
    319 	ac = *auth_context;
    320     } else {
    321 	ret = krb5_auth_con_init (context, &ac);
    322 	if (ret)
    323 	    return ret;
    324     }
    325 
    326     t = calloc(1, sizeof(*t));
    327     if (t == NULL) {
    328 	ret = krb5_enomem(context);
    329 	goto out;
    330     }
    331 
    332     if (ap_req->ap_options.use_session_key && ac->keyblock){
    333 	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
    334 				  ac->keyblock,
    335 				  &t->ticket,
    336 				  flags);
    337 	krb5_free_keyblock(context, ac->keyblock);
    338 	ac->keyblock = NULL;
    339     }else
    340 	ret = krb5_decrypt_ticket(context, &ap_req->ticket,
    341 				  keyblock,
    342 				  &t->ticket,
    343 				  flags);
    344 
    345     if(ret)
    346 	goto out;
    347 
    348     ret = _krb5_principalname2krb5_principal(context,
    349 					     &t->server,
    350 					     ap_req->ticket.sname,
    351 					     ap_req->ticket.realm);
    352     if (ret) goto out;
    353     ret = _krb5_principalname2krb5_principal(context,
    354 					     &t->client,
    355 					     t->ticket.cname,
    356 					     t->ticket.crealm);
    357     if (ret) goto out;
    358 
    359     ret = decrypt_authenticator (context,
    360 				 &t->ticket.key,
    361 				 &ap_req->authenticator,
    362 				 ac->authenticator,
    363 				 usage);
    364     if (ret)
    365 	goto out;
    366 
    367     {
    368 	krb5_principal p1, p2;
    369 	krb5_boolean res;
    370 
    371 	_krb5_principalname2krb5_principal(context,
    372 					   &p1,
    373 					   ac->authenticator->cname,
    374 					   ac->authenticator->crealm);
    375 	_krb5_principalname2krb5_principal(context,
    376 					   &p2,
    377 					   t->ticket.cname,
    378 					   t->ticket.crealm);
    379 	res = krb5_principal_compare (context, p1, p2);
    380 	krb5_free_principal (context, p1);
    381 	krb5_free_principal (context, p2);
    382 	if (!res) {
    383 	    ret = KRB5KRB_AP_ERR_BADMATCH;
    384 	    krb5_clear_error_message (context);
    385 	    goto out;
    386 	}
    387     }
    388 
    389     /* check addresses */
    390 
    391     if (t->ticket.caddr
    392 	&& ac->remote_address
    393 	&& !krb5_address_search (context,
    394 				 ac->remote_address,
    395 				 t->ticket.caddr)) {
    396 	ret = KRB5KRB_AP_ERR_BADADDR;
    397 	krb5_clear_error_message (context);
    398 	goto out;
    399     }
    400 
    401     /* check timestamp in authenticator */
    402     {
    403 	krb5_timestamp now;
    404 
    405 	krb5_timeofday (context, &now);
    406 
    407 	if (labs(ac->authenticator->ctime - now) > context->max_skew) {
    408 	    ret = KRB5KRB_AP_ERR_SKEW;
    409 	    krb5_clear_error_message (context);
    410 	    goto out;
    411 	}
    412     }
    413 
    414     if (ac->authenticator->seq_number)
    415 	krb5_auth_con_setremoteseqnumber(context, ac,
    416 					 *ac->authenticator->seq_number);
    417 
    418     /* XXX - Xor sequence numbers */
    419 
    420     if (ac->authenticator->subkey) {
    421 	ret = krb5_auth_con_setremotesubkey(context, ac,
    422 					    ac->authenticator->subkey);
    423 	if (ret)
    424 	    goto out;
    425     }
    426 
    427     ret = find_etypelist(context, ac, &etypes);
    428     if (ret)
    429 	goto out;
    430 
    431     ac->keytype = ETYPE_NULL;
    432 
    433     if (etypes.val) {
    434 	size_t i;
    435 
    436 	for (i = 0; i < etypes.len; i++) {
    437 	    if (krb5_enctype_valid(context, etypes.val[i]) == 0) {
    438 		ac->keytype = etypes.val[i];
    439 		break;
    440 	    }
    441 	}
    442     }
    443 
    444     /* save key */
    445     ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock);
    446     if (ret) goto out;
    447 
    448     if (ap_req_options) {
    449 	*ap_req_options = 0;
    450 	if (ac->keytype != (krb5_enctype)ETYPE_NULL)
    451 	    *ap_req_options |= AP_OPTS_USE_SUBKEY;
    452 	if (ap_req->ap_options.use_session_key)
    453 	    *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
    454 	if (ap_req->ap_options.mutual_required)
    455 	    *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
    456     }
    457 
    458     if(ticket)
    459 	*ticket = t;
    460     else
    461 	krb5_free_ticket (context, t);
    462     if (auth_context) {
    463 	if (*auth_context == NULL)
    464 	    *auth_context = ac;
    465     } else
    466 	krb5_auth_con_free (context, ac);
    467     free_EtypeList(&etypes);
    468     return 0;
    469  out:
    470     free_EtypeList(&etypes);
    471     if (t)
    472 	krb5_free_ticket (context, t);
    473     if (auth_context == NULL || *auth_context == NULL)
    474 	krb5_auth_con_free (context, ac);
    475     return ret;
    476 }
    477 
    478 /*
    479  *
    480  */
    481 
    482 struct krb5_rd_req_in_ctx_data {
    483     krb5_keytab keytab;
    484     krb5_keyblock *keyblock;
    485     krb5_boolean check_pac;
    486 };
    487 
    488 struct krb5_rd_req_out_ctx_data {
    489     krb5_keyblock *keyblock;
    490     krb5_flags ap_req_options;
    491     krb5_ticket *ticket;
    492     krb5_principal server;
    493 };
    494 
    495 /**
    496  * Allocate a krb5_rd_req_in_ctx as an input parameter to
    497  * krb5_rd_req_ctx(). The caller should free the context with
    498  * krb5_rd_req_in_ctx_free() when done with the context.
    499  *
    500  * @param context Keberos 5 context.
    501  * @param ctx in ctx to krb5_rd_req_ctx().
    502  *
    503  * @return Kerberos 5 error code, see krb5_get_error_message().
    504  *
    505  * @ingroup krb5_auth
    506  */
    507 
    508 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    509 krb5_rd_req_in_ctx_alloc(krb5_context context, krb5_rd_req_in_ctx *ctx)
    510 {
    511     *ctx = calloc(1, sizeof(**ctx));
    512     if (*ctx == NULL)
    513 	return krb5_enomem(context);
    514     (*ctx)->check_pac = (context->flags & KRB5_CTX_F_CHECK_PAC) ? 1 : 0;
    515     return 0;
    516 }
    517 
    518 /**
    519  * Set the keytab that krb5_rd_req_ctx() will use.
    520  *
    521  * @param context Keberos 5 context.
    522  * @param in in ctx to krb5_rd_req_ctx().
    523  * @param keytab keytab that krb5_rd_req_ctx() will use, only copy the
    524  *        pointer, so the caller must free they keytab after
    525  *        krb5_rd_req_in_ctx_free() is called.
    526  *
    527  * @return Kerberos 5 error code, see krb5_get_error_message().
    528  *
    529  * @ingroup krb5_auth
    530  */
    531 
    532 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    533 krb5_rd_req_in_set_keytab(krb5_context context,
    534 			  krb5_rd_req_in_ctx in,
    535 			  krb5_keytab keytab)
    536 {
    537     in->keytab = keytab;
    538     return 0;
    539 }
    540 
    541 /**
    542  * Set if krb5_rq_red() is going to check the Windows PAC or not
    543  *
    544  * @param context Keberos 5 context.
    545  * @param in krb5_rd_req_in_ctx to check the option on.
    546  * @param flag flag to select if to check the pac (TRUE) or not (FALSE).
    547  *
    548  * @return Kerberos 5 error code, see krb5_get_error_message().
    549  *
    550  * @ingroup krb5_auth
    551  */
    552 
    553 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    554 krb5_rd_req_in_set_pac_check(krb5_context context,
    555 			     krb5_rd_req_in_ctx in,
    556 			     krb5_boolean flag)
    557 {
    558     in->check_pac = flag;
    559     return 0;
    560 }
    561 
    562 
    563 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    564 krb5_rd_req_in_set_keyblock(krb5_context context,
    565 			    krb5_rd_req_in_ctx in,
    566 			    krb5_keyblock *keyblock)
    567 {
    568     in->keyblock = keyblock; /* XXX should make copy */
    569     return 0;
    570 }
    571 
    572 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    573 krb5_rd_req_out_get_ap_req_options(krb5_context context,
    574 				   krb5_rd_req_out_ctx out,
    575 				   krb5_flags *ap_req_options)
    576 {
    577     *ap_req_options = out->ap_req_options;
    578     return 0;
    579 }
    580 
    581 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    582 krb5_rd_req_out_get_ticket(krb5_context context,
    583 			    krb5_rd_req_out_ctx out,
    584 			    krb5_ticket **ticket)
    585 {
    586     return krb5_copy_ticket(context, out->ticket, ticket);
    587 }
    588 
    589 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    590 krb5_rd_req_out_get_keyblock(krb5_context context,
    591 			    krb5_rd_req_out_ctx out,
    592 			    krb5_keyblock **keyblock)
    593 {
    594     return krb5_copy_keyblock(context, out->keyblock, keyblock);
    595 }
    596 
    597 /**
    598  * Get the principal that was used in the request from the
    599  * client. Might not match whats in the ticket if krb5_rd_req_ctx()
    600  * searched in the keytab for a matching key.
    601  *
    602  * @param context a Kerberos 5 context.
    603  * @param out a krb5_rd_req_out_ctx from krb5_rd_req_ctx().
    604  * @param principal return principal, free with krb5_free_principal().
    605  *
    606  * @ingroup krb5_auth
    607  */
    608 
    609 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    610 krb5_rd_req_out_get_server(krb5_context context,
    611 			    krb5_rd_req_out_ctx out,
    612 			    krb5_principal *principal)
    613 {
    614     return krb5_copy_principal(context, out->server, principal);
    615 }
    616 
    617 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
    618 krb5_rd_req_in_ctx_free(krb5_context context, krb5_rd_req_in_ctx ctx)
    619 {
    620     free(ctx);
    621 }
    622 
    623 /**
    624  * Free the krb5_rd_req_out_ctx.
    625  *
    626  * @param context Keberos 5 context.
    627  * @param ctx krb5_rd_req_out_ctx context to free.
    628  *
    629  * @ingroup krb5_auth
    630  */
    631 
    632 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
    633 krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx)
    634 {
    635     if (ctx->ticket)
    636 	krb5_free_ticket(context, ctx->ticket);
    637     if (ctx->keyblock)
    638 	krb5_free_keyblock(context, ctx->keyblock);
    639     if (ctx->server)
    640 	krb5_free_principal(context, ctx->server);
    641     free(ctx);
    642 }
    643 
    644 /**
    645  * Process an AP_REQ message.
    646  *
    647  * @param context        Kerberos 5 context.
    648  * @param auth_context   authentication context of the peer.
    649  * @param inbuf          the AP_REQ message, obtained for example with krb5_read_message().
    650  * @param server         server principal.
    651  * @param keytab         server keytab.
    652  * @param ap_req_options set to the AP_REQ options. See the AP_OPTS_* defines.
    653  * @param ticket         on success, set to the authenticated client credentials.
    654  *                       Must be deallocated with krb5_free_ticket(). If not
    655  *                       interested, pass a NULL value.
    656  *
    657  * @return 0 to indicate success. Otherwise a Kerberos error code is
    658  *         returned, see krb5_get_error_message().
    659  */
    660 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    661 krb5_rd_req(krb5_context context,
    662 	    krb5_auth_context *auth_context,
    663 	    const krb5_data *inbuf,
    664 	    krb5_const_principal server,
    665 	    krb5_keytab keytab,
    666 	    krb5_flags *ap_req_options,
    667 	    krb5_ticket **ticket)
    668 {
    669     krb5_error_code ret;
    670     krb5_rd_req_in_ctx in;
    671     krb5_rd_req_out_ctx out;
    672 
    673     ret = krb5_rd_req_in_ctx_alloc(context, &in);
    674     if (ret)
    675 	return ret;
    676 
    677     ret = krb5_rd_req_in_set_keytab(context, in, keytab);
    678     if (ret) {
    679 	krb5_rd_req_in_ctx_free(context, in);
    680 	return ret;
    681     }
    682 
    683     ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
    684     krb5_rd_req_in_ctx_free(context, in);
    685     if (ret)
    686 	return ret;
    687 
    688     if (ap_req_options)
    689 	*ap_req_options = out->ap_req_options;
    690     if (ticket) {
    691 	ret = krb5_copy_ticket(context, out->ticket, ticket);
    692 	if (ret)
    693 	    goto out;
    694     }
    695 
    696 out:
    697     krb5_rd_req_out_ctx_free(context, out);
    698     return ret;
    699 }
    700 
    701 /*
    702  *
    703  */
    704 
    705 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    706 krb5_rd_req_with_keyblock(krb5_context context,
    707 			  krb5_auth_context *auth_context,
    708 			  const krb5_data *inbuf,
    709 			  krb5_const_principal server,
    710 			  krb5_keyblock *keyblock,
    711 			  krb5_flags *ap_req_options,
    712 			  krb5_ticket **ticket)
    713 {
    714     krb5_error_code ret;
    715     krb5_rd_req_in_ctx in;
    716     krb5_rd_req_out_ctx out;
    717 
    718     ret = krb5_rd_req_in_ctx_alloc(context, &in);
    719     if (ret)
    720 	return ret;
    721 
    722     ret = krb5_rd_req_in_set_keyblock(context, in, keyblock);
    723     if (ret) {
    724 	krb5_rd_req_in_ctx_free(context, in);
    725 	return ret;
    726     }
    727 
    728     ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
    729     krb5_rd_req_in_ctx_free(context, in);
    730     if (ret)
    731 	return ret;
    732 
    733     if (ap_req_options)
    734 	*ap_req_options = out->ap_req_options;
    735     if (ticket) {
    736 	ret = krb5_copy_ticket(context, out->ticket, ticket);
    737 	if (ret)
    738 	    goto out;
    739     }
    740 
    741 out:
    742     krb5_rd_req_out_ctx_free(context, out);
    743     return ret;
    744 }
    745 
    746 /*
    747  *
    748  */
    749 
    750 static krb5_error_code
    751 get_key_from_keytab(krb5_context context,
    752 		    krb5_ap_req *ap_req,
    753 		    krb5_const_principal server,
    754 		    krb5_keytab keytab,
    755 		    krb5_keyblock **out_key)
    756 {
    757     krb5_keytab_entry entry;
    758     krb5_error_code ret;
    759     int kvno;
    760     krb5_keytab real_keytab;
    761 
    762     if(keytab == NULL)
    763 	krb5_kt_default(context, &real_keytab);
    764     else
    765 	real_keytab = keytab;
    766 
    767     if (ap_req->ticket.enc_part.kvno)
    768 	kvno = *ap_req->ticket.enc_part.kvno;
    769     else
    770 	kvno = 0;
    771 
    772     ret = krb5_kt_get_entry (context,
    773 			     real_keytab,
    774 			     server,
    775 			     kvno,
    776 			     ap_req->ticket.enc_part.etype,
    777 			     &entry);
    778     if(ret == 0) {
    779         ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
    780         krb5_kt_free_entry(context, &entry);
    781     }
    782     if(keytab == NULL)
    783 	krb5_kt_close(context, real_keytab);
    784 
    785     return ret;
    786 }
    787 
    788 /**
    789  * The core server function that verify application authentication
    790  * requests from clients.
    791  *
    792  * @param context Keberos 5 context.
    793  * @param auth_context the authentication context, can be NULL, then
    794  *        default values for the authentication context will used.
    795  * @param inbuf the (AP-REQ) authentication buffer
    796  *
    797  * @param server the server to authenticate to. If NULL the function
    798  *        will try to find any available credential in the keytab
    799  *        that will verify the reply. The function will prefer the
    800  *        server specified in the AP-REQ, but if
    801  *        there is no mach, it will try all keytab entries for a
    802  *        match. This has serious performance issues for large keytabs.
    803  *
    804  * @param inctx control the behavior of the function, if NULL, the
    805  *        default behavior is used.
    806  * @param outctx the return outctx, free with krb5_rd_req_out_ctx_free().
    807  * @return Kerberos 5 error code, see krb5_get_error_message().
    808  *
    809  * @ingroup krb5_auth
    810  */
    811 
    812 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    813 krb5_rd_req_ctx(krb5_context context,
    814 		krb5_auth_context *auth_context,
    815 		const krb5_data *inbuf,
    816 		krb5_const_principal server,
    817 		krb5_rd_req_in_ctx inctx,
    818 		krb5_rd_req_out_ctx *outctx)
    819 {
    820     krb5_error_code ret;
    821     krb5_ap_req ap_req;
    822     krb5_rd_req_out_ctx o = NULL;
    823     krb5_keytab id = NULL, keytab = NULL;
    824     krb5_principal service = NULL;
    825 
    826     *outctx = NULL;
    827 
    828     o = calloc(1, sizeof(*o));
    829     if (o == NULL)
    830 	return krb5_enomem(context);
    831 
    832     if (*auth_context == NULL) {
    833 	ret = krb5_auth_con_init(context, auth_context);
    834 	if (ret)
    835 	    goto out;
    836     }
    837 
    838     ret = krb5_decode_ap_req(context, inbuf, &ap_req);
    839     if(ret)
    840 	goto out;
    841 
    842     /* Save the principal that was in the request */
    843     ret = _krb5_principalname2krb5_principal(context,
    844 					     &o->server,
    845 					     ap_req.ticket.sname,
    846 					     ap_req.ticket.realm);
    847     if (ret)
    848 	goto out;
    849 
    850     if (ap_req.ap_options.use_session_key &&
    851 	(*auth_context)->keyblock == NULL) {
    852 	ret = KRB5KRB_AP_ERR_NOKEY;
    853 	krb5_set_error_message(context, ret,
    854 			       N_("krb5_rd_req: user to user auth "
    855 				  "without session key given", ""));
    856 	goto out;
    857     }
    858 
    859     if (inctx && inctx->keytab)
    860 	id = inctx->keytab;
    861 
    862     if((*auth_context)->keyblock){
    863 	ret = krb5_copy_keyblock(context,
    864 				 (*auth_context)->keyblock,
    865 				 &o->keyblock);
    866 	if (ret)
    867 	    goto out;
    868     } else if(inctx && inctx->keyblock){
    869 	ret = krb5_copy_keyblock(context,
    870 				 inctx->keyblock,
    871 				 &o->keyblock);
    872 	if (ret)
    873 	    goto out;
    874     } else {
    875 
    876 	if(id == NULL) {
    877 	    krb5_kt_default(context, &keytab);
    878 	    id = keytab;
    879 	}
    880 	if (id == NULL)
    881 	    goto out;
    882 
    883 	if (server == NULL) {
    884 	    ret = _krb5_principalname2krb5_principal(context,
    885 						     &service,
    886 						     ap_req.ticket.sname,
    887 						     ap_req.ticket.realm);
    888 	    if (ret)
    889 		goto out;
    890 	    server = service;
    891 	}
    892 
    893 	ret = get_key_from_keytab(context,
    894 				  &ap_req,
    895 				  server,
    896 				  id,
    897 				  &o->keyblock);
    898 	if (ret) {
    899 	    /* If caller specified a server, fail. */
    900 	    if (service == NULL && (context->flags & KRB5_CTX_F_RD_REQ_IGNORE) == 0)
    901 		goto out;
    902 	    /* Otherwise, fall back to iterating over the keytab. This
    903 	     * have serious performace issues for larger keytab.
    904 	     */
    905 	    o->keyblock = NULL;
    906 	}
    907     }
    908 
    909     if (o->keyblock) {
    910 	/*
    911 	 * We got an exact keymatch, use that.
    912 	 */
    913 
    914 	ret = krb5_verify_ap_req2(context,
    915 				  auth_context,
    916 				  &ap_req,
    917 				  server,
    918 				  o->keyblock,
    919 				  0,
    920 				  &o->ap_req_options,
    921 				  &o->ticket,
    922 				  KRB5_KU_AP_REQ_AUTH);
    923 
    924 	if (ret)
    925 	    goto out;
    926 
    927     } else {
    928 	/*
    929 	 * Interate over keytab to find a key that can decrypt the request.
    930 	 */
    931 
    932 	krb5_keytab_entry entry;
    933 	krb5_kt_cursor cursor;
    934 	int done = 0, kvno = 0;
    935 
    936 	memset(&cursor, 0, sizeof(cursor));
    937 
    938 	if (ap_req.ticket.enc_part.kvno)
    939 	    kvno = *ap_req.ticket.enc_part.kvno;
    940 
    941 	ret = krb5_kt_start_seq_get(context, id, &cursor);
    942 	if (ret)
    943 	    goto out;
    944 
    945 	done = 0;
    946 	while (!done) {
    947 	    krb5_principal p;
    948 
    949 	    ret = krb5_kt_next_entry(context, id, &entry, &cursor);
    950 	    if (ret) {
    951 		_krb5_kt_principal_not_found(context, ret, id, o->server,
    952 					     ap_req.ticket.enc_part.etype,
    953 					     kvno);
    954 		break;
    955 	    }
    956 
    957 	    if (entry.keyblock.keytype != ap_req.ticket.enc_part.etype) {
    958 		krb5_kt_free_entry (context, &entry);
    959 		continue;
    960 	    }
    961 
    962 	    ret = krb5_verify_ap_req2(context,
    963 				      auth_context,
    964 				      &ap_req,
    965 				      server,
    966 				      &entry.keyblock,
    967 				      0,
    968 				      &o->ap_req_options,
    969 				      &o->ticket,
    970 				      KRB5_KU_AP_REQ_AUTH);
    971 	    if (ret) {
    972 		krb5_kt_free_entry (context, &entry);
    973 		continue;
    974 	    }
    975 
    976 	    /*
    977 	     * Found a match, save the keyblock for PAC processing,
    978 	     * and update the service principal in the ticket to match
    979 	     * whatever is in the keytab.
    980 	     */
    981 
    982 	    ret = krb5_copy_keyblock(context,
    983 				     &entry.keyblock,
    984 				     &o->keyblock);
    985 	    if (ret) {
    986 		krb5_kt_free_entry (context, &entry);
    987 		break;
    988 	    }
    989 
    990 	    ret = krb5_copy_principal(context, entry.principal, &p);
    991 	    if (ret) {
    992 		krb5_kt_free_entry (context, &entry);
    993 		break;
    994 	    }
    995 	    krb5_free_principal(context, o->ticket->server);
    996 	    o->ticket->server = p;
    997 
    998 	    krb5_kt_free_entry (context, &entry);
    999 
   1000 	    done = 1;
   1001 	}
   1002 	krb5_kt_end_seq_get (context, id, &cursor);
   1003         if (ret)
   1004             goto out;
   1005     }
   1006 
   1007     /* If there is a PAC, verify its server signature */
   1008     if (inctx == NULL || inctx->check_pac) {
   1009 	krb5_pac pac;
   1010 	krb5_data data;
   1011 
   1012 	ret = krb5_ticket_get_authorization_data_type(context,
   1013 						      o->ticket,
   1014 						      KRB5_AUTHDATA_WIN2K_PAC,
   1015 						      &data);
   1016 	if (ret == 0) {
   1017 	    ret = krb5_pac_parse(context, data.data, data.length, &pac);
   1018 	    krb5_data_free(&data);
   1019 	    if (ret)
   1020 		goto out;
   1021 
   1022 	    ret = krb5_pac_verify(context,
   1023 				  pac,
   1024 				  o->ticket->ticket.authtime,
   1025 				  o->ticket->client,
   1026 				  o->keyblock,
   1027 				  NULL);
   1028 	    krb5_pac_free(context, pac);
   1029 	    if (ret)
   1030 		goto out;
   1031 	} else
   1032 	  ret = 0;
   1033     }
   1034 out:
   1035 
   1036     if (ret || outctx == NULL) {
   1037 	krb5_rd_req_out_ctx_free(context, o);
   1038     } else
   1039 	*outctx = o;
   1040 
   1041     free_AP_REQ(&ap_req);
   1042 
   1043     if (service)
   1044 	krb5_free_principal(context, service);
   1045 
   1046     if (keytab)
   1047 	krb5_kt_close(context, keytab);
   1048 
   1049     return ret;
   1050 }
   1051