1/*
2 * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "krb5_locl.h"
37
38/**
39 * Free ticket and content
40 *
41 * @param context a Kerberos 5 context
42 * @param ticket ticket to free
43 *
44 * @return Returns 0 to indicate success.  Otherwise an kerberos et
45 * error code is returned, see krb5_get_error_message().
46 *
47 * @ingroup krb5
48 */
49
50KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
51krb5_free_ticket(krb5_context context,
52		 krb5_ticket *ticket)
53{
54    free_EncTicketPart(&ticket->ticket);
55    krb5_free_principal(context, ticket->client);
56    krb5_free_principal(context, ticket->server);
57    free(ticket);
58    return 0;
59}
60
61/**
62 * Copy ticket and content
63 *
64 * @param context a Kerberos 5 context
65 * @param from ticket to copy
66 * @param to new copy of ticket, free with krb5_free_ticket()
67 *
68 * @return Returns 0 to indicate success.  Otherwise an kerberos et
69 * error code is returned, see krb5_get_error_message().
70 *
71 * @ingroup krb5
72 */
73
74KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
75krb5_copy_ticket(krb5_context context,
76		 const krb5_ticket *from,
77		 krb5_ticket **to)
78{
79    krb5_error_code ret;
80    krb5_ticket *tmp;
81
82    *to = NULL;
83    tmp = malloc(sizeof(*tmp));
84    if(tmp == NULL) {
85	krb5_set_error_message(context, ENOMEM,
86			       N_("malloc: out of memory", ""));
87	return ENOMEM;
88    }
89    if((ret = copy_EncTicketPart(&from->ticket, &tmp->ticket))){
90	free(tmp);
91	return ret;
92    }
93    ret = krb5_copy_principal(context, from->client, &tmp->client);
94    if(ret){
95	free_EncTicketPart(&tmp->ticket);
96	free(tmp);
97	return ret;
98    }
99    ret = krb5_copy_principal(context, from->server, &tmp->server);
100    if(ret){
101	krb5_free_principal(context, tmp->client);
102	free_EncTicketPart(&tmp->ticket);
103	free(tmp);
104	return ret;
105    }
106    *to = tmp;
107    return 0;
108}
109
110/**
111 * Return client principal in ticket
112 *
113 * @param context a Kerberos 5 context
114 * @param ticket ticket to copy
115 * @param client client principal, free with krb5_free_principal()
116 *
117 * @return Returns 0 to indicate success.  Otherwise an kerberos et
118 * error code is returned, see krb5_get_error_message().
119 *
120 * @ingroup krb5
121 */
122
123KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
124krb5_ticket_get_client(krb5_context context,
125		       const krb5_ticket *ticket,
126		       krb5_principal *client)
127{
128    return krb5_copy_principal(context, ticket->client, client);
129}
130
131/**
132 * Return server principal in ticket
133 *
134 * @param context a Kerberos 5 context
135 * @param ticket ticket to copy
136 * @param server server principal, free with krb5_free_principal()
137 *
138 * @return Returns 0 to indicate success.  Otherwise an kerberos et
139 * error code is returned, see krb5_get_error_message().
140 *
141 * @ingroup krb5
142 */
143
144KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
145krb5_ticket_get_server(krb5_context context,
146		       const krb5_ticket *ticket,
147		       krb5_principal *server)
148{
149    return krb5_copy_principal(context, ticket->server, server);
150}
151
152/**
153 * Return end time of ticket
154 *
155 * @param context a Kerberos 5 context
156 * @param ticket ticket to copy
157 *
158 * @return end time of ticket
159 *
160 * @ingroup krb5
161 */
162
163KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
164krb5_ticket_get_endtime(krb5_context context,
165			const krb5_ticket *ticket)
166{
167    return ticket->ticket.endtime;
168}
169
170/**
171 * Get the flags from the Kerberos ticket
172 *
173 * @param context Kerberos context
174 * @param ticket Kerberos ticket
175 *
176 * @return ticket flags
177 *
178 * @ingroup krb5_ticket
179 */
180KRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL
181krb5_ticket_get_flags(krb5_context context,
182		      const krb5_ticket *ticket)
183{
184    return TicketFlags2int(ticket->ticket.flags);
185}
186
187static int
188find_type_in_ad(krb5_context context,
189		int type,
190		krb5_data *data,
191		krb5_boolean *found,
192		krb5_boolean failp,
193		krb5_keyblock *sessionkey,
194		const AuthorizationData *ad,
195		int level)
196{
197    krb5_error_code ret = 0;
198    size_t i;
199
200    if (level > 9) {
201	ret = ENOENT; /* XXX */
202	krb5_set_error_message(context, ret,
203			       N_("Authorization data nested deeper "
204				  "then %d levels, stop searching", ""),
205			       level);
206	goto out;
207    }
208
209    /*
210     * Only copy out the element the first time we get to it, we need
211     * to run over the whole authorization data fields to check if
212     * there are any container clases we need to care about.
213     */
214    for (i = 0; i < ad->len; i++) {
215	if (!*found && ad->val[i].ad_type == type) {
216	    ret = der_copy_octet_string(&ad->val[i].ad_data, data);
217	    if (ret) {
218		krb5_set_error_message(context, ret,
219				       N_("malloc: out of memory", ""));
220		goto out;
221	    }
222	    *found = TRUE;
223	    continue;
224	}
225	switch (ad->val[i].ad_type) {
226	case KRB5_AUTHDATA_IF_RELEVANT: {
227	    AuthorizationData child;
228	    ret = decode_AuthorizationData(ad->val[i].ad_data.data,
229					   ad->val[i].ad_data.length,
230					   &child,
231					   NULL);
232	    if (ret) {
233		krb5_set_error_message(context, ret,
234				       N_("Failed to decode "
235					  "IF_RELEVANT with %d", ""),
236				       (int)ret);
237		goto out;
238	    }
239	    ret = find_type_in_ad(context, type, data, found, FALSE,
240				  sessionkey, &child, level + 1);
241	    free_AuthorizationData(&child);
242	    if (ret)
243		goto out;
244	    break;
245	}
246#if 0 /* XXX test */
247	case KRB5_AUTHDATA_KDC_ISSUED: {
248	    AD_KDCIssued child;
249
250	    ret = decode_AD_KDCIssued(ad->val[i].ad_data.data,
251				      ad->val[i].ad_data.length,
252				      &child,
253				      NULL);
254	    if (ret) {
255		krb5_set_error_message(context, ret,
256				       N_("Failed to decode "
257					  "AD_KDCIssued with %d", ""),
258				       ret);
259		goto out;
260	    }
261	    if (failp) {
262		krb5_boolean valid;
263		krb5_data buf;
264		size_t len;
265
266		ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length,
267				   &child.elements, &len, ret);
268		if (ret) {
269		    free_AD_KDCIssued(&child);
270		    krb5_clear_error_message(context);
271		    goto out;
272		}
273		if(buf.length != len)
274		    krb5_abortx(context, "internal error in ASN.1 encoder");
275
276		ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf,
277					     &child.ad_checksum, &valid);
278		krb5_data_free(&buf);
279		if (ret) {
280		    free_AD_KDCIssued(&child);
281		    goto out;
282		}
283		if (!valid) {
284		    krb5_clear_error_message(context);
285		    ret = ENOENT;
286		    free_AD_KDCIssued(&child);
287		    goto out;
288		}
289	    }
290	    ret = find_type_in_ad(context, type, data, found, failp, sessionkey,
291				  &child.elements, level + 1);
292	    free_AD_KDCIssued(&child);
293	    if (ret)
294		goto out;
295	    break;
296	}
297#endif
298	case KRB5_AUTHDATA_AND_OR:
299	    if (!failp)
300		break;
301	    ret = ENOENT; /* XXX */
302	    krb5_set_error_message(context, ret,
303				   N_("Authorization data contains "
304				      "AND-OR element that is unknown to the "
305				      "application", ""));
306	    goto out;
307	default:
308	    if (!failp)
309		break;
310	    ret = ENOENT; /* XXX */
311	    krb5_set_error_message(context, ret,
312				   N_("Authorization data contains "
313				      "unknown type (%d) ", ""),
314				   ad->val[i].ad_type);
315	    goto out;
316	}
317    }
318out:
319    if (ret) {
320	if (*found) {
321	    krb5_data_free(data);
322	    *found = 0;
323	}
324    }
325    return ret;
326}
327
328KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
329_krb5_get_ad(krb5_context context,
330	     const AuthorizationData *ad,
331	     krb5_keyblock *sessionkey,
332	     int type,
333	     krb5_data *data)
334{
335    krb5_boolean found = FALSE;
336    krb5_error_code ret;
337
338    krb5_data_zero(data);
339
340    if (ad == NULL) {
341	krb5_set_error_message(context, ENOENT,
342			       N_("No authorization data", ""));
343	return ENOENT; /* XXX */
344    }
345
346    ret = find_type_in_ad(context, type, data, &found, TRUE, sessionkey, ad, 0);
347    if (ret)
348	return ret;
349    if (!found) {
350	krb5_set_error_message(context, ENOENT,
351			       N_("Have no authorization data of type %d", ""),
352			       type);
353	return ENOENT; /* XXX */
354    }
355    return 0;
356}
357
358/**
359 * Extract the authorization data type of type from the ticket. Store
360 * the field in data. This function is to use for kerberos
361 * applications.
362 *
363 * @param context a Kerberos 5 context
364 * @param ticket Kerberos ticket
365 * @param type type to fetch
366 * @param data returned data, free with krb5_data_free()
367 *
368 * @ingroup krb5
369 */
370
371KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
372krb5_ticket_get_authorization_data_type(krb5_context context,
373					krb5_ticket *ticket,
374					int type,
375					krb5_data *data)
376{
377    AuthorizationData *ad;
378    krb5_error_code ret;
379    krb5_boolean found = FALSE;
380
381    krb5_data_zero(data);
382
383    ad = ticket->ticket.authorization_data;
384    if (ticket->ticket.authorization_data == NULL) {
385	krb5_set_error_message(context, ENOENT,
386			       N_("Ticket have not authorization data", ""));
387	return ENOENT; /* XXX */
388    }
389
390    ret = find_type_in_ad(context, type, data, &found, TRUE,
391			  &ticket->ticket.key, ad, 0);
392    if (ret)
393	return ret;
394    if (!found) {
395	krb5_set_error_message(context, ENOENT,
396			       N_("Ticket have not "
397				  "authorization data of type %d", ""),
398			       type);
399	return ENOENT; /* XXX */
400    }
401    return 0;
402}
403
404static krb5_error_code
405check_server_referral(krb5_context context,
406		      krb5_kdc_rep *rep,
407		      unsigned flags,
408		      krb5_const_principal requested,
409		      krb5_const_principal returned,
410		      krb5_keyblock * key)
411{
412    krb5_error_code ret;
413    PA_ServerReferralData ref;
414    krb5_crypto session;
415    EncryptedData ed;
416    size_t len;
417    krb5_data data;
418    PA_DATA *pa;
419    int i = 0, cmp;
420
421    if (rep->kdc_rep.padata == NULL)
422	goto noreferral;
423
424    pa = krb5_find_padata(rep->kdc_rep.padata->val,
425			  rep->kdc_rep.padata->len,
426			  KRB5_PADATA_SERVER_REFERRAL, &i);
427    if (pa == NULL)
428	goto noreferral;
429
430    memset(&ed, 0, sizeof(ed));
431    memset(&ref, 0, sizeof(ref));
432
433    ret = decode_EncryptedData(pa->padata_value.data,
434			       pa->padata_value.length,
435			       &ed, &len);
436    if (ret)
437	return ret;
438    if (len != pa->padata_value.length) {
439	free_EncryptedData(&ed);
440	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
441			       N_("Referral EncryptedData wrong for realm %s",
442				  "realm"), requested->realm);
443	return KRB5KRB_AP_ERR_MODIFIED;
444    }
445
446    ret = krb5_crypto_init(context, key, 0, &session);
447    if (ret) {
448	free_EncryptedData(&ed);
449	return ret;
450    }
451
452    ret = krb5_decrypt_EncryptedData(context, session,
453				     KRB5_KU_PA_SERVER_REFERRAL,
454				     &ed, &data);
455    free_EncryptedData(&ed);
456    krb5_crypto_destroy(context, session);
457    if (ret)
458	return ret;
459
460    ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len);
461    if (ret) {
462	krb5_data_free(&data);
463	return ret;
464    }
465    krb5_data_free(&data);
466
467    if (strcmp(requested->realm, returned->realm) != 0) {
468	free_PA_ServerReferralData(&ref);
469	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
470			       N_("server ref realm mismatch, "
471				  "requested realm %s got back %s", ""),
472			       requested->realm, returned->realm);
473	return KRB5KRB_AP_ERR_MODIFIED;
474    }
475
476    if (krb5_principal_is_krbtgt(context, returned)) {
477	const char *realm = returned->name.name_string.val[1];
478
479	if (ref.referred_realm == NULL
480	    || strcmp(*ref.referred_realm, realm) != 0)
481	{
482	    free_PA_ServerReferralData(&ref);
483	    krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
484				   N_("tgt returned with wrong ref", ""));
485	    return KRB5KRB_AP_ERR_MODIFIED;
486	}
487    } else if (krb5_principal_compare(context, returned, requested) == 0) {
488	free_PA_ServerReferralData(&ref);
489	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
490			       N_("req princ no same as returned", ""));
491	return KRB5KRB_AP_ERR_MODIFIED;
492    }
493
494    if (ref.requested_principal_name) {
495	cmp = _krb5_principal_compare_PrincipalName(context,
496						    requested,
497						    ref.requested_principal_name);
498	if (!cmp) {
499	    free_PA_ServerReferralData(&ref);
500	    krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
501				   N_("referred principal not same "
502				      "as requested", ""));
503	    return KRB5KRB_AP_ERR_MODIFIED;
504	}
505    } else if (flags & EXTRACT_TICKET_AS_REQ) {
506	free_PA_ServerReferralData(&ref);
507	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
508			       N_("Requested principal missing on AS-REQ", ""));
509	return KRB5KRB_AP_ERR_MODIFIED;
510    }
511
512    free_PA_ServerReferralData(&ref);
513
514    return ret;
515noreferral:
516    /*
517     * Expect excact match or that we got a krbtgt
518     */
519    if (krb5_principal_compare(context, requested, returned) != TRUE &&
520	(krb5_realm_compare(context, requested, returned) != TRUE &&
521	 krb5_principal_is_krbtgt(context, returned) != TRUE))
522    {
523	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
524			       N_("Not same server principal returned "
525				  "as requested", ""));
526	return KRB5KRB_AP_ERR_MODIFIED;
527    }
528    return 0;
529}
530
531
532/*
533 * Verify referral data
534 */
535
536
537static krb5_error_code
538check_client_referral(krb5_context context,
539		      krb5_kdc_rep *rep,
540		      krb5_const_principal requested,
541		      krb5_const_principal mapped,
542		      krb5_keyblock const * key)
543{
544    krb5_error_code ret;
545    PA_ClientCanonicalized canon;
546    krb5_crypto crypto;
547    krb5_data data;
548    PA_DATA *pa;
549    size_t len;
550    int i = 0;
551
552    if (rep->kdc_rep.padata == NULL)
553	goto noreferral;
554
555    pa = krb5_find_padata(rep->kdc_rep.padata->val,
556			  rep->kdc_rep.padata->len,
557			  KRB5_PADATA_CLIENT_CANONICALIZED, &i);
558    if (pa == NULL)
559	goto noreferral;
560
561    ret = decode_PA_ClientCanonicalized(pa->padata_value.data,
562					pa->padata_value.length,
563					&canon, &len);
564    if (ret) {
565	krb5_set_error_message(context, ret,
566			       N_("Failed to decode ClientCanonicalized "
567				  "from realm %s", ""), requested->realm);
568	return ret;
569    }
570
571    ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
572		       &canon.names, &len, ret);
573    if (ret) {
574	free_PA_ClientCanonicalized(&canon);
575	return ret;
576    }
577    if (data.length != len)
578	krb5_abortx(context, "internal asn.1 error");
579
580    ret = krb5_crypto_init(context, key, 0, &crypto);
581    if (ret) {
582	free(data.data);
583	free_PA_ClientCanonicalized(&canon);
584	return ret;
585    }
586
587    ret = krb5_verify_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES,
588			       data.data, data.length,
589			       &canon.canon_checksum);
590    krb5_crypto_destroy(context, crypto);
591    free(data.data);
592    if (ret) {
593	krb5_set_error_message(context, ret,
594			       N_("Failed to verify client canonicalized "
595				  "data from realm %s", ""),
596			       requested->realm);
597	free_PA_ClientCanonicalized(&canon);
598	return ret;
599    }
600
601    if (!_krb5_principal_compare_PrincipalName(context,
602					       requested,
603					       &canon.names.requested_name))
604    {
605	free_PA_ClientCanonicalized(&canon);
606	krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
607			       N_("Requested name doesn't match"
608				  " in client referral", ""));
609	return KRB5_PRINC_NOMATCH;
610    }
611    if (!_krb5_principal_compare_PrincipalName(context,
612					       mapped,
613					       &canon.names.mapped_name))
614    {
615	free_PA_ClientCanonicalized(&canon);
616	krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
617			       N_("Mapped name doesn't match"
618				  " in client referral", ""));
619	return KRB5_PRINC_NOMATCH;
620    }
621
622    return 0;
623
624noreferral:
625    if (krb5_principal_compare(context, requested, mapped) == FALSE &&
626	!rep->enc_part.flags.enc_pa_rep)
627    {
628	char *mname = NULL;
629	krb5_unparse_name(context, mapped, &mname);
630	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
631			       N_("Not same client principal returned (%s)"
632				  "as requested", ""),
633			       mname ? mname : "<unknown name>");
634	krb5_xfree(mname);
635	return KRB5KRB_AP_ERR_MODIFIED;
636    }
637    return 0;
638}
639
640
641static krb5_error_code KRB5_CALLCONV
642decrypt_tkt (krb5_context context,
643	     krb5_keyblock *key,
644	     krb5_key_usage usage,
645	     krb5_const_pointer decrypt_arg,
646	     krb5_kdc_rep *dec_rep)
647{
648    krb5_error_code ret;
649    krb5_data data;
650    size_t size;
651    krb5_crypto crypto;
652
653    ret = krb5_crypto_init(context, key, 0, &crypto);
654    if (ret)
655	return ret;
656
657    ret = krb5_decrypt_EncryptedData (context,
658				      crypto,
659				      usage,
660				      &dec_rep->kdc_rep.enc_part,
661				      &data);
662    krb5_crypto_destroy(context, crypto);
663
664    if (ret)
665	return ret;
666
667    ret = decode_EncASRepPart(data.data,
668			      data.length,
669			      &dec_rep->enc_part,
670			      &size);
671    if (ret)
672	ret = decode_EncTGSRepPart(data.data,
673				   data.length,
674				   &dec_rep->enc_part,
675				   &size);
676    krb5_data_free (&data);
677    if (ret) {
678        krb5_set_error_message(context, ret,
679			       N_("Failed to decode encpart in ticket", ""));
680	return ret;
681    }
682    return 0;
683}
684
685int
686_krb5_extract_ticket(krb5_context context,
687		     krb5_kdc_rep *rep,
688		     krb5_creds *creds,
689		     krb5_keyblock *key,
690		     krb5_key_usage key_usage,
691		     krb5_addresses *addrs,
692		     unsigned nonce,
693		     unsigned flags,
694		     krb5_data *request,
695		     krb5_decrypt_proc decrypt_proc,
696		     krb5_const_pointer decryptarg)
697{
698    krb5_error_code ret;
699    krb5_principal tmp_principal;
700    size_t len = 0;
701    time_t tmp_time;
702    krb5_timestamp sec_now;
703
704    /* decrypt */
705
706    if (decrypt_proc == NULL)
707	decrypt_proc = decrypt_tkt;
708
709    ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep);
710    if (ret)
711	goto out;
712
713    if (rep->enc_part.flags.enc_pa_rep && request && (flags & EXTRACT_TICKET_REQUIRE_ENC_PA)) {
714	krb5_crypto crypto = NULL;
715	Checksum cksum;
716	PA_DATA *pa = NULL;
717	int idx = 0;
718
719	_krb5_debugx(context, 5, "processing enc-ap-rep");
720
721	if (rep->enc_part.encrypted_pa_data == NULL ||
722	    (pa = krb5_find_padata(rep->enc_part.encrypted_pa_data->val,
723				   rep->enc_part.encrypted_pa_data->len,
724				   KRB5_PADATA_REQ_ENC_PA_REP,
725				   &idx)) == NULL)
726	{
727	    _krb5_debugx(context, 5, "KRB5_PADATA_REQ_ENC_PA_REP missing");
728	    ret = KRB5KRB_AP_ERR_MODIFIED;
729	    goto out;
730	}
731
732	ret = krb5_crypto_init(context, key, 0, &crypto);
733	if (ret)
734	    goto out;
735
736	ret = decode_Checksum(pa->padata_value.data,
737			      pa->padata_value.length,
738			      &cksum, NULL);
739	if (ret) {
740	    krb5_crypto_destroy(context, crypto);
741	    goto out;
742	}
743
744	ret = krb5_verify_checksum(context, crypto,
745				   KRB5_KU_AS_REQ,
746				   request->data, request->length,
747				   &cksum);
748	krb5_crypto_destroy(context, crypto);
749	free_Checksum(&cksum);
750	_krb5_debug(context, 5, ret, "enc-ap-rep: %svalid", (ret == 0) ? "" : "in");
751	if (ret)
752	    goto out;
753    }
754
755    /* save session key */
756
757    creds->session.keyvalue.length = 0;
758    creds->session.keyvalue.data   = NULL;
759    creds->session.keytype = rep->enc_part.key.keytype;
760    ret = krb5_data_copy (&creds->session.keyvalue,
761			  rep->enc_part.key.keyvalue.data,
762			  rep->enc_part.key.keyvalue.length);
763    if (ret) {
764	krb5_clear_error_message(context);
765	goto out;
766    }
767
768    /* compare client and save */
769    ret = _krb5_principalname2krb5_principal(context,
770					     &tmp_principal,
771					     rep->kdc_rep.cname,
772					     rep->kdc_rep.crealm);
773    if (ret)
774	goto out;
775
776    /* check client referral and save principal */
777    /* anonymous here ? */
778    if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) {
779	ret = check_client_referral(context, rep,
780				    creds->client,
781				    tmp_principal,
782				    &creds->session);
783	if (ret) {
784	    krb5_free_principal (context, tmp_principal);
785	    goto out;
786	}
787    }
788    krb5_free_principal (context, creds->client);
789    creds->client = tmp_principal;
790
791    /* check server referral and save principal */
792    ret = _krb5_principalname2krb5_principal (context,
793					      &tmp_principal,
794					      rep->kdc_rep.ticket.sname,
795					      rep->kdc_rep.ticket.realm);
796    if (ret)
797	goto out;
798    if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){
799	ret = check_server_referral(context,
800				    rep,
801				    flags,
802				    creds->server,
803				    tmp_principal,
804				    &creds->session);
805	if (ret) {
806	    krb5_free_principal (context, tmp_principal);
807	    goto out;
808	}
809    }
810    krb5_free_principal(context, creds->server);
811    creds->server = tmp_principal;
812
813    /* verify names */
814    if(flags & EXTRACT_TICKET_MATCH_REALM){
815	const char *srealm = krb5_principal_get_realm(context, creds->server);
816	const char *crealm = krb5_principal_get_realm(context, creds->client);
817
818	if (strcmp(rep->enc_part.srealm, srealm) != 0 ||
819	    strcmp(rep->enc_part.srealm, crealm) != 0)
820	{
821	    ret = KRB5KRB_AP_ERR_MODIFIED;
822	    krb5_set_error_message(context, ret, "server realm (%s) doesn't match client's (%s)",
823				   srealm, crealm);
824	    krb5_clear_error_message(context);
825	    goto out;
826	}
827    }
828
829    /* compare nonces */
830
831    if (nonce != (unsigned)rep->enc_part.nonce) {
832	ret = KRB5KRB_AP_ERR_MODIFIED;
833	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
834	goto out;
835    }
836
837    /* set kdc-offset */
838
839    krb5_timeofday (context, &sec_now);
840    if (rep->enc_part.flags.initial
841	&& (flags & EXTRACT_TICKET_TIMESYNC)
842	&& context->kdc_sec_offset == 0
843	&& krb5_config_get_bool (context, NULL,
844				 "libdefaults",
845				 "kdc_timesync",
846				 NULL)) {
847	context->kdc_sec_offset = (int32_t)(rep->enc_part.authtime - sec_now);
848	krb5_timeofday (context, &sec_now);
849    }
850
851    /* check all times */
852
853    if (rep->enc_part.starttime) {
854	tmp_time = *rep->enc_part.starttime;
855    } else
856	tmp_time = rep->enc_part.authtime;
857
858    if (creds->times.starttime == 0
859	&& krb5_time_abs(tmp_time, sec_now) > context->max_skew) {
860	ret = KRB5KRB_AP_ERR_SKEW;
861	krb5_set_error_message (context, ret,
862				N_("time skew (%ld) larger than max (%d)", ""),
863				(long)krb5_time_abs(tmp_time, sec_now),
864				(int)context->max_skew);
865	goto out;
866    }
867
868    if (creds->times.starttime != 0
869	&& tmp_time != creds->times.starttime) {
870	krb5_clear_error_message (context);
871	krb5_set_error_message(context, ret, "startime is not the requested startime");
872	ret = KRB5KRB_AP_ERR_MODIFIED;
873	goto out;
874    }
875
876    creds->times.starttime = tmp_time;
877
878    if (rep->enc_part.renew_till) {
879	tmp_time = *rep->enc_part.renew_till;
880    } else
881	tmp_time = 0;
882
883    if (creds->times.renew_till != 0
884	&& tmp_time > creds->times.renew_till) {
885	krb5_clear_error_message (context);
886	krb5_set_error_message(context, ret, "renewtime is past the requested renewtime");
887	ret = KRB5KRB_AP_ERR_MODIFIED;
888	goto out;
889    }
890
891    creds->times.renew_till = tmp_time;
892
893    creds->times.authtime = rep->enc_part.authtime;
894
895    if (creds->times.endtime != 0
896	&& rep->enc_part.endtime > creds->times.endtime) {
897	krb5_clear_error_message (context);
898	ret = KRB5KRB_AP_ERR_MODIFIED;
899	krb5_set_error_message(context, ret, "endtime is past the requested endtime");
900	goto out;
901    }
902
903    creds->times.endtime  = rep->enc_part.endtime;
904
905    if(rep->enc_part.caddr)
906	krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses);
907    else if(addrs)
908	krb5_copy_addresses (context, addrs, &creds->addresses);
909    else {
910	creds->addresses.len = 0;
911	creds->addresses.val = NULL;
912    }
913    creds->flags.b = rep->enc_part.flags;
914
915    creds->authdata.len = 0;
916    creds->authdata.val = NULL;
917
918    /* extract ticket */
919    ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
920		       &rep->kdc_rep.ticket, &len, ret);
921    if(ret)
922	goto out;
923    if (creds->ticket.length != len)
924	krb5_abortx(context, "internal error in ASN.1 encoder");
925    creds->second_ticket.length = 0;
926    creds->second_ticket.data   = NULL;
927
928
929out:
930    if (ret)
931	_krb5_debugx(context, 5, "_krb5_extract_ticket failed with %d", ret);
932
933    memset (rep->enc_part.key.keyvalue.data, 0,
934	    rep->enc_part.key.keyvalue.length);
935    return ret;
936}
937