Home | History | Annotate | Line # | Download | only in krb5
      1 /*	$NetBSD: get_cred.c,v 1.4 2023/06/19 21:41:44 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 - 2008 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  *
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  *
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  *
     21  * 3. Neither the name of the Institute nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  */
     37 
     38 #include "krb5_locl.h"
     39 #include <assert.h>
     40 
     41 static krb5_error_code
     42 get_cred_kdc_capath(krb5_context, krb5_kdc_flags,
     43 		    krb5_ccache, krb5_creds *, krb5_principal,
     44 		    Ticket *, krb5_creds **, krb5_creds ***);
     45 
     46 /*
     47  * Take the `body' and encode it into `padata' using the credentials
     48  * in `creds'.
     49  */
     50 
     51 static krb5_error_code
     52 make_pa_tgs_req(krb5_context context,
     53 		krb5_auth_context ac,
     54 		KDC_REQ_BODY *body,
     55 		PA_DATA *padata,
     56 		krb5_creds *creds)
     57 {
     58     u_char *buf;
     59     size_t buf_size;
     60     size_t len = 0;
     61     krb5_data in_data;
     62     krb5_error_code ret;
     63 
     64     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
     65     if (ret)
     66 	goto out;
     67     if(buf_size != len)
     68 	krb5_abortx(context, "internal error in ASN.1 encoder");
     69 
     70     in_data.length = len;
     71     in_data.data   = buf;
     72     ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
     73 				&padata->padata_value,
     74 				KRB5_KU_TGS_REQ_AUTH_CKSUM,
     75 				KRB5_KU_TGS_REQ_AUTH);
     76  out:
     77     free (buf);
     78     if(ret)
     79 	return ret;
     80     padata->padata_type = KRB5_PADATA_TGS_REQ;
     81     return 0;
     82 }
     83 
     84 /*
     85  * Set the `enc-authorization-data' in `req_body' based on `authdata'
     86  */
     87 
     88 static krb5_error_code
     89 set_auth_data (krb5_context context,
     90 	       KDC_REQ_BODY *req_body,
     91 	       krb5_authdata *authdata,
     92 	       krb5_keyblock *subkey)
     93 {
     94     if(authdata->len) {
     95 	size_t len = 0, buf_size;
     96 	unsigned char *buf;
     97 	krb5_crypto crypto;
     98 	krb5_error_code ret;
     99 
    100 	ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata,
    101 			   &len, ret);
    102 	if (ret)
    103 	    return ret;
    104 	if (buf_size != len)
    105 	    krb5_abortx(context, "internal error in ASN.1 encoder");
    106 
    107 	ALLOC(req_body->enc_authorization_data, 1);
    108 	if (req_body->enc_authorization_data == NULL) {
    109 	    free (buf);
    110 	    return krb5_enomem(context);
    111 	}
    112 	ret = krb5_crypto_init(context, subkey, 0, &crypto);
    113 	if (ret) {
    114 	    free (buf);
    115 	    free (req_body->enc_authorization_data);
    116 	    req_body->enc_authorization_data = NULL;
    117 	    return ret;
    118 	}
    119 	krb5_encrypt_EncryptedData(context,
    120 				   crypto,
    121 				   KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
    122 				   buf,
    123 				   len,
    124 				   0,
    125 				   req_body->enc_authorization_data);
    126 	free (buf);
    127 	krb5_crypto_destroy(context, crypto);
    128     } else {
    129 	req_body->enc_authorization_data = NULL;
    130     }
    131     return 0;
    132 }
    133 
    134 /*
    135  * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
    136  * (if not-NULL), `in_creds', `krbtgt', and returning the generated
    137  * subkey in `subkey'.
    138  */
    139 
    140 static krb5_error_code
    141 init_tgs_req (krb5_context context,
    142 	      krb5_ccache ccache,
    143 	      krb5_addresses *addresses,
    144 	      krb5_kdc_flags flags,
    145 	      Ticket *second_ticket,
    146 	      krb5_creds *in_creds,
    147 	      krb5_creds *krbtgt,
    148 	      unsigned nonce,
    149 	      const METHOD_DATA *padata,
    150 	      krb5_keyblock **subkey,
    151 	      TGS_REQ *t)
    152 {
    153     krb5_auth_context ac = NULL;
    154     krb5_error_code ret = 0;
    155 
    156     memset(t, 0, sizeof(*t));
    157     t->pvno = 5;
    158     t->msg_type = krb_tgs_req;
    159     if (in_creds->session.keytype) {
    160 	ALLOC_SEQ(&t->req_body.etype, 1);
    161 	if(t->req_body.etype.val == NULL) {
    162 	    ret = krb5_enomem(context);
    163 	    goto fail;
    164 	}
    165 	t->req_body.etype.val[0] = in_creds->session.keytype;
    166     } else {
    167 	ret = _krb5_init_etype(context,
    168 			       KRB5_PDU_TGS_REQUEST,
    169 			       &t->req_body.etype.len,
    170 			       &t->req_body.etype.val,
    171 			       NULL);
    172     }
    173     if (ret)
    174 	goto fail;
    175     t->req_body.addresses = addresses;
    176     t->req_body.kdc_options = flags.b;
    177     t->req_body.kdc_options.forwardable = krbtgt->flags.b.forwardable;
    178     t->req_body.kdc_options.renewable = krbtgt->flags.b.renewable;
    179     t->req_body.kdc_options.proxiable = krbtgt->flags.b.proxiable;
    180     ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
    181     if (ret)
    182 	goto fail;
    183     ALLOC(t->req_body.sname, 1);
    184     if (t->req_body.sname == NULL) {
    185 	ret = krb5_enomem(context);
    186 	goto fail;
    187     }
    188 
    189     /* some versions of some code might require that the client be
    190        present in TGS-REQs, but this is clearly against the spec */
    191 
    192     ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
    193     if (ret)
    194 	goto fail;
    195 
    196     if (krbtgt->times.starttime) {
    197         ALLOC(t->req_body.from, 1);
    198         if(t->req_body.from == NULL){
    199             ret = krb5_enomem(context);
    200             goto fail;
    201         }
    202         *t->req_body.from = in_creds->times.starttime;
    203     }
    204 
    205     /* req_body.till should be NULL if there is no endtime specified,
    206        but old MIT code (like DCE secd) doesn't like that */
    207     ALLOC(t->req_body.till, 1);
    208     if(t->req_body.till == NULL){
    209 	ret = krb5_enomem(context);
    210 	goto fail;
    211     }
    212     *t->req_body.till = in_creds->times.endtime;
    213 
    214     if (t->req_body.kdc_options.renewable && krbtgt->times.renew_till) {
    215         ALLOC(t->req_body.rtime, 1);
    216         if(t->req_body.rtime == NULL){
    217             ret = krb5_enomem(context);
    218             goto fail;
    219         }
    220         *t->req_body.rtime = in_creds->times.renew_till;
    221     }
    222 
    223     t->req_body.nonce = nonce;
    224     if(second_ticket){
    225 	ALLOC(t->req_body.additional_tickets, 1);
    226 	if (t->req_body.additional_tickets == NULL) {
    227 	    ret = krb5_enomem(context);
    228 	    goto fail;
    229 	}
    230 	ALLOC_SEQ(t->req_body.additional_tickets, 1);
    231 	if (t->req_body.additional_tickets->val == NULL) {
    232 	    ret = krb5_enomem(context);
    233 	    goto fail;
    234 	}
    235 	ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
    236 	if (ret)
    237 	    goto fail;
    238     }
    239     ALLOC(t->padata, 1);
    240     if (t->padata == NULL) {
    241 	ret = krb5_enomem(context);
    242 	goto fail;
    243     }
    244     ALLOC_SEQ(t->padata, 1 + padata->len);
    245     if (t->padata->val == NULL) {
    246 	ret = krb5_enomem(context);
    247 	goto fail;
    248     }
    249     {
    250 	size_t i;
    251 	for (i = 0; i < padata->len; i++) {
    252 	    ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
    253 	    if (ret) {
    254 		krb5_set_error_message(context, ret,
    255 				       N_("malloc: out of memory", ""));
    256 		goto fail;
    257 	    }
    258 	}
    259     }
    260 
    261     ret = krb5_auth_con_init(context, &ac);
    262     if(ret)
    263 	goto fail;
    264 
    265     ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session);
    266     if (ret)
    267 	goto fail;
    268 
    269     ret = set_auth_data (context, &t->req_body, &in_creds->authdata,
    270 			 ac->local_subkey);
    271     if (ret)
    272 	goto fail;
    273 
    274     ret = make_pa_tgs_req(context,
    275 			  ac,
    276 			  &t->req_body,
    277 			  &t->padata->val[0],
    278 			  krbtgt);
    279     if(ret)
    280 	goto fail;
    281 
    282     ret = krb5_auth_con_getlocalsubkey(context, ac, subkey);
    283     if (ret)
    284 	goto fail;
    285 
    286 fail:
    287     if (ac)
    288 	krb5_auth_con_free(context, ac);
    289     if (ret) {
    290 	t->req_body.addresses = NULL;
    291 	free_TGS_REQ (t);
    292     }
    293     return ret;
    294 }
    295 
    296 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    297 _krb5_get_krbtgt(krb5_context context,
    298 		 krb5_ccache  id,
    299 		 krb5_realm realm,
    300 		 krb5_creds **cred)
    301 {
    302     krb5_error_code ret;
    303     krb5_creds tmp_cred;
    304 
    305     memset(&tmp_cred, 0, sizeof(tmp_cred));
    306 
    307     ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
    308     if (ret)
    309 	return ret;
    310 
    311     ret = krb5_make_principal(context,
    312 			      &tmp_cred.server,
    313 			      realm,
    314 			      KRB5_TGS_NAME,
    315 			      realm,
    316 			      NULL);
    317     if(ret) {
    318 	krb5_free_principal(context, tmp_cred.client);
    319 	return ret;
    320     }
    321     /*
    322      * The forwardable TGT might not be the start TGT, in which case, it is
    323      * generally, but not always already cached.  Just in case, get it again if
    324      * lost.
    325      */
    326     ret = krb5_get_credentials(context,
    327 			       0,
    328 			       id,
    329 			       &tmp_cred,
    330 			       cred);
    331     krb5_free_principal(context, tmp_cred.client);
    332     krb5_free_principal(context, tmp_cred.server);
    333     if(ret)
    334 	return ret;
    335     return 0;
    336 }
    337 
    338 /* DCE compatible decrypt proc */
    339 static krb5_error_code KRB5_CALLCONV
    340 decrypt_tkt_with_subkey (krb5_context context,
    341 			 krb5_keyblock *key,
    342 			 krb5_key_usage usage,
    343 			 krb5_const_pointer skey,
    344 			 krb5_kdc_rep *dec_rep)
    345 {
    346     const krb5_keyblock *subkey = skey;
    347     krb5_error_code ret = 0;
    348     krb5_data data;
    349     size_t size;
    350     krb5_crypto crypto;
    351 
    352     assert(usage == 0);
    353 
    354     krb5_data_zero(&data);
    355 
    356     /*
    357      * start out with trying with subkey if we have one
    358      */
    359     if (subkey) {
    360 	ret = krb5_crypto_init(context, subkey, 0, &crypto);
    361 	if (ret)
    362 	    return ret;
    363 	ret = krb5_decrypt_EncryptedData (context,
    364 					  crypto,
    365 					  KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
    366 					  &dec_rep->kdc_rep.enc_part,
    367 					  &data);
    368 	/*
    369 	 * If the is Windows 2000 DC, we need to retry with key usage
    370 	 * 8 when doing ARCFOUR.
    371 	 */
    372 	if (ret && subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) {
    373 	    ret = krb5_decrypt_EncryptedData(context,
    374 					     crypto,
    375 					     8,
    376 					     &dec_rep->kdc_rep.enc_part,
    377 					     &data);
    378 	}
    379 	krb5_crypto_destroy(context, crypto);
    380     }
    381     if (subkey == NULL || ret) {
    382 	ret = krb5_crypto_init(context, key, 0, &crypto);
    383 	if (ret)
    384 	    return ret;
    385 	ret = krb5_decrypt_EncryptedData (context,
    386 					  crypto,
    387 					  KRB5_KU_TGS_REP_ENC_PART_SESSION,
    388 					  &dec_rep->kdc_rep.enc_part,
    389 					  &data);
    390 	krb5_crypto_destroy(context, crypto);
    391     }
    392     if (ret)
    393 	return ret;
    394 
    395     ret = decode_EncASRepPart(data.data,
    396 			      data.length,
    397 			      &dec_rep->enc_part,
    398 			      &size);
    399     if (ret)
    400 	ret = decode_EncTGSRepPart(data.data,
    401 				   data.length,
    402 				   &dec_rep->enc_part,
    403 				   &size);
    404     if (ret)
    405       krb5_set_error_message(context, ret,
    406 			     N_("Failed to decode encpart in ticket", ""));
    407     krb5_data_free (&data);
    408     return ret;
    409 }
    410 
    411 static krb5_error_code
    412 get_cred_kdc(krb5_context context,
    413 	     krb5_ccache id,
    414 	     krb5_kdc_flags flags,
    415 	     krb5_addresses *addresses,
    416 	     krb5_creds *in_creds,
    417 	     krb5_creds *krbtgt,
    418 	     krb5_principal impersonate_principal,
    419 	     Ticket *second_ticket,
    420 	     krb5_creds *out_creds)
    421 {
    422     TGS_REQ req;
    423     krb5_data enc;
    424     krb5_data resp;
    425     krb5_kdc_rep rep = {0};
    426     KRB_ERROR error;
    427     krb5_error_code ret;
    428     unsigned nonce;
    429     krb5_keyblock *subkey = NULL;
    430     size_t len = 0;
    431     Ticket second_ticket_data;
    432     METHOD_DATA padata;
    433 
    434     krb5_data_zero(&resp);
    435     krb5_data_zero(&enc);
    436     padata.val = NULL;
    437     padata.len = 0;
    438 
    439     krb5_generate_random_block(&nonce, sizeof(nonce));
    440     nonce &= 0xffffffff;
    441 
    442     if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
    443 	ret = decode_Ticket(in_creds->second_ticket.data,
    444 			    in_creds->second_ticket.length,
    445 			    &second_ticket_data, &len);
    446 	if(ret)
    447 	    return ret;
    448 	second_ticket = &second_ticket_data;
    449     }
    450 
    451 
    452     if (impersonate_principal) {
    453 	krb5_crypto crypto;
    454 	PA_S4U2Self self;
    455 	krb5_data data;
    456 	void *buf;
    457 	size_t size = 0;
    458 
    459 	self.name = impersonate_principal->name;
    460 	self.realm = impersonate_principal->realm;
    461 	self.auth = estrdup("Kerberos");
    462 
    463 	ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
    464 	if (ret) {
    465 	    free(self.auth);
    466 	    goto out;
    467 	}
    468 
    469 	ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
    470 	if (ret) {
    471 	    free(self.auth);
    472 	    krb5_data_free(&data);
    473 	    goto out;
    474 	}
    475 
    476 	ret = krb5_create_checksum(context,
    477 				   crypto,
    478 				   KRB5_KU_OTHER_CKSUM,
    479 				   0,
    480 				   data.data,
    481 				   data.length,
    482 				   &self.cksum);
    483 	krb5_crypto_destroy(context, crypto);
    484 	krb5_data_free(&data);
    485 	if (ret) {
    486 	    free(self.auth);
    487 	    goto out;
    488 	}
    489 
    490 	ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
    491 	free(self.auth);
    492 	free_Checksum(&self.cksum);
    493 	if (ret)
    494 	    goto out;
    495 	if (len != size)
    496 	    krb5_abortx(context, "internal asn1 error");
    497 
    498 	ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);
    499 	if (ret)
    500 	    goto out;
    501     }
    502 
    503     ret = init_tgs_req (context,
    504 			id,
    505 			addresses,
    506 			flags,
    507 			second_ticket,
    508 			in_creds,
    509 			krbtgt,
    510 			nonce,
    511 			&padata,
    512 			&subkey,
    513 			&req);
    514     if (ret)
    515 	goto out;
    516 
    517     ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
    518     if (ret)
    519 	goto out;
    520     if(enc.length != len)
    521 	krb5_abortx(context, "internal error in ASN.1 encoder");
    522 
    523     /* don't free addresses */
    524     req.req_body.addresses = NULL;
    525     free_TGS_REQ(&req);
    526 
    527     /*
    528      * Send and receive
    529      */
    530     {
    531 	krb5_sendto_ctx stctx;
    532 	ret = krb5_sendto_ctx_alloc(context, &stctx);
    533 	if (ret)
    534 	    return ret;
    535 	krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
    536 
    537 	ret = krb5_sendto_context (context, stctx, &enc,
    538 				   krbtgt->server->name.name_string.val[1],
    539 				   &resp);
    540 	krb5_sendto_ctx_free(context, stctx);
    541     }
    542     if(ret)
    543 	goto out;
    544 
    545     if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
    546 	unsigned eflags = 0;
    547 
    548 	ret = krb5_copy_principal(context,
    549 				  in_creds->client,
    550 				  &out_creds->client);
    551 	if(ret)
    552 	    goto out2;
    553 	ret = krb5_copy_principal(context,
    554 				  in_creds->server,
    555 				  &out_creds->server);
    556 	if(ret)
    557 	    goto out2;
    558 	/* this should go someplace else */
    559 	out_creds->times.endtime = in_creds->times.endtime;
    560 
    561 	/* XXX should do better testing */
    562 	if (flags.b.cname_in_addl_tkt || impersonate_principal)
    563 	    eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
    564 	if (flags.b.request_anonymous)
    565 	    eflags |= EXTRACT_TICKET_MATCH_ANON;
    566 
    567 	ret = _krb5_extract_ticket(context,
    568 				   &rep,
    569 				   out_creds,
    570 				   &krbtgt->session,
    571 				   NULL,
    572 				   0,
    573 				   &krbtgt->addresses,
    574 				   nonce,
    575 				   eflags,
    576 				   NULL,
    577 				   decrypt_tkt_with_subkey,
    578 				   subkey);
    579     out2:
    580 	krb5_free_kdc_rep(context, &rep);
    581     } else if(krb5_rd_error(context, &resp, &error) == 0) {
    582 	ret = krb5_error_from_rd_error(context, &error, in_creds);
    583 	krb5_free_error_contents(context, &error);
    584     } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
    585 	ret = KRB5KRB_AP_ERR_V4_REPLY;
    586 	krb5_clear_error_message(context);
    587     } else {
    588 	ret = KRB5KRB_AP_ERR_MSG_TYPE;
    589 	krb5_clear_error_message(context);
    590     }
    591 
    592 out:
    593     if (second_ticket == &second_ticket_data)
    594 	free_Ticket(&second_ticket_data);
    595     free_METHOD_DATA(&padata);
    596     krb5_data_free(&resp);
    597     krb5_data_free(&enc);
    598     if(subkey)
    599 	krb5_free_keyblock(context, subkey);
    600     return ret;
    601 
    602 }
    603 
    604 /*
    605  * same as above, just get local addresses first if the krbtgt have
    606  * them and the realm is not addressless
    607  */
    608 
    609 static krb5_error_code
    610 get_cred_kdc_address(krb5_context context,
    611 		     krb5_ccache id,
    612 		     krb5_kdc_flags flags,
    613 		     krb5_addresses *addrs,
    614 		     krb5_creds *in_creds,
    615 		     krb5_creds *krbtgt,
    616 		     krb5_principal impersonate_principal,
    617 		     Ticket *second_ticket,
    618 		     krb5_creds *out_creds)
    619 {
    620     krb5_error_code ret;
    621     krb5_addresses addresses = { 0, NULL };
    622 
    623     /*
    624      * Inherit the address-ness of the krbtgt if the address is not
    625      * specified.
    626      */
    627 
    628     if (addrs == NULL && krbtgt->addresses.len != 0) {
    629 	krb5_boolean noaddr;
    630 
    631 	krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
    632 				"no-addresses", FALSE, &noaddr);
    633 
    634 	if (!noaddr) {
    635 	    krb5_get_all_client_addrs(context, &addresses);
    636 	    /* XXX this sucks. */
    637 	    addrs = &addresses;
    638 	    if(addresses.len == 0)
    639 		addrs = NULL;
    640 	}
    641     }
    642     ret = get_cred_kdc(context, id, flags, addrs, in_creds,
    643 		       krbtgt, impersonate_principal,
    644 		       second_ticket, out_creds);
    645     krb5_free_addresses(context, &addresses);
    646     return ret;
    647 }
    648 
    649 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    650 krb5_get_kdc_cred(krb5_context context,
    651 		  krb5_ccache id,
    652 		  krb5_kdc_flags flags,
    653 		  krb5_addresses *addresses,
    654 		  Ticket  *second_ticket,
    655 		  krb5_creds *in_creds,
    656 		  krb5_creds **out_creds
    657 		  )
    658 {
    659     krb5_error_code ret;
    660     krb5_creds *krbtgt;
    661 
    662     *out_creds = calloc(1, sizeof(**out_creds));
    663     if(*out_creds == NULL)
    664 	return krb5_enomem(context);
    665     ret = _krb5_get_krbtgt (context,
    666 			    id,
    667 			    in_creds->server->realm,
    668 			    &krbtgt);
    669     if(ret) {
    670 	free(*out_creds);
    671 	*out_creds = NULL;
    672 	return ret;
    673     }
    674     ret = get_cred_kdc(context, id, flags, addresses,
    675 		       in_creds, krbtgt, NULL, NULL, *out_creds);
    676     krb5_free_creds (context, krbtgt);
    677     if(ret) {
    678 	free(*out_creds);
    679 	*out_creds = NULL;
    680     }
    681     return ret;
    682 }
    683 
    684 static int
    685 not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
    686 {
    687     krb5_error_code ret;
    688     const char *err;
    689     char *str;
    690 
    691     err = krb5_get_error_message(context, code);
    692     ret = krb5_unparse_name(context, p, &str);
    693     if(ret) {
    694 	krb5_clear_error_message(context);
    695 	return code;
    696     }
    697     krb5_set_error_message(context, code, N_("%s (%s)", ""), err, str);
    698     free(str);
    699     return code;
    700 }
    701 
    702 static krb5_error_code
    703 find_cred(krb5_context context,
    704 	  krb5_ccache id,
    705 	  krb5_principal server,
    706 	  krb5_creds **tgts,
    707 	  krb5_creds *out_creds)
    708 {
    709     krb5_error_code ret;
    710     krb5_creds mcreds;
    711 
    712     krb5_cc_clear_mcred(&mcreds);
    713     mcreds.server = server;
    714     krb5_timeofday(context, &mcreds.times.endtime);
    715     ret = krb5_cc_retrieve_cred(context, id,
    716 				KRB5_TC_DONT_MATCH_REALM |
    717 				KRB5_TC_MATCH_TIMES,
    718 				&mcreds, out_creds);
    719     if(ret == 0)
    720 	return 0;
    721     while(tgts && *tgts){
    722 	if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
    723 			      &mcreds, *tgts)){
    724 	    ret = krb5_copy_creds_contents(context, *tgts, out_creds);
    725 	    return ret;
    726 	}
    727 	tgts++;
    728     }
    729     return not_found(context, server, KRB5_CC_NOTFOUND);
    730 }
    731 
    732 static krb5_error_code
    733 add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
    734 {
    735     int i;
    736     krb5_error_code ret;
    737     krb5_creds **tmp = *tgts;
    738 
    739     for(i = 0; tmp && tmp[i]; i++); /* XXX */
    740     tmp = realloc(tmp, (i+2)*sizeof(*tmp));
    741     if(tmp == NULL)
    742 	return krb5_enomem(context);
    743     *tgts = tmp;
    744     ret = krb5_copy_creds(context, tkt, &tmp[i]);
    745     tmp[i+1] = NULL;
    746     return ret;
    747 }
    748 
    749 static krb5_error_code
    750 get_cred_kdc_capath_worker(krb5_context context,
    751                            krb5_kdc_flags flags,
    752                            krb5_ccache ccache,
    753                            krb5_creds *in_creds,
    754                            krb5_const_realm try_realm,
    755                            krb5_principal impersonate_principal,
    756                            Ticket *second_ticket,
    757                            krb5_creds **out_creds,
    758                            krb5_creds ***ret_tgts)
    759 {
    760     krb5_error_code ret;
    761     krb5_creds *tgt = NULL;
    762     krb5_creds tmp_creds;
    763     krb5_const_realm client_realm, server_realm;
    764     int ok_as_delegate = 1;
    765 
    766     *out_creds = calloc(1, sizeof(**out_creds));
    767     if (*out_creds == NULL)
    768 	return krb5_enomem(context);
    769 
    770     memset(&tmp_creds, 0, sizeof(tmp_creds));
    771 
    772     client_realm = krb5_principal_get_realm(context, in_creds->client);
    773     server_realm = krb5_principal_get_realm(context, in_creds->server);
    774     ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
    775     if (ret)
    776 	goto out;
    777 
    778     ret = krb5_make_principal(context,
    779 			      &tmp_creds.server,
    780 			      try_realm,
    781 			      KRB5_TGS_NAME,
    782 			      server_realm,
    783 			      NULL);
    784     if (ret)
    785 	goto out;
    786 
    787     {
    788 	krb5_creds tgts;
    789 
    790 	/*
    791 	 * If we have krbtgt/server_realm@try_realm cached, use it and we're
    792 	 * done.
    793 	 */
    794 	ret = find_cred(context, ccache, tmp_creds.server,
    795 			*ret_tgts, &tgts);
    796 	if (ret == 0) {
    797 	    /* only allow implicit ok_as_delegate if the realm is the clients realm */
    798 	    if (strcmp(try_realm, client_realm) != 0
    799 		 || strcmp(try_realm, server_realm) != 0) {
    800 		ok_as_delegate = tgts.flags.b.ok_as_delegate;
    801 	    }
    802 
    803 	    ret = get_cred_kdc_address(context, ccache, flags, NULL,
    804 				   in_creds, &tgts,
    805 				   impersonate_principal,
    806 				   second_ticket,
    807 				   *out_creds);
    808             krb5_free_cred_contents(context, &tgts);
    809 	    if (ret == 0 &&
    810                 !krb5_principal_compare(context, in_creds->server,
    811                                         (*out_creds)->server)) {
    812 		ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
    813 	    }
    814 	    if (ret == 0 && ok_as_delegate == 0)
    815 		(*out_creds)->flags.b.ok_as_delegate = 0;
    816 
    817 	    goto out;
    818 	}
    819     }
    820 
    821     if (krb5_realm_compare(context, in_creds->client, in_creds->server)) {
    822 	ret = not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
    823 	goto out;
    824     }
    825 
    826     /*
    827      * XXX This can loop forever, plus we recurse, so we can't just keep a
    828      * count here.  The count would have to get passed around by reference.
    829      *
    830      * The KDCs check for transit loops for us, and capath data is finite, so
    831      * in fact we'll fall out of this loop at some point.  We should do our own
    832      * transit loop checking (like get_cred_kdc_referral()), and we should
    833      * impose a max number of iterations altogether.  But barring malicious or
    834      * broken KDCs, this is good enough.
    835      */
    836     while (1) {
    837 	heim_general_string tgt_inst;
    838 
    839 	ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
    840 				  NULL, NULL, &tgt, ret_tgts);
    841 	if (ret)
    842 	    goto out;
    843 
    844 	/*
    845 	 * if either of the chain or the ok_as_delegate was stripped
    846 	 * by the kdc, make sure we strip it too.
    847 	 */
    848 	if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
    849 	    ok_as_delegate = 0;
    850 	    tgt->flags.b.ok_as_delegate = 0;
    851 	}
    852 
    853 	ret = add_cred(context, tgt, ret_tgts);
    854 	if (ret)
    855 	    goto out;
    856 	tgt_inst = tgt->server->name.name_string.val[1];
    857 	if (strcmp(tgt_inst, server_realm) == 0)
    858 	    break;
    859 	krb5_free_principal(context, tmp_creds.server);
    860 	tmp_creds.server = NULL;
    861 	ret = krb5_make_principal(context, &tmp_creds.server,
    862 				  tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
    863 	if (ret)
    864 	    goto out;
    865 	ret = krb5_free_creds(context, tgt);
    866 	tgt = NULL;
    867 	if (ret)
    868 	    goto out;
    869     }
    870 
    871     ret = get_cred_kdc_address(context, ccache, flags, NULL,
    872 			       in_creds, tgt, impersonate_principal,
    873 			       second_ticket, *out_creds);
    874     if (ret == 0 &&
    875         !krb5_principal_compare(context, in_creds->server,
    876                                     (*out_creds)->server)) {
    877         krb5_free_cred_contents(context, *out_creds);
    878         ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
    879     }
    880     if (ret == 0 && ok_as_delegate == 0)
    881         (*out_creds)->flags.b.ok_as_delegate = 0;
    882 
    883 out:
    884     if (ret) {
    885 	krb5_free_creds(context, *out_creds);
    886         *out_creds = NULL;
    887     }
    888     if (tmp_creds.server)
    889 	krb5_free_principal(context, tmp_creds.server);
    890     if (tmp_creds.client)
    891 	krb5_free_principal(context, tmp_creds.client);
    892     if (tgt)
    893 	krb5_free_creds(context, tgt);
    894     return ret;
    895 }
    896 
    897 /*
    898 get_cred(server)
    899 	creds = cc_get_cred(server)
    900 	if(creds) return creds
    901 	tgt = cc_get_cred(krbtgt/server_realm@any_realm)
    902 	if(tgt)
    903 		return get_cred_tgt(server, tgt)
    904 	if(client_realm == server_realm)
    905 		return NULL
    906 	tgt = get_cred(krbtgt/server_realm@client_realm)
    907 	while(tgt_inst != server_realm)
    908 		tgt = get_cred(krbtgt/server_realm@tgt_inst)
    909 	return get_cred_tgt(server, tgt)
    910 	*/
    911 
    912 static krb5_error_code
    913 get_cred_kdc_capath(krb5_context context,
    914 		    krb5_kdc_flags flags,
    915 		    krb5_ccache ccache,
    916 		    krb5_creds *in_creds,
    917 		    krb5_principal impersonate_principal,
    918 		    Ticket *second_ticket,
    919 		    krb5_creds **out_creds,
    920 		    krb5_creds ***ret_tgts)
    921 {
    922     krb5_error_code ret;
    923     krb5_const_realm client_realm, server_realm, try_realm;
    924 
    925     client_realm = krb5_principal_get_realm(context, in_creds->client);
    926     server_realm = krb5_principal_get_realm(context, in_creds->server);
    927 
    928     try_realm = client_realm;
    929     ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, try_realm,
    930                                      impersonate_principal, second_ticket, out_creds,
    931                                      ret_tgts);
    932 
    933     if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
    934         try_realm = krb5_config_get_string(context, NULL, "capaths",
    935                                            client_realm, server_realm, NULL);
    936 
    937         if (try_realm != NULL && strcmp(try_realm, client_realm)) {
    938             ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds,
    939                                              try_realm, impersonate_principal,
    940                                              second_ticket, out_creds, ret_tgts);
    941         }
    942     }
    943 
    944     return ret;
    945 }
    946 
    947 /*
    948  * Get a service ticket from a KDC by chasing referrals from a start realm.
    949  *
    950  * All referral TGTs produced in the process are thrown away when we're done.
    951  * We don't store them, and we don't allow other search mechanisms (capaths) to
    952  * use referral TGTs produced here.
    953  */
    954 static krb5_error_code
    955 get_cred_kdc_referral(krb5_context context,
    956 		      krb5_kdc_flags flags,
    957 		      krb5_ccache ccache,
    958 		      krb5_creds *in_creds,
    959 		      krb5_principal impersonate_principal,
    960 		      Ticket *second_ticket,
    961 		      krb5_creds **out_creds)
    962 {
    963     krb5_realm start_realm = NULL;
    964     krb5_data config_start_realm;
    965     krb5_error_code ret;
    966     krb5_creds tgt, referral, ticket;
    967     krb5_creds **referral_tgts = NULL;  /* used for loop detection */
    968     int loop = 0;
    969     int ok_as_delegate = 1;
    970     size_t i;
    971 
    972     if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) {
    973 	krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED,
    974 			       N_("Name too short to do referals, skipping", ""));
    975 	return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
    976     }
    977 
    978     memset(&tgt, 0, sizeof(tgt));
    979     memset(&ticket, 0, sizeof(ticket));
    980 
    981     flags.b.canonicalize = 1;
    982 
    983     *out_creds = NULL;
    984 
    985 
    986     ret = krb5_cc_get_config(context, ccache, NULL, "start_realm", &config_start_realm);
    987     if (ret == 0) {
    988         start_realm = strndup(config_start_realm.data, config_start_realm.length);
    989 	krb5_data_free(&config_start_realm);
    990     } else {
    991         start_realm = strdup(krb5_principal_get_realm(context, in_creds->client));
    992     }
    993     if (start_realm == NULL)
    994         return krb5_enomem(context);
    995 
    996     /* find tgt for the clients base realm */
    997     {
    998 	krb5_principal tgtname;
    999 
   1000 	ret = krb5_make_principal(context, &tgtname,
   1001 				  start_realm,
   1002 				  KRB5_TGS_NAME,
   1003 				  start_realm,
   1004 				  NULL);
   1005 	if (ret) {
   1006             free(start_realm);
   1007 	    return ret;
   1008         }
   1009 
   1010 	ret = find_cred(context, ccache, tgtname, NULL, &tgt);
   1011 	krb5_free_principal(context, tgtname);
   1012 	if (ret) {
   1013             free(start_realm);
   1014 	    return ret;
   1015         }
   1016     }
   1017 
   1018     referral = *in_creds;
   1019     ret = krb5_copy_principal(context, in_creds->server, &referral.server);
   1020     if (ret) {
   1021 	krb5_free_cred_contents(context, &tgt);
   1022         free(start_realm);
   1023 	return ret;
   1024     }
   1025     ret = krb5_principal_set_realm(context, referral.server, start_realm);
   1026     free(start_realm);
   1027     start_realm = NULL;
   1028     if (ret) {
   1029 	krb5_free_cred_contents(context, &tgt);
   1030 	krb5_free_principal(context, referral.server);
   1031 	return ret;
   1032     }
   1033 
   1034     while (loop++ < 17) {
   1035 	krb5_creds **tickets;
   1036 	krb5_creds mcreds;
   1037 	char *referral_realm;
   1038 
   1039 	/* Use cache if we are not doing impersonation or contrained deleg */
   1040 	if (impersonate_principal == NULL || flags.b.cname_in_addl_tkt) {
   1041 	    krb5_cc_clear_mcred(&mcreds);
   1042 	    mcreds.server = referral.server;
   1043 	    krb5_timeofday(context, &mcreds.times.endtime);
   1044 	    ret = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_TIMES,
   1045 					&mcreds, &ticket);
   1046 	} else
   1047 	    ret = EINVAL;
   1048 
   1049 	if (ret) {
   1050 	    ret = get_cred_kdc_address(context, ccache, flags, NULL,
   1051 				       &referral, &tgt, impersonate_principal,
   1052 				       second_ticket, &ticket);
   1053 	    if (ret)
   1054 		goto out;
   1055 	}
   1056 
   1057 	/* Did we get the right ticket ? */
   1058 	if (krb5_principal_compare_any_realm(context,
   1059 					     referral.server,
   1060 					     ticket.server))
   1061 	    break;
   1062 
   1063 	if (!krb5_principal_is_krbtgt(context, ticket.server)) {
   1064 	    krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
   1065 				   N_("Got back an non krbtgt "
   1066 				      "ticket referrals", ""));
   1067 	    ret = KRB5KRB_AP_ERR_NOT_US;
   1068 	    goto out;
   1069 	}
   1070 
   1071 	referral_realm = ticket.server->name.name_string.val[1];
   1072 
   1073 	/* check that there are no referrals loops */
   1074 	tickets = referral_tgts;
   1075 
   1076 	krb5_cc_clear_mcred(&mcreds);
   1077 	mcreds.server = ticket.server;
   1078 
   1079 	while (tickets && *tickets){
   1080 	    if (krb5_compare_creds(context,
   1081 				  KRB5_TC_DONT_MATCH_REALM,
   1082 				  &mcreds,
   1083 				  *tickets)) {
   1084 		krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
   1085 				       N_("Referral from %s "
   1086 					  "loops back to realm %s", ""),
   1087 				       tgt.server->realm,
   1088 				       referral_realm);
   1089 		ret = KRB5_GET_IN_TKT_LOOP;
   1090                 goto out;
   1091 	    }
   1092 	    tickets++;
   1093 	}
   1094 
   1095 	/*
   1096 	 * if either of the chain or the ok_as_delegate was stripped
   1097 	 * by the kdc, make sure we strip it too.
   1098 	 */
   1099 
   1100 	if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
   1101 	    ok_as_delegate = 0;
   1102 	    ticket.flags.b.ok_as_delegate = 0;
   1103 	}
   1104 
   1105         _krb5_debug(context, 6, "get_cred_kdc_referral: got referral "
   1106                     "to %s from %s", referral_realm, referral.server->realm);
   1107 	ret = add_cred(context, &ticket, &referral_tgts);
   1108 	if (ret)
   1109 	    goto out;
   1110 
   1111 	/* try realm in the referral */
   1112 	ret = krb5_principal_set_realm(context,
   1113 				       referral.server,
   1114 				       referral_realm);
   1115 	krb5_free_cred_contents(context, &tgt);
   1116 	tgt = ticket;
   1117 	memset(&ticket, 0, sizeof(ticket));
   1118 	if (ret)
   1119 	    goto out;
   1120     }
   1121 
   1122     ret = krb5_copy_creds(context, &ticket, out_creds);
   1123 
   1124 out:
   1125     for (i = 0; referral_tgts && referral_tgts[i]; i++)
   1126 	krb5_free_creds(context, referral_tgts[i]);
   1127     free(referral_tgts);
   1128     krb5_free_principal(context, referral.server);
   1129     krb5_free_cred_contents(context, &tgt);
   1130     krb5_free_cred_contents(context, &ticket);
   1131     return ret;
   1132 }
   1133 
   1134 
   1135 /*
   1136  * Glue function between referrals version and old client chasing
   1137  * codebase.
   1138  */
   1139 
   1140 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1141 _krb5_get_cred_kdc_any(krb5_context context,
   1142 		       krb5_kdc_flags flags,
   1143 		       krb5_ccache ccache,
   1144 		       krb5_creds *in_creds,
   1145 		       krb5_principal impersonate_principal,
   1146 		       Ticket *second_ticket,
   1147 		       krb5_creds **out_creds,
   1148 		       krb5_creds ***ret_tgts)
   1149 {
   1150     krb5_error_code ret;
   1151     krb5_deltat offset;
   1152 
   1153     ret = krb5_cc_get_kdc_offset(context, ccache, &offset);
   1154     if (ret == 0) {
   1155 	context->kdc_sec_offset = offset;
   1156 	context->kdc_usec_offset = 0;
   1157     }
   1158 
   1159     if (strcmp(in_creds->server->realm, "") != 0) {
   1160         /*
   1161          * Non-empty realm?  Try capaths first.  We might have local
   1162          * policy (capaths) to honor.
   1163          */
   1164         ret = get_cred_kdc_capath(context,
   1165                                   flags,
   1166                                   ccache,
   1167                                   in_creds,
   1168                                   impersonate_principal,
   1169                                   second_ticket,
   1170                                   out_creds,
   1171                                   ret_tgts);
   1172         if (ret == 0)
   1173             return ret;
   1174     }
   1175 
   1176     /* Otherwise try referrals */
   1177     return get_cred_kdc_referral(context,
   1178                                  flags,
   1179                                  ccache,
   1180                                  in_creds,
   1181                                  impersonate_principal,
   1182                                  second_ticket,
   1183                                  out_creds);
   1184 }
   1185 
   1186 static krb5_error_code
   1187 check_cc(krb5_context context, krb5_flags options, krb5_ccache ccache,
   1188 	 krb5_creds *in_creds, krb5_creds *out_creds)
   1189 {
   1190     krb5_error_code ret;
   1191     krb5_timestamp now;
   1192     krb5_creds mcreds = *in_creds;
   1193 
   1194     krb5_timeofday(context, &now);
   1195 
   1196     if (!(options & KRB5_GC_EXPIRED_OK) &&
   1197 	mcreds.times.endtime < now) {
   1198 	mcreds.times.renew_till = 0;
   1199 	krb5_timeofday(context, &mcreds.times.endtime);
   1200 	options |= KRB5_TC_MATCH_TIMES;
   1201     }
   1202 
   1203     if (mcreds.server->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON) {
   1204         /* Avoid name canonicalization in krb5_cc_retrieve_cred() */
   1205         krb5_principal_set_type(context, mcreds.server, KRB5_NT_SRV_HST);
   1206     }
   1207 
   1208     if (options & KRB5_GC_ANONYMOUS) {
   1209 	ret = krb5_make_principal(context,
   1210 				  &mcreds.client,
   1211 				  krb5_principal_get_realm(context, mcreds.client),
   1212 				  KRB5_WELLKNOWN_NAME,
   1213 				  KRB5_ANON_NAME,
   1214 				  NULL);
   1215 	if (ret)
   1216 	    return ret;
   1217     }
   1218 
   1219     ret = krb5_cc_retrieve_cred(context, ccache,
   1220 				(options &
   1221 				 (KRB5_TC_DONT_MATCH_REALM |
   1222                                   KRB5_TC_MATCH_KEYTYPE |
   1223 				  KRB5_TC_MATCH_TIMES)),
   1224 				&mcreds, out_creds);
   1225 
   1226     if (options & KRB5_GC_ANONYMOUS)
   1227 	krb5_free_principal(context, mcreds.client);
   1228 
   1229     return ret;
   1230 }
   1231 
   1232 static void
   1233 store_cred(krb5_context context, krb5_ccache ccache,
   1234 	   krb5_const_principal server_princ, krb5_creds *creds)
   1235 {
   1236     if (!krb5_principal_compare(context, creds->server, server_princ)) {
   1237         krb5_principal tmp_princ = creds->server;
   1238         /*
   1239          * Store the cred with the pre-canon server princ first so it
   1240          * can be found quickly in the future.
   1241          */
   1242         creds->server = (krb5_principal)server_princ;
   1243         krb5_cc_store_cred(context, ccache, creds);
   1244         creds->server = tmp_princ;
   1245         /* Then store again with the canonicalized server princ */
   1246     }
   1247     krb5_cc_store_cred(context, ccache, creds);
   1248 }
   1249 
   1250 
   1251 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1252 krb5_get_credentials_with_flags(krb5_context context,
   1253 				krb5_flags options,
   1254 				krb5_kdc_flags flags,
   1255 				krb5_ccache ccache,
   1256 				krb5_creds *in_creds,
   1257 				krb5_creds **out_creds)
   1258 {
   1259     krb5_error_code ret;
   1260     krb5_name_canon_iterator name_canon_iter = NULL;
   1261     krb5_name_canon_rule_options rule_opts;
   1262     krb5_const_principal try_princ = NULL;
   1263     krb5_principal save_princ = in_creds->server;
   1264     krb5_creds **tgts;
   1265     krb5_creds *res_creds;
   1266     int i;
   1267 
   1268     if (_krb5_have_debug(context, 5)) {
   1269         char *unparsed;
   1270 
   1271         ret = krb5_unparse_name(context, in_creds->server, &unparsed);
   1272         if (ret) {
   1273             _krb5_debug(context, 5, "krb5_get_creds: unable to display "
   1274                         "requested service principal");
   1275         } else {
   1276             _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket "
   1277                         "for %s", unparsed);
   1278             free(unparsed);
   1279         }
   1280     }
   1281 
   1282     if (in_creds->session.keytype) {
   1283 	ret = krb5_enctype_valid(context, in_creds->session.keytype);
   1284 	if (ret)
   1285 	    return ret;
   1286 	options |= KRB5_TC_MATCH_KEYTYPE;
   1287     }
   1288 
   1289     *out_creds = NULL;
   1290     res_creds = calloc(1, sizeof(*res_creds));
   1291     if (res_creds == NULL)
   1292 	return krb5_enomem(context);
   1293 
   1294     ret = krb5_name_canon_iterator_start(context, in_creds->server,
   1295 					 &name_canon_iter);
   1296     if (ret)
   1297 	return ret;
   1298 
   1299 next_rule:
   1300     krb5_free_cred_contents(context, res_creds);
   1301     memset(res_creds, 0, sizeof (*res_creds));
   1302     ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ,
   1303                                   &rule_opts);
   1304     in_creds->server = rk_UNCONST(try_princ);
   1305     if (ret)
   1306 	goto out;
   1307 
   1308     if (name_canon_iter == NULL) {
   1309 	if (options & KRB5_GC_CACHED)
   1310 	    ret = KRB5_CC_NOTFOUND;
   1311 	else
   1312 	    ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
   1313 	goto out;
   1314     }
   1315 
   1316     ret = check_cc(context, options, ccache, in_creds, res_creds);
   1317     if (ret == 0) {
   1318 	*out_creds = res_creds;
   1319         res_creds = NULL;
   1320 	goto out;
   1321     } else if(ret != KRB5_CC_END) {
   1322         goto out;
   1323     }
   1324     if (options & KRB5_GC_CACHED)
   1325 	goto next_rule;
   1326 
   1327     if(options & KRB5_GC_USER_USER)
   1328 	flags.b.enc_tkt_in_skey = 1;
   1329     if (flags.b.enc_tkt_in_skey)
   1330 	options |= KRB5_GC_NO_STORE;
   1331 
   1332     tgts = NULL;
   1333     ret = _krb5_get_cred_kdc_any(context, flags, ccache,
   1334 				 in_creds, NULL, NULL, out_creds, &tgts);
   1335     for (i = 0; tgts && tgts[i]; i++) {
   1336 	if ((options & KRB5_GC_NO_STORE) == 0)
   1337 	    krb5_cc_store_cred(context, ccache, tgts[i]);
   1338 	krb5_free_creds(context, tgts[i]);
   1339     }
   1340     free(tgts);
   1341 
   1342     /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */
   1343     if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN &&
   1344 	!(rule_opts & KRB5_NCRO_USE_FAST))
   1345 	goto next_rule;
   1346 
   1347     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
   1348 	store_cred(context, ccache, in_creds->server, *out_creds);
   1349 
   1350     if (ret == 0 && _krb5_have_debug(context, 5)) {
   1351         char *unparsed;
   1352 
   1353         ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed);
   1354         if (ret) {
   1355             _krb5_debug(context, 5, "krb5_get_creds: unable to display "
   1356                         "service principal");
   1357         } else {
   1358             _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s",
   1359                         unparsed);
   1360             free(unparsed);
   1361         }
   1362     }
   1363 
   1364 out:
   1365     in_creds->server = save_princ;
   1366     krb5_free_creds(context, res_creds);
   1367     krb5_free_name_canon_iterator(context, name_canon_iter);
   1368     if (ret)
   1369 	return not_found(context, in_creds->server, ret);
   1370     return 0;
   1371 }
   1372 
   1373 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1374 krb5_get_credentials(krb5_context context,
   1375 		     krb5_flags options,
   1376 		     krb5_ccache ccache,
   1377 		     krb5_creds *in_creds,
   1378 		     krb5_creds **out_creds)
   1379 {
   1380     krb5_kdc_flags flags;
   1381     flags.i = 0;
   1382     return krb5_get_credentials_with_flags(context, options, flags,
   1383 					   ccache, in_creds, out_creds);
   1384 }
   1385 
   1386 struct krb5_get_creds_opt_data {
   1387     krb5_principal self;
   1388     krb5_flags options;
   1389     krb5_enctype enctype;
   1390     Ticket *ticket;
   1391 };
   1392 
   1393 
   1394 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1395 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
   1396 {
   1397     *opt = calloc(1, sizeof(**opt));
   1398     if (*opt == NULL)
   1399 	return krb5_enomem(context);
   1400     return 0;
   1401 }
   1402 
   1403 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
   1404 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
   1405 {
   1406     if (opt->self)
   1407 	krb5_free_principal(context, opt->self);
   1408     if (opt->ticket) {
   1409 	free_Ticket(opt->ticket);
   1410 	free(opt->ticket);
   1411     }
   1412     memset(opt, 0, sizeof(*opt));
   1413     free(opt);
   1414 }
   1415 
   1416 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
   1417 krb5_get_creds_opt_set_options(krb5_context context,
   1418 			       krb5_get_creds_opt opt,
   1419 			       krb5_flags options)
   1420 {
   1421     opt->options = options;
   1422 }
   1423 
   1424 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
   1425 krb5_get_creds_opt_add_options(krb5_context context,
   1426 			       krb5_get_creds_opt opt,
   1427 			       krb5_flags options)
   1428 {
   1429     opt->options |= options;
   1430 }
   1431 
   1432 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
   1433 krb5_get_creds_opt_set_enctype(krb5_context context,
   1434 			       krb5_get_creds_opt opt,
   1435 			       krb5_enctype enctype)
   1436 {
   1437     opt->enctype = enctype;
   1438 }
   1439 
   1440 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1441 krb5_get_creds_opt_set_impersonate(krb5_context context,
   1442 				   krb5_get_creds_opt opt,
   1443 				   krb5_const_principal self)
   1444 {
   1445     if (opt->self)
   1446 	krb5_free_principal(context, opt->self);
   1447     return krb5_copy_principal(context, self, &opt->self);
   1448 }
   1449 
   1450 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1451 krb5_get_creds_opt_set_ticket(krb5_context context,
   1452 			      krb5_get_creds_opt opt,
   1453 			      const Ticket *ticket)
   1454 {
   1455     if (opt->ticket) {
   1456 	free_Ticket(opt->ticket);
   1457 	free(opt->ticket);
   1458 	opt->ticket = NULL;
   1459     }
   1460     if (ticket) {
   1461 	krb5_error_code ret;
   1462 
   1463 	opt->ticket = malloc(sizeof(*ticket));
   1464 	if (opt->ticket == NULL)
   1465 	    return krb5_enomem(context);
   1466 	ret = copy_Ticket(ticket, opt->ticket);
   1467 	if (ret) {
   1468 	    free(opt->ticket);
   1469 	    opt->ticket = NULL;
   1470 	    krb5_set_error_message(context, ret,
   1471 				   N_("malloc: out of memory", ""));
   1472 	    return ret;
   1473 	}
   1474     }
   1475     return 0;
   1476 }
   1477 
   1478 
   1479 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1480 krb5_get_creds(krb5_context context,
   1481 	       krb5_get_creds_opt opt,
   1482 	       krb5_ccache ccache,
   1483 	       krb5_const_principal inprinc,
   1484 	       krb5_creds **out_creds)
   1485 {
   1486     krb5_kdc_flags flags;
   1487     krb5_flags options;
   1488     krb5_creds in_creds;
   1489     krb5_error_code ret;
   1490     krb5_creds **tgts;
   1491     krb5_creds *res_creds;
   1492     krb5_const_principal try_princ = NULL;
   1493     krb5_name_canon_iterator name_canon_iter = NULL;
   1494     krb5_name_canon_rule_options rule_opts;
   1495     int i;
   1496     int type;
   1497     const char *comp;
   1498 
   1499     memset(&in_creds, 0, sizeof(in_creds));
   1500     in_creds.server = rk_UNCONST(inprinc);
   1501 
   1502     if (_krb5_have_debug(context, 5)) {
   1503         char *unparsed;
   1504 
   1505         ret = krb5_unparse_name(context, in_creds.server, &unparsed);
   1506         if (ret) {
   1507             _krb5_debug(context, 5, "krb5_get_creds: unable to display "
   1508                         "requested service principal");
   1509         } else {
   1510             _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket "
   1511                         "for %s", unparsed);
   1512             free(unparsed);
   1513         }
   1514     }
   1515 
   1516     if (opt && opt->enctype) {
   1517 	ret = krb5_enctype_valid(context, opt->enctype);
   1518 	if (ret)
   1519 	    return ret;
   1520     }
   1521 
   1522     ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
   1523     if (ret)
   1524 	return ret;
   1525 
   1526     if (opt)
   1527 	options = opt->options;
   1528     else
   1529 	options = 0;
   1530     flags.i = 0;
   1531 
   1532     *out_creds = NULL;
   1533     res_creds = calloc(1, sizeof(*res_creds));
   1534     if (res_creds == NULL) {
   1535 	krb5_free_principal(context, in_creds.client);
   1536 	return krb5_enomem(context);
   1537     }
   1538 
   1539     if (opt && opt->enctype) {
   1540 	in_creds.session.keytype = opt->enctype;
   1541 	options |= KRB5_TC_MATCH_KEYTYPE;
   1542     }
   1543 
   1544     ret = krb5_name_canon_iterator_start(context, in_creds.server,
   1545 					 &name_canon_iter);
   1546     if (ret)
   1547 	goto out;
   1548 
   1549 next_rule:
   1550     ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ,
   1551                                   &rule_opts);
   1552     in_creds.server = rk_UNCONST(try_princ);
   1553     if (ret)
   1554 	goto out;
   1555 
   1556     if (name_canon_iter == NULL) {
   1557 	if (options & KRB5_GC_CACHED)
   1558 	    ret = KRB5_CC_NOTFOUND;
   1559 	else
   1560 	    ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
   1561 	goto out;
   1562     }
   1563 
   1564     ret = check_cc(context, options, ccache, &in_creds, res_creds);
   1565     if (ret == 0) {
   1566 	*out_creds = res_creds;
   1567         res_creds = NULL;
   1568 	goto out;
   1569     } else if (ret != KRB5_CC_END) {
   1570 	goto out;
   1571     }
   1572     if (options & KRB5_GC_CACHED)
   1573 	goto next_rule;
   1574 
   1575     type = krb5_principal_get_type(context, try_princ);
   1576     comp = krb5_principal_get_comp_string(context, try_princ, 0);
   1577     if ((type == KRB5_NT_SRV_HST || type == KRB5_NT_UNKNOWN) &&
   1578         comp != NULL && strcmp(comp, "host") == 0)
   1579 	flags.b.canonicalize = 1;
   1580     if (rule_opts & KRB5_NCRO_NO_REFERRALS)
   1581 	flags.b.canonicalize = 0;
   1582     else
   1583 	flags.b.canonicalize = (options & KRB5_GC_CANONICALIZE) ? 1 : 0;
   1584     if (options & KRB5_GC_USER_USER) {
   1585 	flags.b.enc_tkt_in_skey = 1;
   1586 	options |= KRB5_GC_NO_STORE;
   1587     }
   1588     if (options & KRB5_GC_FORWARDABLE)
   1589 	flags.b.forwardable = 1;
   1590     if (options & KRB5_GC_NO_TRANSIT_CHECK)
   1591 	flags.b.disable_transited_check = 1;
   1592     if (options & KRB5_GC_CONSTRAINED_DELEGATION)
   1593 	flags.b.cname_in_addl_tkt = 1;
   1594     if (options & KRB5_GC_ANONYMOUS)
   1595 	flags.b.request_anonymous = 1;
   1596 
   1597     tgts = NULL;
   1598     ret = _krb5_get_cred_kdc_any(context, flags, ccache,
   1599 				 &in_creds, opt ? opt->self : 0,
   1600 				 opt ? opt->ticket : 0, out_creds,
   1601 				 &tgts);
   1602     for (i = 0; tgts && tgts[i]; i++) {
   1603 	if ((options & KRB5_GC_NO_STORE) == 0)
   1604 	    krb5_cc_store_cred(context, ccache, tgts[i]);
   1605 	krb5_free_creds(context, tgts[i]);
   1606     }
   1607     free(tgts);
   1608 
   1609     /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */
   1610     if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN &&
   1611 	!(rule_opts & KRB5_NCRO_USE_FAST))
   1612 	goto next_rule;
   1613 
   1614     if (ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
   1615 	store_cred(context, ccache, inprinc, *out_creds);
   1616 
   1617     if (ret == 0 && _krb5_have_debug(context, 5)) {
   1618         char *unparsed;
   1619 
   1620         ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed);
   1621         if (ret) {
   1622             _krb5_debug(context, 5, "krb5_get_creds: unable to display "
   1623                         "service principal");
   1624         } else {
   1625             _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s",
   1626                         unparsed);
   1627             free(unparsed);
   1628         }
   1629     }
   1630 
   1631 out:
   1632     krb5_free_creds(context, res_creds);
   1633     krb5_free_principal(context, in_creds.client);
   1634     krb5_free_name_canon_iterator(context, name_canon_iter);
   1635     if (ret)
   1636 	return not_found(context, inprinc, ret);
   1637     return ret;
   1638 }
   1639 
   1640 /*
   1641  *
   1642  */
   1643 
   1644 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1645 krb5_get_renewed_creds(krb5_context context,
   1646 		       krb5_creds *creds,
   1647 		       krb5_const_principal client,
   1648 		       krb5_ccache ccache,
   1649 		       const char *in_tkt_service)
   1650 {
   1651     krb5_error_code ret;
   1652     krb5_kdc_flags flags;
   1653     krb5_creds in, *template, *out = NULL;
   1654 
   1655     memset(&in, 0, sizeof(in));
   1656     memset(creds, 0, sizeof(*creds));
   1657 
   1658     ret = krb5_copy_principal(context, client, &in.client);
   1659     if (ret)
   1660 	return ret;
   1661 
   1662     if (in_tkt_service) {
   1663 	ret = krb5_parse_name(context, in_tkt_service, &in.server);
   1664 	if (ret) {
   1665 	    krb5_free_principal(context, in.client);
   1666 	    return ret;
   1667 	}
   1668     } else {
   1669 	const char *realm = krb5_principal_get_realm(context, client);
   1670 
   1671 	ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
   1672 				  realm, NULL);
   1673 	if (ret) {
   1674 	    krb5_free_principal(context, in.client);
   1675 	    return ret;
   1676 	}
   1677     }
   1678 
   1679     flags.i = 0;
   1680     flags.b.renewable = flags.b.renew = 1;
   1681 
   1682     /*
   1683      * Get template from old credential cache for the same entry, if
   1684      * this failes, no worries.
   1685      */
   1686     ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
   1687     if (ret == 0) {
   1688 	flags.b.forwardable = template->flags.b.forwardable;
   1689 	flags.b.proxiable = template->flags.b.proxiable;
   1690 	krb5_free_creds (context, template);
   1691     }
   1692 
   1693     ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
   1694     krb5_free_principal(context, in.client);
   1695     krb5_free_principal(context, in.server);
   1696     if (ret)
   1697 	return ret;
   1698 
   1699     ret = krb5_copy_creds_contents(context, out, creds);
   1700     krb5_free_creds(context, out);
   1701 
   1702     return ret;
   1703 }
   1704