1/*	$NetBSD: ticket.c,v 1.6 2023/06/19 21:41:45 christos Exp $	*/
2
3/*
4 * Copyright (c) 1997 - 2001 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
40/**
41 * Free ticket and content
42 *
43 * @param context a Kerberos 5 context
44 * @param ticket ticket to free
45 *
46 * @return Returns 0 to indicate success.  Otherwise an kerberos et
47 * error code is returned, see krb5_get_error_message().
48 *
49 * @ingroup krb5
50 */
51
52KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
53krb5_free_ticket(krb5_context context,
54		 krb5_ticket *ticket)
55{
56    free_EncTicketPart(&ticket->ticket);
57    krb5_free_principal(context, ticket->client);
58    krb5_free_principal(context, ticket->server);
59    free(ticket);
60    return 0;
61}
62
63/**
64 * Copy ticket and content
65 *
66 * @param context a Kerberos 5 context
67 * @param from ticket to copy
68 * @param to new copy of ticket, free with krb5_free_ticket()
69 *
70 * @return Returns 0 to indicate success.  Otherwise an kerberos et
71 * error code is returned, see krb5_get_error_message().
72 *
73 * @ingroup krb5
74 */
75
76KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
77krb5_copy_ticket(krb5_context context,
78		 const krb5_ticket *from,
79		 krb5_ticket **to)
80{
81    krb5_error_code ret;
82    krb5_ticket *tmp;
83
84    *to = NULL;
85    tmp = malloc(sizeof(*tmp));
86    if (tmp == NULL)
87	return krb5_enomem(context);
88    if((ret = copy_EncTicketPart(&from->ticket, &tmp->ticket))){
89	free(tmp);
90	return ret;
91    }
92    ret = krb5_copy_principal(context, from->client, &tmp->client);
93    if(ret){
94	free_EncTicketPart(&tmp->ticket);
95	free(tmp);
96	return ret;
97    }
98    ret = krb5_copy_principal(context, from->server, &tmp->server);
99    if(ret){
100	krb5_free_principal(context, tmp->client);
101	free_EncTicketPart(&tmp->ticket);
102	free(tmp);
103	return ret;
104    }
105    *to = tmp;
106    return 0;
107}
108
109/**
110 * Return client principal in ticket
111 *
112 * @param context a Kerberos 5 context
113 * @param ticket ticket to copy
114 * @param client client principal, free with krb5_free_principal()
115 *
116 * @return Returns 0 to indicate success.  Otherwise an kerberos et
117 * error code is returned, see krb5_get_error_message().
118 *
119 * @ingroup krb5
120 */
121
122KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
123krb5_ticket_get_client(krb5_context context,
124		       const krb5_ticket *ticket,
125		       krb5_principal *client)
126{
127    return krb5_copy_principal(context, ticket->client, client);
128}
129
130/**
131 * Return server principal in ticket
132 *
133 * @param context a Kerberos 5 context
134 * @param ticket ticket to copy
135 * @param server server principal, free with krb5_free_principal()
136 *
137 * @return Returns 0 to indicate success.  Otherwise an kerberos et
138 * error code is returned, see krb5_get_error_message().
139 *
140 * @ingroup krb5
141 */
142
143KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
144krb5_ticket_get_server(krb5_context context,
145		       const krb5_ticket *ticket,
146		       krb5_principal *server)
147{
148    return krb5_copy_principal(context, ticket->server, server);
149}
150
151/**
152 * Return end time of ticket
153 *
154 * @param context a Kerberos 5 context
155 * @param ticket ticket to copy
156 *
157 * @return end time of ticket
158 *
159 * @ingroup krb5
160 */
161
162KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
163krb5_ticket_get_endtime(krb5_context context,
164			const krb5_ticket *ticket)
165{
166    return ticket->ticket.endtime;
167}
168
169/**
170 * Get the flags from the Kerberos ticket
171 *
172 * @param context Kerberos context
173 * @param ticket Kerberos ticket
174 *
175 * @return ticket flags
176 *
177 * @ingroup krb5_ticket
178 */
179KRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL
180krb5_ticket_get_flags(krb5_context context,
181		      const krb5_ticket *ticket)
182{
183    return TicketFlags2int(ticket->ticket.flags);
184}
185
186static int
187find_type_in_ad(krb5_context context,
188		int type,
189		krb5_data *data,
190		krb5_boolean *found,
191		krb5_boolean failp,
192		krb5_keyblock *sessionkey,
193		const AuthorizationData *ad,
194		int level)
195{
196    krb5_error_code ret = 0;
197    size_t i;
198
199    if (level > 9) {
200	ret = ENOENT; /* XXX */
201	krb5_set_error_message(context, ret,
202			       N_("Authorization data nested deeper "
203				  "then %d levels, stop searching", ""),
204			       level);
205	goto out;
206    }
207
208    /*
209     * Only copy out the element the first time we get to it, we need
210     * to run over the whole authorization data fields to check if
211     * there are any container clases we need to care about.
212     */
213    for (i = 0; i < ad->len; i++) {
214	if (!*found && ad->val[i].ad_type == type) {
215	    ret = der_copy_octet_string(&ad->val[i].ad_data, data);
216	    if (ret) {
217		krb5_set_error_message(context, ret,
218				       N_("malloc: out of memory", ""));
219		goto out;
220	    }
221	    *found = TRUE;
222	    continue;
223	}
224	switch (ad->val[i].ad_type) {
225	case KRB5_AUTHDATA_IF_RELEVANT: {
226	    AuthorizationData child;
227	    ret = decode_AuthorizationData(ad->val[i].ad_data.data,
228					   ad->val[i].ad_data.length,
229					   &child,
230					   NULL);
231	    if (ret) {
232		krb5_set_error_message(context, ret,
233				       N_("Failed to decode "
234					  "IF_RELEVANT with %d", ""),
235				       (int)ret);
236		goto out;
237	    }
238	    ret = find_type_in_ad(context, type, data, found, FALSE,
239				  sessionkey, &child, level + 1);
240	    free_AuthorizationData(&child);
241	    if (ret)
242		goto out;
243	    break;
244	}
245#if 0 /* XXX test */
246	case KRB5_AUTHDATA_KDC_ISSUED: {
247	    AD_KDCIssued child;
248
249	    ret = decode_AD_KDCIssued(ad->val[i].ad_data.data,
250				      ad->val[i].ad_data.length,
251				      &child,
252				      NULL);
253	    if (ret) {
254		krb5_set_error_message(context, ret,
255				       N_("Failed to decode "
256					  "AD_KDCIssued with %d", ""),
257				       ret);
258		goto out;
259	    }
260	    if (failp) {
261		krb5_boolean valid;
262		krb5_data buf;
263		size_t len;
264
265		ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length,
266				   &child.elements, &len, ret);
267		if (ret) {
268		    free_AD_KDCIssued(&child);
269		    krb5_clear_error_message(context);
270		    goto out;
271		}
272		if(buf.length != len)
273		    krb5_abortx(context, "internal error in ASN.1 encoder");
274
275		ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf,
276					     &child.ad_checksum, &valid);
277		krb5_data_free(&buf);
278		if (ret) {
279		    free_AD_KDCIssued(&child);
280		    goto out;
281		}
282		if (!valid) {
283		    krb5_clear_error_message(context);
284		    ret = ENOENT;
285		    free_AD_KDCIssued(&child);
286		    goto out;
287		}
288	    }
289	    ret = find_type_in_ad(context, type, data, found, failp, sessionkey,
290				  &child.elements, level + 1);
291	    free_AD_KDCIssued(&child);
292	    if (ret)
293		goto out;
294	    break;
295	}
296#endif
297	case KRB5_AUTHDATA_AND_OR:
298	    if (!failp)
299		break;
300	    ret = ENOENT; /* XXX */
301	    krb5_set_error_message(context, ret,
302				   N_("Authorization data contains "
303				      "AND-OR element that is unknown to the "
304				      "application", ""));
305	    goto out;
306	default:
307	    if (!failp)
308		break;
309	    ret = ENOENT; /* XXX */
310	    krb5_set_error_message(context, ret,
311				   N_("Authorization data contains "
312				      "unknown type (%d) ", ""),
313				   ad->val[i].ad_type);
314	    goto out;
315	}
316    }
317out:
318    if (ret) {
319	if (*found) {
320	    krb5_data_free(data);
321	    *found = 0;
322	}
323    }
324    return ret;
325}
326
327KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
328_krb5_get_ad(krb5_context context,
329	     const AuthorizationData *ad,
330	     krb5_keyblock *sessionkey,
331	     int type,
332	     krb5_data *data)
333{
334    krb5_boolean found = FALSE;
335    krb5_error_code ret;
336
337    krb5_data_zero(data);
338
339    if (ad == NULL) {
340	krb5_set_error_message(context, ENOENT,
341			       N_("No authorization data", ""));
342	return ENOENT; /* XXX */
343    }
344
345    ret = find_type_in_ad(context, type, data, &found, TRUE, sessionkey, ad, 0);
346    if (ret)
347	return ret;
348    if (!found) {
349	krb5_set_error_message(context, ENOENT,
350			       N_("Have no authorization data of type %d", ""),
351			       type);
352	return ENOENT; /* XXX */
353    }
354    return 0;
355}
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 * Verify KDC supported anonymous if requested
533 */
534static krb5_error_code
535check_client_anonymous(krb5_context context,
536		       krb5_kdc_rep *rep,
537		       krb5_const_principal requested,
538		       krb5_const_principal mapped,
539		       krb5_boolean is_tgs_rep)
540{
541    int flags;
542
543    if (!rep->enc_part.flags.anonymous)
544	return KRB5KDC_ERR_BADOPTION;
545
546    /*
547     * Here we must validate that the AS returned a ticket of the expected type
548     * for either a fully anonymous request, or authenticated request for an
549     * anonymous ticket.  If this is a TGS request, we're done.  Then if the
550     * 'requested' principal was anonymous, we'll check the 'mapped' principal
551     * accordingly (without enforcing the name type and perhaps the realm).
552     * Finally, if the 'requested' principal was not anonymous, well check
553     * that the 'mapped' principal has an anonymous name and type, in a
554     * non-anonymous realm.  (Should we also be checking for a realm match
555     * between the request and the mapped name in this case?)
556     */
557    if (is_tgs_rep)
558	flags = KRB5_ANON_MATCH_ANY_NONT;
559    else if (krb5_principal_is_anonymous(context, requested,
560                                         KRB5_ANON_MATCH_ANY_NONT))
561	flags = KRB5_ANON_MATCH_UNAUTHENTICATED | KRB5_ANON_IGNORE_NAME_TYPE;
562    else
563	flags = KRB5_ANON_MATCH_AUTHENTICATED;
564
565    if (!krb5_principal_is_anonymous(context, mapped, flags))
566	return KRB5KRB_AP_ERR_MODIFIED;
567
568    return 0;
569}
570
571/*
572 * Verify returned client principal name in anonymous/referral case
573 */
574
575static krb5_error_code
576check_client_mismatch(krb5_context context,
577		      krb5_kdc_rep *rep,
578		      krb5_const_principal requested,
579		      krb5_const_principal mapped,
580		      krb5_keyblock const * key)
581{
582    if (rep->enc_part.flags.anonymous) {
583	if (!krb5_principal_is_anonymous(context, mapped,
584                                         KRB5_ANON_MATCH_ANY_NONT)) {
585	    krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
586				   N_("Anonymous ticket does not contain anonymous "
587				      "principal", ""));
588	    return KRB5KRB_AP_ERR_MODIFIED;
589	}
590    } else {
591	if (krb5_principal_compare(context, requested, mapped) == FALSE &&
592	    !rep->enc_part.flags.enc_pa_rep) {
593	    krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
594				   N_("Not same client principal returned "
595				   "as requested", ""));
596	    return KRB5KRB_AP_ERR_MODIFIED;
597	}
598    }
599
600    return 0;
601}
602
603
604static krb5_error_code KRB5_CALLCONV
605decrypt_tkt (krb5_context context,
606	     krb5_keyblock *key,
607	     krb5_key_usage usage,
608	     krb5_const_pointer decrypt_arg,
609	     krb5_kdc_rep *dec_rep)
610{
611    krb5_error_code ret;
612    krb5_data data;
613    size_t size;
614    krb5_crypto crypto;
615
616    ret = krb5_crypto_init(context, key, 0, &crypto);
617    if (ret)
618	return ret;
619
620    ret = krb5_decrypt_EncryptedData (context,
621				      crypto,
622				      usage,
623				      &dec_rep->kdc_rep.enc_part,
624				      &data);
625    krb5_crypto_destroy(context, crypto);
626
627    if (ret)
628	return ret;
629
630    ret = decode_EncASRepPart(data.data,
631			      data.length,
632			      &dec_rep->enc_part,
633			      &size);
634    if (ret)
635	ret = decode_EncTGSRepPart(data.data,
636				   data.length,
637				   &dec_rep->enc_part,
638				   &size);
639    krb5_data_free (&data);
640    if (ret) {
641        krb5_set_error_message(context, ret,
642			       N_("Failed to decode encpart in ticket", ""));
643	return ret;
644    }
645    return 0;
646}
647
648KRB5_LIB_FUNCTION int KRB5_LIB_CALL
649_krb5_extract_ticket(krb5_context context,
650		     krb5_kdc_rep *rep,
651		     krb5_creds *creds,
652		     krb5_keyblock *key,
653		     krb5_const_pointer keyseed,
654		     krb5_key_usage key_usage,
655		     krb5_addresses *addrs,
656		     unsigned nonce,
657		     unsigned flags,
658		     krb5_data *request,
659		     krb5_decrypt_proc decrypt_proc,
660		     krb5_const_pointer decryptarg)
661{
662    krb5_error_code ret;
663    krb5_principal tmp_principal;
664    size_t len = 0;
665    time_t tmp_time;
666    krb5_timestamp sec_now;
667
668    /* decrypt */
669
670    if (decrypt_proc == NULL)
671	decrypt_proc = decrypt_tkt;
672
673    ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep);
674    if (ret)
675	goto out;
676
677    if (rep->enc_part.flags.enc_pa_rep && request) {
678	krb5_crypto crypto = NULL;
679	Checksum cksum;
680	PA_DATA *pa = NULL;
681	int idx = 0;
682
683	_krb5_debug(context, 5, "processing enc-ap-rep");
684
685	if (rep->enc_part.encrypted_pa_data == NULL ||
686	    (pa = krb5_find_padata(rep->enc_part.encrypted_pa_data->val,
687				   rep->enc_part.encrypted_pa_data->len,
688				   KRB5_PADATA_REQ_ENC_PA_REP,
689				   &idx)) == NULL)
690	{
691	    _krb5_debug(context, 5, "KRB5_PADATA_REQ_ENC_PA_REP missing");
692	    ret = KRB5KRB_AP_ERR_MODIFIED;
693	    goto out;
694	}
695
696	ret = krb5_crypto_init(context, key, 0, &crypto);
697	if (ret)
698	    goto out;
699
700	ret = decode_Checksum(pa->padata_value.data,
701			      pa->padata_value.length,
702			      &cksum, NULL);
703	if (ret) {
704	    krb5_crypto_destroy(context, crypto);
705	    goto out;
706	}
707
708	ret = krb5_verify_checksum(context, crypto,
709				   KRB5_KU_AS_REQ,
710				   request->data, request->length,
711				   &cksum);
712	krb5_crypto_destroy(context, crypto);
713	free_Checksum(&cksum);
714	_krb5_debug(context, 5, "enc-ap-rep: %svalid", (ret == 0) ? "" : "in");
715	if (ret)
716	    goto out;
717    }
718
719    /* save session key */
720
721    creds->session.keyvalue.length = 0;
722    creds->session.keyvalue.data   = NULL;
723    creds->session.keytype = rep->enc_part.key.keytype;
724    ret = krb5_data_copy (&creds->session.keyvalue,
725			  rep->enc_part.key.keyvalue.data,
726			  rep->enc_part.key.keyvalue.length);
727    if (ret) {
728	krb5_clear_error_message(context);
729	goto out;
730    }
731
732    /* compare client and save */
733    ret = _krb5_principalname2krb5_principal(context,
734					     &tmp_principal,
735					     rep->kdc_rep.cname,
736					     rep->kdc_rep.crealm);
737    if (ret)
738	goto out;
739
740    /* check KDC supported anonymous if it was requested */
741    if (flags & EXTRACT_TICKET_MATCH_ANON) {
742	ret = check_client_anonymous(context,rep,
743				     creds->client,
744				     tmp_principal,
745				     request == NULL); /* is TGS */
746	if (ret) {
747	    krb5_free_principal(context, tmp_principal);
748	    goto out;
749	}
750    }
751
752    /* check client referral and save principal */
753    if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) {
754	ret = check_client_mismatch(context, rep,
755				    creds->client,
756				    tmp_principal,
757				    &creds->session);
758	if (ret) {
759	    krb5_free_principal (context, tmp_principal);
760	    goto out;
761	}
762    }
763    krb5_free_principal (context, creds->client);
764    creds->client = tmp_principal;
765
766    /* check server referral and save principal */
767    ret = _krb5_principalname2krb5_principal (context,
768					      &tmp_principal,
769					      rep->enc_part.sname,
770					      rep->enc_part.srealm);
771    if (ret)
772	goto out;
773    if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){
774	ret = check_server_referral(context,
775				    rep,
776				    flags,
777				    creds->server,
778				    tmp_principal,
779				    &creds->session);
780	if (ret) {
781	    krb5_free_principal (context, tmp_principal);
782	    goto out;
783	}
784    }
785    krb5_free_principal(context, creds->server);
786    creds->server = tmp_principal;
787
788    /* verify names */
789    if(flags & EXTRACT_TICKET_MATCH_REALM){
790	const char *srealm = krb5_principal_get_realm(context, creds->server);
791	const char *crealm = krb5_principal_get_realm(context, creds->client);
792
793	if (strcmp(rep->enc_part.srealm, srealm) != 0 ||
794	    strcmp(rep->enc_part.srealm, crealm) != 0)
795	{
796	    ret = KRB5KRB_AP_ERR_MODIFIED;
797	    krb5_clear_error_message(context);
798	    goto out;
799	}
800    }
801
802    /* compare nonces */
803
804    if (nonce != (unsigned)rep->enc_part.nonce) {
805	ret = KRB5KRB_AP_ERR_MODIFIED;
806	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
807	goto out;
808    }
809
810    /* set kdc-offset */
811
812    krb5_timeofday (context, &sec_now);
813    if (rep->enc_part.flags.initial
814	&& (flags & EXTRACT_TICKET_TIMESYNC)
815	&& context->kdc_sec_offset == 0
816	&& krb5_config_get_bool (context, NULL,
817				 "libdefaults",
818				 "kdc_timesync",
819				 NULL)) {
820	context->kdc_sec_offset = rep->enc_part.authtime - sec_now;
821	krb5_timeofday (context, &sec_now);
822    }
823
824    /* check all times */
825
826    if (rep->enc_part.starttime) {
827	tmp_time = *rep->enc_part.starttime;
828    } else
829	tmp_time = rep->enc_part.authtime;
830
831    if (creds->times.starttime == 0
832	&& labs(tmp_time - sec_now) > context->max_skew) {
833	ret = KRB5KRB_AP_ERR_SKEW;
834	krb5_set_error_message (context, ret,
835				N_("time skew (%ld) larger than max (%ld)", ""),
836			       labs(tmp_time - sec_now),
837			       (long)context->max_skew);
838	goto out;
839    }
840
841    if (creds->times.starttime != 0
842	&& tmp_time != creds->times.starttime) {
843	krb5_clear_error_message (context);
844	ret = KRB5KRB_AP_ERR_MODIFIED;
845	goto out;
846    }
847
848    creds->times.starttime = tmp_time;
849
850    if (rep->enc_part.renew_till) {
851	tmp_time = *rep->enc_part.renew_till;
852    } else
853	tmp_time = 0;
854
855    if (creds->times.renew_till != 0
856	&& tmp_time > creds->times.renew_till) {
857	krb5_clear_error_message (context);
858	ret = KRB5KRB_AP_ERR_MODIFIED;
859	goto out;
860    }
861
862    creds->times.renew_till = tmp_time;
863
864    creds->times.authtime = rep->enc_part.authtime;
865
866    if (creds->times.endtime != 0
867	&& rep->enc_part.endtime > creds->times.endtime) {
868	krb5_clear_error_message (context);
869	ret = KRB5KRB_AP_ERR_MODIFIED;
870	goto out;
871    }
872
873    creds->times.endtime  = rep->enc_part.endtime;
874
875    if(rep->enc_part.caddr)
876	krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses);
877    else if(addrs)
878	krb5_copy_addresses (context, addrs, &creds->addresses);
879    else {
880	creds->addresses.len = 0;
881	creds->addresses.val = NULL;
882    }
883    creds->flags.b = rep->enc_part.flags;
884
885    creds->authdata.len = 0;
886    creds->authdata.val = NULL;
887
888    /* extract ticket */
889    ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
890		       &rep->kdc_rep.ticket, &len, ret);
891    if(ret)
892	goto out;
893    if (creds->ticket.length != len)
894	krb5_abortx(context, "internal error in ASN.1 encoder");
895    creds->second_ticket.length = 0;
896    creds->second_ticket.data   = NULL;
897
898
899out:
900    memset (rep->enc_part.key.keyvalue.data, 0,
901	    rep->enc_part.key.keyvalue.length);
902    return ret;
903}
904