1 /*	$NetBSD: get_cred.c,v 1.1.1.2 2014/04/24 12:45:50 pettai Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
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
make_pa_tgs_req(krb5_context context,krb5_auth_context ac,KDC_REQ_BODY * body,PA_DATA * padata,krb5_creds * creds)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
set_auth_data(krb5_context context,KDC_REQ_BODY * req_body,krb5_authdata * authdata,krb5_keyblock * subkey)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 	    krb5_set_error_message(context, ENOMEM,
111 				   N_("malloc: out of memory", ""));
112 	    return ENOMEM;
113 	}
114 	ret = krb5_crypto_init(context, subkey, 0, &crypto);
115 	if (ret) {
116 	    free (buf);
117 	    free (req_body->enc_authorization_data);
118 	    req_body->enc_authorization_data = NULL;
119 	    return ret;
120 	}
121 	krb5_encrypt_EncryptedData(context,
122 				   crypto,
123 				   KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
124 				   buf,
125 				   len,
126 				   0,
127 				   req_body->enc_authorization_data);
128 	free (buf);
129 	krb5_crypto_destroy(context, crypto);
130     } else {
131 	req_body->enc_authorization_data = NULL;
132     }
133     return 0;
134 }
135 
136 /*
137  * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
138  * (if not-NULL), `in_creds', `krbtgt', and returning the generated
139  * subkey in `subkey'.
140  */
141 
142 static krb5_error_code
init_tgs_req(krb5_context context,krb5_ccache ccache,krb5_addresses * addresses,krb5_kdc_flags flags,Ticket * second_ticket,krb5_creds * in_creds,krb5_creds * krbtgt,unsigned nonce,const METHOD_DATA * padata,krb5_keyblock ** subkey,TGS_REQ * t)143 init_tgs_req (krb5_context context,
144 	      krb5_ccache ccache,
145 	      krb5_addresses *addresses,
146 	      krb5_kdc_flags flags,
147 	      Ticket *second_ticket,
148 	      krb5_creds *in_creds,
149 	      krb5_creds *krbtgt,
150 	      unsigned nonce,
151 	      const METHOD_DATA *padata,
152 	      krb5_keyblock **subkey,
153 	      TGS_REQ *t)
154 {
155     krb5_auth_context ac = NULL;
156     krb5_error_code ret = 0;
157 
158     memset(t, 0, sizeof(*t));
159     t->pvno = 5;
160     t->msg_type = krb_tgs_req;
161     if (in_creds->session.keytype) {
162 	ALLOC_SEQ(&t->req_body.etype, 1);
163 	if(t->req_body.etype.val == NULL) {
164 	    ret = ENOMEM;
165 	    krb5_set_error_message(context, ret,
166 				   N_("malloc: out of memory", ""));
167 	    goto fail;
168 	}
169 	t->req_body.etype.val[0] = in_creds->session.keytype;
170     } else {
171 	ret = _krb5_init_etype(context,
172 			       KRB5_PDU_TGS_REQUEST,
173 			       &t->req_body.etype.len,
174 			       &t->req_body.etype.val,
175 			       NULL);
176     }
177     if (ret)
178 	goto fail;
179     t->req_body.addresses = addresses;
180     t->req_body.kdc_options = flags.b;
181     t->req_body.kdc_options.forwardable = krbtgt->flags.b.forwardable;
182     t->req_body.kdc_options.renewable = krbtgt->flags.b.renewable;
183     t->req_body.kdc_options.proxiable = krbtgt->flags.b.proxiable;
184     ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
185     if (ret)
186 	goto fail;
187     ALLOC(t->req_body.sname, 1);
188     if (t->req_body.sname == NULL) {
189 	ret = ENOMEM;
190 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
191 	goto fail;
192     }
193 
194     /* some versions of some code might require that the client be
195        present in TGS-REQs, but this is clearly against the spec */
196 
197     ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
198     if (ret)
199 	goto fail;
200 
201     if (krbtgt->times.starttime) {
202         ALLOC(t->req_body.from, 1);
203         if(t->req_body.from == NULL){
204             ret = krb5_enomem(context);
205             goto fail;
206         }
207         *t->req_body.from = in_creds->times.starttime;
208     }
209 
210     /* req_body.till should be NULL if there is no endtime specified,
211        but old MIT code (like DCE secd) doesn't like that */
212     ALLOC(t->req_body.till, 1);
213     if(t->req_body.till == NULL){
214 	ret = ENOMEM;
215 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
216 	goto fail;
217     }
218     *t->req_body.till = in_creds->times.endtime;
219 
220     if (t->req_body.kdc_options.renewable && krbtgt->times.renew_till) {
221         ALLOC(t->req_body.rtime, 1);
222         if(t->req_body.rtime == NULL){
223             ret = krb5_enomem(context);
224             goto fail;
225         }
226         *t->req_body.rtime = in_creds->times.renew_till;
227     }
228 
229     t->req_body.nonce = nonce;
230     if(second_ticket){
231 	ALLOC(t->req_body.additional_tickets, 1);
232 	if (t->req_body.additional_tickets == NULL) {
233 	    ret = ENOMEM;
234 	    krb5_set_error_message(context, ret,
235 				   N_("malloc: out of memory", ""));
236 	    goto fail;
237 	}
238 	ALLOC_SEQ(t->req_body.additional_tickets, 1);
239 	if (t->req_body.additional_tickets->val == NULL) {
240 	    ret = ENOMEM;
241 	    krb5_set_error_message(context, ret,
242 				   N_("malloc: out of memory", ""));
243 	    goto fail;
244 	}
245 	ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
246 	if (ret)
247 	    goto fail;
248     }
249     ALLOC(t->padata, 1);
250     if (t->padata == NULL) {
251 	ret = ENOMEM;
252 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
253 	goto fail;
254     }
255     ALLOC_SEQ(t->padata, 1 + padata->len);
256     if (t->padata->val == NULL) {
257 	ret = ENOMEM;
258 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
259 	goto fail;
260     }
261     {
262 	size_t i;
263 	for (i = 0; i < padata->len; i++) {
264 	    ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
265 	    if (ret) {
266 		krb5_set_error_message(context, ret,
267 				       N_("malloc: out of memory", ""));
268 		goto fail;
269 	    }
270 	}
271     }
272 
273     ret = krb5_auth_con_init(context, &ac);
274     if(ret)
275 	goto fail;
276 
277     ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session);
278     if (ret)
279 	goto fail;
280 
281     ret = set_auth_data (context, &t->req_body, &in_creds->authdata,
282 			 ac->local_subkey);
283     if (ret)
284 	goto fail;
285 
286     ret = make_pa_tgs_req(context,
287 			  ac,
288 			  &t->req_body,
289 			  &t->padata->val[0],
290 			  krbtgt);
291     if(ret)
292 	goto fail;
293 
294     ret = krb5_auth_con_getlocalsubkey(context, ac, subkey);
295     if (ret)
296 	goto fail;
297 
298 fail:
299     if (ac)
300 	krb5_auth_con_free(context, ac);
301     if (ret) {
302 	t->req_body.addresses = NULL;
303 	free_TGS_REQ (t);
304     }
305     return ret;
306 }
307 
308 krb5_error_code
_krb5_get_krbtgt(krb5_context context,krb5_ccache id,krb5_realm realm,krb5_creds ** cred)309 _krb5_get_krbtgt(krb5_context context,
310 		 krb5_ccache  id,
311 		 krb5_realm realm,
312 		 krb5_creds **cred)
313 {
314     krb5_error_code ret;
315     krb5_creds tmp_cred;
316 
317     memset(&tmp_cred, 0, sizeof(tmp_cred));
318 
319     ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
320     if (ret)
321 	return ret;
322 
323     ret = krb5_make_principal(context,
324 			      &tmp_cred.server,
325 			      realm,
326 			      KRB5_TGS_NAME,
327 			      realm,
328 			      NULL);
329     if(ret) {
330 	krb5_free_principal(context, tmp_cred.client);
331 	return ret;
332     }
333     ret = krb5_get_credentials(context,
334 			       KRB5_GC_CACHED,
335 			       id,
336 			       &tmp_cred,
337 			       cred);
338     krb5_free_principal(context, tmp_cred.client);
339     krb5_free_principal(context, tmp_cred.server);
340     if(ret)
341 	return ret;
342     return 0;
343 }
344 
345 /* DCE compatible decrypt proc */
346 static krb5_error_code KRB5_CALLCONV
decrypt_tkt_with_subkey(krb5_context context,krb5_keyblock * key,krb5_key_usage usage,krb5_const_pointer skey,krb5_kdc_rep * dec_rep)347 decrypt_tkt_with_subkey (krb5_context context,
348 			 krb5_keyblock *key,
349 			 krb5_key_usage usage,
350 			 krb5_const_pointer skey,
351 			 krb5_kdc_rep *dec_rep)
352 {
353     const krb5_keyblock *subkey = skey;
354     krb5_error_code ret = 0;
355     krb5_data data;
356     size_t size;
357     krb5_crypto crypto;
358 
359     assert(usage == 0);
360 
361     krb5_data_zero(&data);
362 
363     /*
364      * start out with trying with subkey if we have one
365      */
366     if (subkey) {
367 	ret = krb5_crypto_init(context, subkey, 0, &crypto);
368 	if (ret)
369 	    return ret;
370 	ret = krb5_decrypt_EncryptedData (context,
371 					  crypto,
372 					  KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
373 					  &dec_rep->kdc_rep.enc_part,
374 					  &data);
375 	/*
376 	 * If the is Windows 2000 DC, we need to retry with key usage
377 	 * 8 when doing ARCFOUR.
378 	 */
379 	if (ret && subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) {
380 	    ret = krb5_decrypt_EncryptedData(context,
381 					     crypto,
382 					     8,
383 					     &dec_rep->kdc_rep.enc_part,
384 					     &data);
385 	}
386 	krb5_crypto_destroy(context, crypto);
387     }
388     if (subkey == NULL || ret) {
389 	ret = krb5_crypto_init(context, key, 0, &crypto);
390 	if (ret)
391 	    return ret;
392 	ret = krb5_decrypt_EncryptedData (context,
393 					  crypto,
394 					  KRB5_KU_TGS_REP_ENC_PART_SESSION,
395 					  &dec_rep->kdc_rep.enc_part,
396 					  &data);
397 	krb5_crypto_destroy(context, crypto);
398     }
399     if (ret)
400 	return ret;
401 
402     ret = decode_EncASRepPart(data.data,
403 			      data.length,
404 			      &dec_rep->enc_part,
405 			      &size);
406     if (ret)
407 	ret = decode_EncTGSRepPart(data.data,
408 				   data.length,
409 				   &dec_rep->enc_part,
410 				   &size);
411     if (ret)
412       krb5_set_error_message(context, ret,
413 			     N_("Failed to decode encpart in ticket", ""));
414     krb5_data_free (&data);
415     return ret;
416 }
417 
418 static krb5_error_code
get_cred_kdc(krb5_context context,krb5_ccache id,krb5_kdc_flags flags,krb5_addresses * addresses,krb5_creds * in_creds,krb5_creds * krbtgt,krb5_principal impersonate_principal,Ticket * second_ticket,krb5_creds * out_creds)419 get_cred_kdc(krb5_context context,
420 	     krb5_ccache id,
421 	     krb5_kdc_flags flags,
422 	     krb5_addresses *addresses,
423 	     krb5_creds *in_creds,
424 	     krb5_creds *krbtgt,
425 	     krb5_principal impersonate_principal,
426 	     Ticket *second_ticket,
427 	     krb5_creds *out_creds)
428 {
429     TGS_REQ req;
430     krb5_data enc;
431     krb5_data resp;
432     krb5_kdc_rep rep;
433     KRB_ERROR error;
434     krb5_error_code ret;
435     unsigned nonce;
436     krb5_keyblock *subkey = NULL;
437     size_t len = 0;
438     Ticket second_ticket_data;
439     METHOD_DATA padata;
440 
441     krb5_data_zero(&resp);
442     krb5_data_zero(&enc);
443     padata.val = NULL;
444     padata.len = 0;
445 
446     krb5_generate_random_block(&nonce, sizeof(nonce));
447     nonce &= 0xffffffff;
448 
449     if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
450 	ret = decode_Ticket(in_creds->second_ticket.data,
451 			    in_creds->second_ticket.length,
452 			    &second_ticket_data, &len);
453 	if(ret)
454 	    return ret;
455 	second_ticket = &second_ticket_data;
456     }
457 
458 
459     if (impersonate_principal) {
460 	krb5_crypto crypto;
461 	PA_S4U2Self self;
462 	krb5_data data;
463 	void *buf;
464 	size_t size = 0;
465 
466 	self.name = impersonate_principal->name;
467 	self.realm = impersonate_principal->realm;
468 	self.auth = estrdup("Kerberos");
469 
470 	ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
471 	if (ret) {
472 	    free(self.auth);
473 	    goto out;
474 	}
475 
476 	ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
477 	if (ret) {
478 	    free(self.auth);
479 	    krb5_data_free(&data);
480 	    goto out;
481 	}
482 
483 	ret = krb5_create_checksum(context,
484 				   crypto,
485 				   KRB5_KU_OTHER_CKSUM,
486 				   0,
487 				   data.data,
488 				   data.length,
489 				   &self.cksum);
490 	krb5_crypto_destroy(context, crypto);
491 	krb5_data_free(&data);
492 	if (ret) {
493 	    free(self.auth);
494 	    goto out;
495 	}
496 
497 	ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
498 	free(self.auth);
499 	free_Checksum(&self.cksum);
500 	if (ret)
501 	    goto out;
502 	if (len != size)
503 	    krb5_abortx(context, "internal asn1 error");
504 
505 	ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);
506 	if (ret)
507 	    goto out;
508     }
509 
510     ret = init_tgs_req (context,
511 			id,
512 			addresses,
513 			flags,
514 			second_ticket,
515 			in_creds,
516 			krbtgt,
517 			nonce,
518 			&padata,
519 			&subkey,
520 			&req);
521     if (ret)
522 	goto out;
523 
524     ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
525     if (ret)
526 	goto out;
527     if(enc.length != len)
528 	krb5_abortx(context, "internal error in ASN.1 encoder");
529 
530     /* don't free addresses */
531     req.req_body.addresses = NULL;
532     free_TGS_REQ(&req);
533 
534     /*
535      * Send and receive
536      */
537     {
538 	krb5_sendto_ctx stctx;
539 	ret = krb5_sendto_ctx_alloc(context, &stctx);
540 	if (ret)
541 	    return ret;
542 	krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
543 
544 	ret = krb5_sendto_context (context, stctx, &enc,
545 				   krbtgt->server->name.name_string.val[1],
546 				   &resp);
547 	krb5_sendto_ctx_free(context, stctx);
548     }
549     if(ret)
550 	goto out;
551 
552     memset(&rep, 0, sizeof(rep));
553     if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
554 	unsigned eflags = 0;
555 
556 	ret = krb5_copy_principal(context,
557 				  in_creds->client,
558 				  &out_creds->client);
559 	if(ret)
560 	    goto out2;
561 	ret = krb5_copy_principal(context,
562 				  in_creds->server,
563 				  &out_creds->server);
564 	if(ret)
565 	    goto out2;
566 	/* this should go someplace else */
567 	out_creds->times.endtime = in_creds->times.endtime;
568 
569 	/* XXX should do better testing */
570 	if (flags.b.constrained_delegation || impersonate_principal)
571 	    eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
572 
573 	ret = _krb5_extract_ticket(context,
574 				   &rep,
575 				   out_creds,
576 				   &krbtgt->session,
577 				   NULL,
578 				   0,
579 				   &krbtgt->addresses,
580 				   nonce,
581 				   eflags,
582 				   decrypt_tkt_with_subkey,
583 				   subkey);
584     out2:
585 	krb5_free_kdc_rep(context, &rep);
586     } else if(krb5_rd_error(context, &resp, &error) == 0) {
587 	ret = krb5_error_from_rd_error(context, &error, in_creds);
588 	krb5_free_error_contents(context, &error);
589     } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
590 	ret = KRB5KRB_AP_ERR_V4_REPLY;
591 	krb5_clear_error_message(context);
592     } else {
593 	ret = KRB5KRB_AP_ERR_MSG_TYPE;
594 	krb5_clear_error_message(context);
595     }
596 
597 out:
598     if (second_ticket == &second_ticket_data)
599 	free_Ticket(&second_ticket_data);
600     free_METHOD_DATA(&padata);
601     krb5_data_free(&resp);
602     krb5_data_free(&enc);
603     if(subkey)
604 	krb5_free_keyblock(context, subkey);
605     return ret;
606 
607 }
608 
609 /*
610  * same as above, just get local addresses first if the krbtgt have
611  * them and the realm is not addressless
612  */
613 
614 static krb5_error_code
get_cred_kdc_address(krb5_context context,krb5_ccache id,krb5_kdc_flags flags,krb5_addresses * addrs,krb5_creds * in_creds,krb5_creds * krbtgt,krb5_principal impersonate_principal,Ticket * second_ticket,krb5_creds * out_creds)615 get_cred_kdc_address(krb5_context context,
616 		     krb5_ccache id,
617 		     krb5_kdc_flags flags,
618 		     krb5_addresses *addrs,
619 		     krb5_creds *in_creds,
620 		     krb5_creds *krbtgt,
621 		     krb5_principal impersonate_principal,
622 		     Ticket *second_ticket,
623 		     krb5_creds *out_creds)
624 {
625     krb5_error_code ret;
626     krb5_addresses addresses = { 0, NULL };
627 
628     /*
629      * Inherit the address-ness of the krbtgt if the address is not
630      * specified.
631      */
632 
633     if (addrs == NULL && krbtgt->addresses.len != 0) {
634 	krb5_boolean noaddr;
635 
636 	krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
637 				"no-addresses", FALSE, &noaddr);
638 
639 	if (!noaddr) {
640 	    krb5_get_all_client_addrs(context, &addresses);
641 	    /* XXX this sucks. */
642 	    addrs = &addresses;
643 	    if(addresses.len == 0)
644 		addrs = NULL;
645 	}
646     }
647     ret = get_cred_kdc(context, id, flags, addrs, in_creds,
648 		       krbtgt, impersonate_principal,
649 		       second_ticket, out_creds);
650     krb5_free_addresses(context, &addresses);
651     return ret;
652 }
653 
654 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_kdc_cred(krb5_context context,krb5_ccache id,krb5_kdc_flags flags,krb5_addresses * addresses,Ticket * second_ticket,krb5_creds * in_creds,krb5_creds ** out_creds)655 krb5_get_kdc_cred(krb5_context context,
656 		  krb5_ccache id,
657 		  krb5_kdc_flags flags,
658 		  krb5_addresses *addresses,
659 		  Ticket  *second_ticket,
660 		  krb5_creds *in_creds,
661 		  krb5_creds **out_creds
662 		  )
663 {
664     krb5_error_code ret;
665     krb5_creds *krbtgt;
666 
667     *out_creds = calloc(1, sizeof(**out_creds));
668     if(*out_creds == NULL) {
669 	krb5_set_error_message(context, ENOMEM,
670 			       N_("malloc: out of memory", ""));
671 	return ENOMEM;
672     }
673     ret = _krb5_get_krbtgt (context,
674 			    id,
675 			    in_creds->server->realm,
676 			    &krbtgt);
677     if(ret) {
678 	free(*out_creds);
679 	*out_creds = NULL;
680 	return ret;
681     }
682     ret = get_cred_kdc(context, id, flags, addresses,
683 		       in_creds, krbtgt, NULL, NULL, *out_creds);
684     krb5_free_creds (context, krbtgt);
685     if(ret) {
686 	free(*out_creds);
687 	*out_creds = NULL;
688     }
689     return ret;
690 }
691 
692 static int
not_found(krb5_context context,krb5_const_principal p,krb5_error_code code)693 not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
694 {
695     krb5_error_code ret;
696     char *str;
697 
698     ret = krb5_unparse_name(context, p, &str);
699     if(ret) {
700 	krb5_clear_error_message(context);
701 	return code;
702     }
703     krb5_set_error_message(context, code,
704 			   N_("Matching credential (%s) not found", ""), str);
705     free(str);
706     return code;
707 }
708 
709 static krb5_error_code
find_cred(krb5_context context,krb5_ccache id,krb5_principal server,krb5_creds ** tgts,krb5_creds * out_creds)710 find_cred(krb5_context context,
711 	  krb5_ccache id,
712 	  krb5_principal server,
713 	  krb5_creds **tgts,
714 	  krb5_creds *out_creds)
715 {
716     krb5_error_code ret;
717     krb5_creds mcreds;
718 
719     krb5_cc_clear_mcred(&mcreds);
720     mcreds.server = server;
721     ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
722 				&mcreds, out_creds);
723     if(ret == 0)
724 	return 0;
725     while(tgts && *tgts){
726 	if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
727 			      &mcreds, *tgts)){
728 	    ret = krb5_copy_creds_contents(context, *tgts, out_creds);
729 	    return ret;
730 	}
731 	tgts++;
732     }
733     return not_found(context, server, KRB5_CC_NOTFOUND);
734 }
735 
736 static krb5_error_code
add_cred(krb5_context context,krb5_creds const * tkt,krb5_creds *** tgts)737 add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
738 {
739     int i;
740     krb5_error_code ret;
741     krb5_creds **tmp = *tgts;
742 
743     for(i = 0; tmp && tmp[i]; i++); /* XXX */
744     tmp = realloc(tmp, (i+2)*sizeof(*tmp));
745     if(tmp == NULL) {
746 	krb5_set_error_message(context, ENOMEM,
747 			       N_("malloc: out of memory", ""));
748 	return ENOMEM;
749     }
750     *tgts = tmp;
751     ret = krb5_copy_creds(context, tkt, &tmp[i]);
752     tmp[i+1] = NULL;
753     return ret;
754 }
755 
756 static krb5_error_code
get_cred_kdc_capath_worker(krb5_context context,krb5_kdc_flags flags,krb5_ccache ccache,krb5_creds * in_creds,krb5_const_realm try_realm,krb5_principal impersonate_principal,Ticket * second_ticket,krb5_creds ** out_creds,krb5_creds *** ret_tgts)757 get_cred_kdc_capath_worker(krb5_context context,
758                            krb5_kdc_flags flags,
759                            krb5_ccache ccache,
760                            krb5_creds *in_creds,
761                            krb5_const_realm try_realm,
762                            krb5_principal impersonate_principal,
763                            Ticket *second_ticket,
764                            krb5_creds **out_creds,
765                            krb5_creds ***ret_tgts)
766 {
767     krb5_error_code ret;
768     krb5_creds *tgt, tmp_creds;
769     krb5_const_realm client_realm, server_realm;
770     int ok_as_delegate = 1;
771 
772     *out_creds = NULL;
773 
774     client_realm = krb5_principal_get_realm(context, in_creds->client);
775     server_realm = krb5_principal_get_realm(context, in_creds->server);
776     memset(&tmp_creds, 0, sizeof(tmp_creds));
777     ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
778     if(ret)
779 	return ret;
780 
781     ret = krb5_make_principal(context,
782 			      &tmp_creds.server,
783 			      try_realm,
784 			      KRB5_TGS_NAME,
785 			      server_realm,
786 			      NULL);
787     if(ret){
788 	krb5_free_principal(context, tmp_creds.client);
789 	return ret;
790     }
791     {
792 	krb5_creds tgts;
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 || strcmp(try_realm, server_realm) != 0)
799 		ok_as_delegate = tgts.flags.b.ok_as_delegate;
800 
801 	    *out_creds = calloc(1, sizeof(**out_creds));
802 	    if(*out_creds == NULL) {
803 		ret = ENOMEM;
804 		krb5_set_error_message(context, ret,
805 				       N_("malloc: out of memory", ""));
806 	    } else {
807 		ret = get_cred_kdc_address(context, ccache, flags, NULL,
808 					   in_creds, &tgts,
809 					   impersonate_principal,
810 					   second_ticket,
811 					   *out_creds);
812 		if (ret) {
813 		    free (*out_creds);
814 		    *out_creds = NULL;
815 		} else if (ok_as_delegate == 0)
816 		    (*out_creds)->flags.b.ok_as_delegate = 0;
817 	    }
818 	    krb5_free_cred_contents(context, &tgts);
819 	    krb5_free_principal(context, tmp_creds.server);
820 	    krb5_free_principal(context, tmp_creds.client);
821 	    return ret;
822 	}
823     }
824     if(krb5_realm_compare(context, in_creds->client, in_creds->server))
825 	return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
826 
827     /* XXX this can loop forever */
828     while(1){
829 	heim_general_string tgt_inst;
830 
831 	ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
832 				  NULL, NULL, &tgt, ret_tgts);
833 	if(ret) {
834 	    krb5_free_principal(context, tmp_creds.server);
835 	    krb5_free_principal(context, tmp_creds.client);
836 	    return ret;
837 	}
838 	/*
839 	 * if either of the chain or the ok_as_delegate was stripped
840 	 * by the kdc, make sure we strip it too.
841 	 */
842 	if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
843 	    ok_as_delegate = 0;
844 	    tgt->flags.b.ok_as_delegate = 0;
845 	}
846 
847 	ret = add_cred(context, tgt, ret_tgts);
848 	if(ret) {
849 	    krb5_free_principal(context, tmp_creds.server);
850 	    krb5_free_principal(context, tmp_creds.client);
851 	    return ret;
852 	}
853 	tgt_inst = tgt->server->name.name_string.val[1];
854 	if(strcmp(tgt_inst, server_realm) == 0)
855 	    break;
856 	krb5_free_principal(context, tmp_creds.server);
857 	ret = krb5_make_principal(context, &tmp_creds.server,
858 				  tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
859 	if(ret) {
860 	    krb5_free_principal(context, tmp_creds.server);
861 	    krb5_free_principal(context, tmp_creds.client);
862 	    return ret;
863 	}
864 	ret = krb5_free_creds(context, tgt);
865 	if(ret) {
866 	    krb5_free_principal(context, tmp_creds.server);
867 	    krb5_free_principal(context, tmp_creds.client);
868 	    return ret;
869 	}
870     }
871 
872     krb5_free_principal(context, tmp_creds.server);
873     krb5_free_principal(context, tmp_creds.client);
874     *out_creds = calloc(1, sizeof(**out_creds));
875     if(*out_creds == NULL) {
876 	ret = ENOMEM;
877 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
878     } else {
879 	ret = get_cred_kdc_address (context, ccache, flags, NULL,
880 				    in_creds, tgt, impersonate_principal,
881 				    second_ticket, *out_creds);
882 	if (ret) {
883 	    free (*out_creds);
884 	    *out_creds = NULL;
885 	}
886     }
887     krb5_free_creds(context, tgt);
888     return ret;
889 }
890 
891 /*
892 get_cred(server)
893 	creds = cc_get_cred(server)
894 	if(creds) return creds
895 	tgt = cc_get_cred(krbtgt/server_realm@any_realm)
896 	if(tgt)
897 		return get_cred_tgt(server, tgt)
898 	if(client_realm == server_realm)
899 		return NULL
900 	tgt = get_cred(krbtgt/server_realm@client_realm)
901 	while(tgt_inst != server_realm)
902 		tgt = get_cred(krbtgt/server_realm@tgt_inst)
903 	return get_cred_tgt(server, tgt)
904 	*/
905 
906 static krb5_error_code
get_cred_kdc_capath(krb5_context context,krb5_kdc_flags flags,krb5_ccache ccache,krb5_creds * in_creds,krb5_principal impersonate_principal,Ticket * second_ticket,krb5_creds ** out_creds,krb5_creds *** ret_tgts)907 get_cred_kdc_capath(krb5_context context,
908 		    krb5_kdc_flags flags,
909 		    krb5_ccache ccache,
910 		    krb5_creds *in_creds,
911 		    krb5_principal impersonate_principal,
912 		    Ticket *second_ticket,
913 		    krb5_creds **out_creds,
914 		    krb5_creds ***ret_tgts)
915 {
916     krb5_error_code ret;
917     krb5_const_realm client_realm, server_realm, try_realm;
918 
919     client_realm = krb5_principal_get_realm(context, in_creds->client);
920     server_realm = krb5_principal_get_realm(context, in_creds->server);
921 
922     try_realm = client_realm;
923     ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, try_realm,
924                                      impersonate_principal, second_ticket, out_creds,
925                                      ret_tgts);
926 
927     if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
928         try_realm = krb5_config_get_string(context, NULL, "capaths",
929                                            client_realm, server_realm, NULL);
930 
931         if (try_realm != NULL && strcmp(try_realm, client_realm)) {
932             ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds,
933                                              try_realm, impersonate_principal,
934                                              second_ticket, out_creds, ret_tgts);
935         }
936     }
937 
938     return ret;
939 }
940 
941 static krb5_error_code
get_cred_kdc_referral(krb5_context context,krb5_kdc_flags flags,krb5_ccache ccache,krb5_creds * in_creds,krb5_principal impersonate_principal,Ticket * second_ticket,krb5_creds ** out_creds,krb5_creds *** ret_tgts)942 get_cred_kdc_referral(krb5_context context,
943 		      krb5_kdc_flags flags,
944 		      krb5_ccache ccache,
945 		      krb5_creds *in_creds,
946 		      krb5_principal impersonate_principal,
947 		      Ticket *second_ticket,
948 		      krb5_creds **out_creds,
949 		      krb5_creds ***ret_tgts)
950 {
951     krb5_const_realm client_realm;
952     krb5_error_code ret;
953     krb5_creds tgt, referral, ticket;
954     int loop = 0;
955     int ok_as_delegate = 1;
956 
957     if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) {
958 	krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED,
959 			       N_("Name too short to do referals, skipping", ""));
960 	return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
961     }
962 
963     memset(&tgt, 0, sizeof(tgt));
964     memset(&ticket, 0, sizeof(ticket));
965 
966     flags.b.canonicalize = 1;
967 
968     *out_creds = NULL;
969 
970     client_realm = krb5_principal_get_realm(context, in_creds->client);
971 
972     /* find tgt for the clients base realm */
973     {
974 	krb5_principal tgtname;
975 
976 	ret = krb5_make_principal(context, &tgtname,
977 				  client_realm,
978 				  KRB5_TGS_NAME,
979 				  client_realm,
980 				  NULL);
981 	if(ret)
982 	    return ret;
983 
984 	ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt);
985 	krb5_free_principal(context, tgtname);
986 	if (ret)
987 	    return ret;
988     }
989 
990     referral = *in_creds;
991     ret = krb5_copy_principal(context, in_creds->server, &referral.server);
992     if (ret) {
993 	krb5_free_cred_contents(context, &tgt);
994 	return ret;
995     }
996     ret = krb5_principal_set_realm(context, referral.server, client_realm);
997     if (ret) {
998 	krb5_free_cred_contents(context, &tgt);
999 	krb5_free_principal(context, referral.server);
1000 	return ret;
1001     }
1002 
1003     while (loop++ < 17) {
1004 	krb5_creds **tickets;
1005 	krb5_creds mcreds;
1006 	char *referral_realm;
1007 
1008 	/* Use cache if we are not doing impersonation or contrainte deleg */
1009 	if (impersonate_principal == NULL || flags.b.constrained_delegation) {
1010 	    krb5_cc_clear_mcred(&mcreds);
1011 	    mcreds.server = referral.server;
1012 	    ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket);
1013 	} else
1014 	    ret = EINVAL;
1015 
1016 	if (ret) {
1017 	    ret = get_cred_kdc_address(context, ccache, flags, NULL,
1018 				       &referral, &tgt, impersonate_principal,
1019 				       second_ticket, &ticket);
1020 	    if (ret)
1021 		goto out;
1022 	}
1023 
1024 	/* Did we get the right ticket ? */
1025 	if (krb5_principal_compare_any_realm(context,
1026 					     referral.server,
1027 					     ticket.server))
1028 	    break;
1029 
1030 	if (!krb5_principal_is_krbtgt(context, ticket.server)) {
1031 	    krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
1032 				   N_("Got back an non krbtgt "
1033 				      "ticket referrals", ""));
1034 	    ret = KRB5KRB_AP_ERR_NOT_US;
1035 	    goto out;
1036 	}
1037 
1038 	referral_realm = ticket.server->name.name_string.val[1];
1039 
1040 	/* check that there are no referrals loops */
1041 	tickets = *ret_tgts;
1042 
1043 	krb5_cc_clear_mcred(&mcreds);
1044 	mcreds.server = ticket.server;
1045 
1046 	while(tickets && *tickets){
1047 	    if(krb5_compare_creds(context,
1048 				  KRB5_TC_DONT_MATCH_REALM,
1049 				  &mcreds,
1050 				  *tickets))
1051 	    {
1052 		krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1053 				       N_("Referral from %s "
1054 					  "loops back to realm %s", ""),
1055 				       tgt.server->realm,
1056 				       referral_realm);
1057 		ret = KRB5_GET_IN_TKT_LOOP;
1058                 goto out;
1059 	    }
1060 	    tickets++;
1061 	}
1062 
1063 	/*
1064 	 * if either of the chain or the ok_as_delegate was stripped
1065 	 * by the kdc, make sure we strip it too.
1066 	 */
1067 
1068 	if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
1069 	    ok_as_delegate = 0;
1070 	    ticket.flags.b.ok_as_delegate = 0;
1071 	}
1072 
1073 	ret = add_cred(context, &ticket, ret_tgts);
1074 	if (ret)
1075 	    goto out;
1076 
1077 	/* try realm in the referral */
1078 	ret = krb5_principal_set_realm(context,
1079 				       referral.server,
1080 				       referral_realm);
1081 	krb5_free_cred_contents(context, &tgt);
1082 	tgt = ticket;
1083 	memset(&ticket, 0, sizeof(ticket));
1084 	if (ret)
1085 	    goto out;
1086     }
1087 
1088     ret = krb5_copy_creds(context, &ticket, out_creds);
1089 
1090 out:
1091     krb5_free_principal(context, referral.server);
1092     krb5_free_cred_contents(context, &tgt);
1093     krb5_free_cred_contents(context, &ticket);
1094     return ret;
1095 }
1096 
1097 
1098 /*
1099  * Glue function between referrals version and old client chasing
1100  * codebase.
1101  */
1102 
1103 krb5_error_code
_krb5_get_cred_kdc_any(krb5_context context,krb5_kdc_flags flags,krb5_ccache ccache,krb5_creds * in_creds,krb5_principal impersonate_principal,Ticket * second_ticket,krb5_creds ** out_creds,krb5_creds *** ret_tgts)1104 _krb5_get_cred_kdc_any(krb5_context context,
1105 		       krb5_kdc_flags flags,
1106 		       krb5_ccache ccache,
1107 		       krb5_creds *in_creds,
1108 		       krb5_principal impersonate_principal,
1109 		       Ticket *second_ticket,
1110 		       krb5_creds **out_creds,
1111 		       krb5_creds ***ret_tgts)
1112 {
1113     krb5_error_code ret;
1114     krb5_deltat offset;
1115 
1116     ret = krb5_cc_get_kdc_offset(context, ccache, &offset);
1117     if (ret) {
1118 	context->kdc_sec_offset = offset;
1119 	context->kdc_usec_offset = 0;
1120     }
1121 
1122     ret = get_cred_kdc_referral(context,
1123 				flags,
1124 				ccache,
1125 				in_creds,
1126 				impersonate_principal,
1127 				second_ticket,
1128 				out_creds,
1129 				ret_tgts);
1130     if (ret == 0 || flags.b.canonicalize)
1131 	return ret;
1132     return get_cred_kdc_capath(context,
1133 				flags,
1134 				ccache,
1135 				in_creds,
1136 				impersonate_principal,
1137 				second_ticket,
1138 				out_creds,
1139 				ret_tgts);
1140 }
1141 
1142 
1143 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_credentials_with_flags(krb5_context context,krb5_flags options,krb5_kdc_flags flags,krb5_ccache ccache,krb5_creds * in_creds,krb5_creds ** out_creds)1144 krb5_get_credentials_with_flags(krb5_context context,
1145 				krb5_flags options,
1146 				krb5_kdc_flags flags,
1147 				krb5_ccache ccache,
1148 				krb5_creds *in_creds,
1149 				krb5_creds **out_creds)
1150 {
1151     krb5_error_code ret;
1152     krb5_creds **tgts;
1153     krb5_creds *res_creds;
1154     int i;
1155 
1156     if (in_creds->session.keytype) {
1157 	ret = krb5_enctype_valid(context, in_creds->session.keytype);
1158 	if (ret)
1159 	    return ret;
1160     }
1161 
1162     *out_creds = NULL;
1163     res_creds = calloc(1, sizeof(*res_creds));
1164     if (res_creds == NULL) {
1165 	krb5_set_error_message(context, ENOMEM,
1166 			       N_("malloc: out of memory", ""));
1167 	return ENOMEM;
1168     }
1169 
1170     if (in_creds->session.keytype)
1171 	options |= KRB5_TC_MATCH_KEYTYPE;
1172 
1173     /*
1174      * If we got a credential, check if credential is expired before
1175      * returning it.
1176      */
1177     ret = krb5_cc_retrieve_cred(context,
1178                                 ccache,
1179                                 in_creds->session.keytype ?
1180                                 KRB5_TC_MATCH_KEYTYPE : 0,
1181                                 in_creds, res_creds);
1182     /*
1183      * If we got a credential, check if credential is expired before
1184      * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1185      */
1186     if (ret == 0) {
1187 	krb5_timestamp timeret;
1188 
1189 	/* If expired ok, don't bother checking */
1190         if(options & KRB5_GC_EXPIRED_OK) {
1191             *out_creds = res_creds;
1192             return 0;
1193         }
1194 
1195 	krb5_timeofday(context, &timeret);
1196 	if(res_creds->times.endtime > timeret) {
1197 	    *out_creds = res_creds;
1198 	    return 0;
1199 	}
1200 	if(options & KRB5_GC_CACHED)
1201 	    krb5_cc_remove_cred(context, ccache, 0, res_creds);
1202 
1203     } else if(ret != KRB5_CC_END) {
1204         free(res_creds);
1205         return ret;
1206     }
1207     free(res_creds);
1208     if(options & KRB5_GC_CACHED)
1209 	return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
1210 
1211     if(options & KRB5_GC_USER_USER)
1212 	flags.b.enc_tkt_in_skey = 1;
1213     if (flags.b.enc_tkt_in_skey)
1214 	options |= KRB5_GC_NO_STORE;
1215 
1216     tgts = NULL;
1217     ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1218 				 in_creds, NULL, NULL, out_creds, &tgts);
1219     for(i = 0; tgts && tgts[i]; i++) {
1220 	krb5_cc_store_cred(context, ccache, tgts[i]);
1221 	krb5_free_creds(context, tgts[i]);
1222     }
1223     free(tgts);
1224     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1225 	krb5_cc_store_cred(context, ccache, *out_creds);
1226     return ret;
1227 }
1228 
1229 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_credentials(krb5_context context,krb5_flags options,krb5_ccache ccache,krb5_creds * in_creds,krb5_creds ** out_creds)1230 krb5_get_credentials(krb5_context context,
1231 		     krb5_flags options,
1232 		     krb5_ccache ccache,
1233 		     krb5_creds *in_creds,
1234 		     krb5_creds **out_creds)
1235 {
1236     krb5_kdc_flags flags;
1237     flags.i = 0;
1238     return krb5_get_credentials_with_flags(context, options, flags,
1239 					   ccache, in_creds, out_creds);
1240 }
1241 
1242 struct krb5_get_creds_opt_data {
1243     krb5_principal self;
1244     krb5_flags options;
1245     krb5_enctype enctype;
1246     Ticket *ticket;
1247 };
1248 
1249 
1250 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_creds_opt_alloc(krb5_context context,krb5_get_creds_opt * opt)1251 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1252 {
1253     *opt = calloc(1, sizeof(**opt));
1254     if (*opt == NULL) {
1255 	krb5_set_error_message(context, ENOMEM,
1256 			       N_("malloc: out of memory", ""));
1257 	return ENOMEM;
1258     }
1259     return 0;
1260 }
1261 
1262 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_get_creds_opt_free(krb5_context context,krb5_get_creds_opt opt)1263 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1264 {
1265     if (opt->self)
1266 	krb5_free_principal(context, opt->self);
1267     if (opt->ticket) {
1268 	free_Ticket(opt->ticket);
1269 	free(opt->ticket);
1270     }
1271     memset(opt, 0, sizeof(*opt));
1272     free(opt);
1273 }
1274 
1275 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_get_creds_opt_set_options(krb5_context context,krb5_get_creds_opt opt,krb5_flags options)1276 krb5_get_creds_opt_set_options(krb5_context context,
1277 			       krb5_get_creds_opt opt,
1278 			       krb5_flags options)
1279 {
1280     opt->options = options;
1281 }
1282 
1283 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_get_creds_opt_add_options(krb5_context context,krb5_get_creds_opt opt,krb5_flags options)1284 krb5_get_creds_opt_add_options(krb5_context context,
1285 			       krb5_get_creds_opt opt,
1286 			       krb5_flags options)
1287 {
1288     opt->options |= options;
1289 }
1290 
1291 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_get_creds_opt_set_enctype(krb5_context context,krb5_get_creds_opt opt,krb5_enctype enctype)1292 krb5_get_creds_opt_set_enctype(krb5_context context,
1293 			       krb5_get_creds_opt opt,
1294 			       krb5_enctype enctype)
1295 {
1296     opt->enctype = enctype;
1297 }
1298 
1299 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_creds_opt_set_impersonate(krb5_context context,krb5_get_creds_opt opt,krb5_const_principal self)1300 krb5_get_creds_opt_set_impersonate(krb5_context context,
1301 				   krb5_get_creds_opt opt,
1302 				   krb5_const_principal self)
1303 {
1304     if (opt->self)
1305 	krb5_free_principal(context, opt->self);
1306     return krb5_copy_principal(context, self, &opt->self);
1307 }
1308 
1309 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_creds_opt_set_ticket(krb5_context context,krb5_get_creds_opt opt,const Ticket * ticket)1310 krb5_get_creds_opt_set_ticket(krb5_context context,
1311 			      krb5_get_creds_opt opt,
1312 			      const Ticket *ticket)
1313 {
1314     if (opt->ticket) {
1315 	free_Ticket(opt->ticket);
1316 	free(opt->ticket);
1317 	opt->ticket = NULL;
1318     }
1319     if (ticket) {
1320 	krb5_error_code ret;
1321 
1322 	opt->ticket = malloc(sizeof(*ticket));
1323 	if (opt->ticket == NULL) {
1324 	    krb5_set_error_message(context, ENOMEM,
1325 				   N_("malloc: out of memory", ""));
1326 	    return ENOMEM;
1327 	}
1328 	ret = copy_Ticket(ticket, opt->ticket);
1329 	if (ret) {
1330 	    free(opt->ticket);
1331 	    opt->ticket = NULL;
1332 	    krb5_set_error_message(context, ret,
1333 				   N_("malloc: out of memory", ""));
1334 	    return ret;
1335 	}
1336     }
1337     return 0;
1338 }
1339 
1340 
1341 
1342 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_creds(krb5_context context,krb5_get_creds_opt opt,krb5_ccache ccache,krb5_const_principal inprinc,krb5_creds ** out_creds)1343 krb5_get_creds(krb5_context context,
1344 	       krb5_get_creds_opt opt,
1345 	       krb5_ccache ccache,
1346 	       krb5_const_principal inprinc,
1347 	       krb5_creds **out_creds)
1348 {
1349     krb5_kdc_flags flags;
1350     krb5_flags options;
1351     krb5_creds in_creds;
1352     krb5_error_code ret;
1353     krb5_creds **tgts;
1354     krb5_creds *res_creds;
1355     int i;
1356 
1357     if (opt && opt->enctype) {
1358 	ret = krb5_enctype_valid(context, opt->enctype);
1359 	if (ret)
1360 	    return ret;
1361     }
1362 
1363     memset(&in_creds, 0, sizeof(in_creds));
1364     in_creds.server = rk_UNCONST(inprinc);
1365 
1366     ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1367     if (ret)
1368 	return ret;
1369 
1370     if (opt)
1371 	options = opt->options;
1372     else
1373 	options = 0;
1374     flags.i = 0;
1375 
1376     *out_creds = NULL;
1377     res_creds = calloc(1, sizeof(*res_creds));
1378     if (res_creds == NULL) {
1379 	krb5_free_principal(context, in_creds.client);
1380 	krb5_set_error_message(context, ENOMEM,
1381 			       N_("malloc: out of memory", ""));
1382 	return ENOMEM;
1383     }
1384 
1385     if (opt && opt->enctype) {
1386 	in_creds.session.keytype = opt->enctype;
1387 	options |= KRB5_TC_MATCH_KEYTYPE;
1388     }
1389 
1390     /*
1391      * If we got a credential, check if credential is expired before
1392      * returning it.
1393      */
1394     ret = krb5_cc_retrieve_cred(context,
1395                                 ccache,
1396 				options & KRB5_TC_MATCH_KEYTYPE,
1397                                 &in_creds, res_creds);
1398     /*
1399      * If we got a credential, check if credential is expired before
1400      * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1401      */
1402     if (ret == 0) {
1403 	krb5_timestamp timeret;
1404 
1405 	/* If expired ok, don't bother checking */
1406         if(options & KRB5_GC_EXPIRED_OK) {
1407             *out_creds = res_creds;
1408 	    krb5_free_principal(context, in_creds.client);
1409             goto out;
1410         }
1411 
1412 	krb5_timeofday(context, &timeret);
1413 	if(res_creds->times.endtime > timeret) {
1414 	    *out_creds = res_creds;
1415 	    krb5_free_principal(context, in_creds.client);
1416             goto out;
1417 	}
1418 	if(options & KRB5_GC_CACHED)
1419 	    krb5_cc_remove_cred(context, ccache, 0, res_creds);
1420 
1421     } else if(ret != KRB5_CC_END) {
1422         free(res_creds);
1423 	krb5_free_principal(context, in_creds.client);
1424 	goto out;
1425     }
1426     free(res_creds);
1427     if(options & KRB5_GC_CACHED) {
1428 	krb5_free_principal(context, in_creds.client);
1429 	ret = not_found(context, in_creds.server, KRB5_CC_NOTFOUND);
1430 	goto out;
1431     }
1432     if(options & KRB5_GC_USER_USER) {
1433 	flags.b.enc_tkt_in_skey = 1;
1434 	options |= KRB5_GC_NO_STORE;
1435     }
1436     if (options & KRB5_GC_FORWARDABLE)
1437 	flags.b.forwardable = 1;
1438     if (options & KRB5_GC_NO_TRANSIT_CHECK)
1439 	flags.b.disable_transited_check = 1;
1440     if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
1441 	flags.b.request_anonymous = 1; /* XXX ARGH confusion */
1442 	flags.b.constrained_delegation = 1;
1443     }
1444     if (options & KRB5_GC_CANONICALIZE)
1445 	flags.b.canonicalize = 1;
1446 
1447     tgts = NULL;
1448     ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1449 				 &in_creds, opt->self, opt->ticket,
1450 				 out_creds, &tgts);
1451     krb5_free_principal(context, in_creds.client);
1452     for(i = 0; tgts && tgts[i]; i++) {
1453 	krb5_cc_store_cred(context, ccache, tgts[i]);
1454 	krb5_free_creds(context, tgts[i]);
1455     }
1456     free(tgts);
1457     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1458 	krb5_cc_store_cred(context, ccache, *out_creds);
1459 
1460  out:
1461     _krb5_debug(context, 5, "krb5_get_creds: ret = %d", ret);
1462 
1463     return ret;
1464 }
1465 
1466 /*
1467  *
1468  */
1469 
1470 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_renewed_creds(krb5_context context,krb5_creds * creds,krb5_const_principal client,krb5_ccache ccache,const char * in_tkt_service)1471 krb5_get_renewed_creds(krb5_context context,
1472 		       krb5_creds *creds,
1473 		       krb5_const_principal client,
1474 		       krb5_ccache ccache,
1475 		       const char *in_tkt_service)
1476 {
1477     krb5_error_code ret;
1478     krb5_kdc_flags flags;
1479     krb5_creds in, *template, *out = NULL;
1480 
1481     memset(&in, 0, sizeof(in));
1482     memset(creds, 0, sizeof(*creds));
1483 
1484     ret = krb5_copy_principal(context, client, &in.client);
1485     if (ret)
1486 	return ret;
1487 
1488     if (in_tkt_service) {
1489 	ret = krb5_parse_name(context, in_tkt_service, &in.server);
1490 	if (ret) {
1491 	    krb5_free_principal(context, in.client);
1492 	    return ret;
1493 	}
1494     } else {
1495 	const char *realm = krb5_principal_get_realm(context, client);
1496 
1497 	ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
1498 				  realm, NULL);
1499 	if (ret) {
1500 	    krb5_free_principal(context, in.client);
1501 	    return ret;
1502 	}
1503     }
1504 
1505     flags.i = 0;
1506     flags.b.renewable = flags.b.renew = 1;
1507 
1508     /*
1509      * Get template from old credential cache for the same entry, if
1510      * this failes, no worries.
1511      */
1512     ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
1513     if (ret == 0) {
1514 	flags.b.forwardable = template->flags.b.forwardable;
1515 	flags.b.proxiable = template->flags.b.proxiable;
1516 	krb5_free_creds (context, template);
1517     }
1518 
1519     ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
1520     krb5_free_principal(context, in.client);
1521     krb5_free_principal(context, in.server);
1522     if (ret)
1523 	return ret;
1524 
1525     ret = krb5_copy_creds_contents(context, out, creds);
1526     krb5_free_creds(context, out);
1527 
1528     return ret;
1529 }
1530