xref: /freebsd/crypto/heimdal/lib/krb5/ticket.c (revision fed1ca4b719c56c930f2259d80663cd34be812bb)
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 
50 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
51 krb5_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 
74 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
75 krb5_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 
123 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
124 krb5_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 
144 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
145 krb5_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 
163 KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
164 krb5_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  */
180 KRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL
181 krb5_ticket_get_flags(krb5_context context,
182 		      const krb5_ticket *ticket)
183 {
184     return TicketFlags2int(ticket->ticket.flags);
185 }
186 
187 static int
188 find_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     }
318 out:
319     if (ret) {
320 	if (*found) {
321 	    krb5_data_free(data);
322 	    *found = 0;
323 	}
324     }
325     return ret;
326 }
327 
328 /**
329  * Extract the authorization data type of type from the ticket. Store
330  * the field in data. This function is to use for kerberos
331  * applications.
332  *
333  * @param context a Kerberos 5 context
334  * @param ticket Kerberos ticket
335  * @param type type to fetch
336  * @param data returned data, free with krb5_data_free()
337  *
338  * @ingroup krb5
339  */
340 
341 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
342 krb5_ticket_get_authorization_data_type(krb5_context context,
343 					krb5_ticket *ticket,
344 					int type,
345 					krb5_data *data)
346 {
347     AuthorizationData *ad;
348     krb5_error_code ret;
349     krb5_boolean found = FALSE;
350 
351     krb5_data_zero(data);
352 
353     ad = ticket->ticket.authorization_data;
354     if (ticket->ticket.authorization_data == NULL) {
355 	krb5_set_error_message(context, ENOENT,
356 			       N_("Ticket have not authorization data", ""));
357 	return ENOENT; /* XXX */
358     }
359 
360     ret = find_type_in_ad(context, type, data, &found, TRUE,
361 			  &ticket->ticket.key, ad, 0);
362     if (ret)
363 	return ret;
364     if (!found) {
365 	krb5_set_error_message(context, ENOENT,
366 			       N_("Ticket have not "
367 				  "authorization data of type %d", ""),
368 			       type);
369 	return ENOENT; /* XXX */
370     }
371     return 0;
372 }
373 
374 static krb5_error_code
375 check_server_referral(krb5_context context,
376 		      krb5_kdc_rep *rep,
377 		      unsigned flags,
378 		      krb5_const_principal requested,
379 		      krb5_const_principal returned,
380 		      krb5_keyblock * key)
381 {
382     krb5_error_code ret;
383     PA_ServerReferralData ref;
384     krb5_crypto session;
385     EncryptedData ed;
386     size_t len;
387     krb5_data data;
388     PA_DATA *pa;
389     int i = 0, cmp;
390 
391     if (rep->kdc_rep.padata == NULL)
392 	goto noreferral;
393 
394     pa = krb5_find_padata(rep->kdc_rep.padata->val,
395 			  rep->kdc_rep.padata->len,
396 			  KRB5_PADATA_SERVER_REFERRAL, &i);
397     if (pa == NULL)
398 	goto noreferral;
399 
400     memset(&ed, 0, sizeof(ed));
401     memset(&ref, 0, sizeof(ref));
402 
403     ret = decode_EncryptedData(pa->padata_value.data,
404 			       pa->padata_value.length,
405 			       &ed, &len);
406     if (ret)
407 	return ret;
408     if (len != pa->padata_value.length) {
409 	free_EncryptedData(&ed);
410 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
411 			       N_("Referral EncryptedData wrong for realm %s",
412 				  "realm"), requested->realm);
413 	return KRB5KRB_AP_ERR_MODIFIED;
414     }
415 
416     ret = krb5_crypto_init(context, key, 0, &session);
417     if (ret) {
418 	free_EncryptedData(&ed);
419 	return ret;
420     }
421 
422     ret = krb5_decrypt_EncryptedData(context, session,
423 				     KRB5_KU_PA_SERVER_REFERRAL,
424 				     &ed, &data);
425     free_EncryptedData(&ed);
426     krb5_crypto_destroy(context, session);
427     if (ret)
428 	return ret;
429 
430     ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len);
431     if (ret) {
432 	krb5_data_free(&data);
433 	return ret;
434     }
435     krb5_data_free(&data);
436 
437     if (strcmp(requested->realm, returned->realm) != 0) {
438 	free_PA_ServerReferralData(&ref);
439 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
440 			       N_("server ref realm mismatch, "
441 				  "requested realm %s got back %s", ""),
442 			       requested->realm, returned->realm);
443 	return KRB5KRB_AP_ERR_MODIFIED;
444     }
445 
446     if (krb5_principal_is_krbtgt(context, returned)) {
447 	const char *realm = returned->name.name_string.val[1];
448 
449 	if (ref.referred_realm == NULL
450 	    || strcmp(*ref.referred_realm, realm) != 0)
451 	{
452 	    free_PA_ServerReferralData(&ref);
453 	    krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
454 				   N_("tgt returned with wrong ref", ""));
455 	    return KRB5KRB_AP_ERR_MODIFIED;
456 	}
457     } else if (krb5_principal_compare(context, returned, requested) == 0) {
458 	free_PA_ServerReferralData(&ref);
459 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
460 			       N_("req princ no same as returned", ""));
461 	return KRB5KRB_AP_ERR_MODIFIED;
462     }
463 
464     if (ref.requested_principal_name) {
465 	cmp = _krb5_principal_compare_PrincipalName(context,
466 						    requested,
467 						    ref.requested_principal_name);
468 	if (!cmp) {
469 	    free_PA_ServerReferralData(&ref);
470 	    krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
471 				   N_("referred principal not same "
472 				      "as requested", ""));
473 	    return KRB5KRB_AP_ERR_MODIFIED;
474 	}
475     } else if (flags & EXTRACT_TICKET_AS_REQ) {
476 	free_PA_ServerReferralData(&ref);
477 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
478 			       N_("Requested principal missing on AS-REQ", ""));
479 	return KRB5KRB_AP_ERR_MODIFIED;
480     }
481 
482     free_PA_ServerReferralData(&ref);
483 
484     return ret;
485 noreferral:
486     /*
487      * Expect excact match or that we got a krbtgt
488      */
489     if (krb5_principal_compare(context, requested, returned) != TRUE &&
490 	(krb5_realm_compare(context, requested, returned) != TRUE &&
491 	 krb5_principal_is_krbtgt(context, returned) != TRUE))
492     {
493 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
494 			       N_("Not same server principal returned "
495 				  "as requested", ""));
496 	return KRB5KRB_AP_ERR_MODIFIED;
497     }
498     return 0;
499 }
500 
501 
502 /*
503  * Verify referral data
504  */
505 
506 
507 static krb5_error_code
508 check_client_referral(krb5_context context,
509 		      krb5_kdc_rep *rep,
510 		      krb5_const_principal requested,
511 		      krb5_const_principal mapped,
512 		      krb5_keyblock const * key)
513 {
514     krb5_error_code ret;
515     PA_ClientCanonicalized canon;
516     krb5_crypto crypto;
517     krb5_data data;
518     PA_DATA *pa;
519     size_t len;
520     int i = 0;
521 
522     if (rep->kdc_rep.padata == NULL)
523 	goto noreferral;
524 
525     pa = krb5_find_padata(rep->kdc_rep.padata->val,
526 			  rep->kdc_rep.padata->len,
527 			  KRB5_PADATA_CLIENT_CANONICALIZED, &i);
528     if (pa == NULL)
529 	goto noreferral;
530 
531     ret = decode_PA_ClientCanonicalized(pa->padata_value.data,
532 					pa->padata_value.length,
533 					&canon, &len);
534     if (ret) {
535 	krb5_set_error_message(context, ret,
536 			       N_("Failed to decode ClientCanonicalized "
537 				  "from realm %s", ""), requested->realm);
538 	return ret;
539     }
540 
541     ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
542 		       &canon.names, &len, ret);
543     if (ret) {
544 	free_PA_ClientCanonicalized(&canon);
545 	return ret;
546     }
547     if (data.length != len)
548 	krb5_abortx(context, "internal asn.1 error");
549 
550     ret = krb5_crypto_init(context, key, 0, &crypto);
551     if (ret) {
552 	free(data.data);
553 	free_PA_ClientCanonicalized(&canon);
554 	return ret;
555     }
556 
557     ret = krb5_verify_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES,
558 			       data.data, data.length,
559 			       &canon.canon_checksum);
560     krb5_crypto_destroy(context, crypto);
561     free(data.data);
562     if (ret) {
563 	krb5_set_error_message(context, ret,
564 			       N_("Failed to verify client canonicalized "
565 				  "data from realm %s", ""),
566 			       requested->realm);
567 	free_PA_ClientCanonicalized(&canon);
568 	return ret;
569     }
570 
571     if (!_krb5_principal_compare_PrincipalName(context,
572 					       requested,
573 					       &canon.names.requested_name))
574     {
575 	free_PA_ClientCanonicalized(&canon);
576 	krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
577 			       N_("Requested name doesn't match"
578 				  " in client referral", ""));
579 	return KRB5_PRINC_NOMATCH;
580     }
581     if (!_krb5_principal_compare_PrincipalName(context,
582 					       mapped,
583 					       &canon.names.mapped_name))
584     {
585 	free_PA_ClientCanonicalized(&canon);
586 	krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
587 			       N_("Mapped name doesn't match"
588 				  " in client referral", ""));
589 	return KRB5_PRINC_NOMATCH;
590     }
591 
592     return 0;
593 
594 noreferral:
595     if (krb5_principal_compare(context, requested, mapped) == FALSE) {
596 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
597 			       N_("Not same client principal returned "
598 				  "as requested", ""));
599 	return KRB5KRB_AP_ERR_MODIFIED;
600     }
601     return 0;
602 }
603 
604 
605 static krb5_error_code KRB5_CALLCONV
606 decrypt_tkt (krb5_context context,
607 	     krb5_keyblock *key,
608 	     krb5_key_usage usage,
609 	     krb5_const_pointer decrypt_arg,
610 	     krb5_kdc_rep *dec_rep)
611 {
612     krb5_error_code ret;
613     krb5_data data;
614     size_t size;
615     krb5_crypto crypto;
616 
617     ret = krb5_crypto_init(context, key, 0, &crypto);
618     if (ret)
619 	return ret;
620 
621     ret = krb5_decrypt_EncryptedData (context,
622 				      crypto,
623 				      usage,
624 				      &dec_rep->kdc_rep.enc_part,
625 				      &data);
626     krb5_crypto_destroy(context, crypto);
627 
628     if (ret)
629 	return ret;
630 
631     ret = decode_EncASRepPart(data.data,
632 			      data.length,
633 			      &dec_rep->enc_part,
634 			      &size);
635     if (ret)
636 	ret = decode_EncTGSRepPart(data.data,
637 				   data.length,
638 				   &dec_rep->enc_part,
639 				   &size);
640     krb5_data_free (&data);
641     if (ret) {
642         krb5_set_error_message(context, ret,
643 			       N_("Failed to decode encpart in ticket", ""));
644 	return ret;
645     }
646     return 0;
647 }
648 
649 int
650 _krb5_extract_ticket(krb5_context context,
651 		     krb5_kdc_rep *rep,
652 		     krb5_creds *creds,
653 		     krb5_keyblock *key,
654 		     krb5_const_pointer keyseed,
655 		     krb5_key_usage key_usage,
656 		     krb5_addresses *addrs,
657 		     unsigned nonce,
658 		     unsigned flags,
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     /* save session key */
678 
679     creds->session.keyvalue.length = 0;
680     creds->session.keyvalue.data   = NULL;
681     creds->session.keytype = rep->enc_part.key.keytype;
682     ret = krb5_data_copy (&creds->session.keyvalue,
683 			  rep->enc_part.key.keyvalue.data,
684 			  rep->enc_part.key.keyvalue.length);
685     if (ret) {
686 	krb5_clear_error_message(context);
687 	goto out;
688     }
689 
690     /* compare client and save */
691     ret = _krb5_principalname2krb5_principal (context,
692 					      &tmp_principal,
693 					      rep->kdc_rep.cname,
694 					      rep->kdc_rep.crealm);
695     if (ret)
696 	goto out;
697 
698     /* check client referral and save principal */
699     /* anonymous here ? */
700     if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) {
701 	ret = check_client_referral(context, rep,
702 				    creds->client,
703 				    tmp_principal,
704 				    &creds->session);
705 	if (ret) {
706 	    krb5_free_principal (context, tmp_principal);
707 	    goto out;
708 	}
709     }
710     krb5_free_principal (context, creds->client);
711     creds->client = tmp_principal;
712 
713     /* check server referral and save principal */
714     ret = _krb5_principalname2krb5_principal (context,
715 					      &tmp_principal,
716 					      rep->kdc_rep.ticket.sname,
717 					      rep->kdc_rep.ticket.realm);
718     if (ret)
719 	goto out;
720     if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){
721 	ret = check_server_referral(context,
722 				    rep,
723 				    flags,
724 				    creds->server,
725 				    tmp_principal,
726 				    &creds->session);
727 	if (ret) {
728 	    krb5_free_principal (context, tmp_principal);
729 	    goto out;
730 	}
731     }
732     krb5_free_principal(context, creds->server);
733     creds->server = tmp_principal;
734 
735     /* verify names */
736     if(flags & EXTRACT_TICKET_MATCH_REALM){
737 	const char *srealm = krb5_principal_get_realm(context, creds->server);
738 	const char *crealm = krb5_principal_get_realm(context, creds->client);
739 
740 	if (strcmp(rep->enc_part.srealm, srealm) != 0 ||
741 	    strcmp(rep->enc_part.srealm, crealm) != 0)
742 	{
743 	    ret = KRB5KRB_AP_ERR_MODIFIED;
744 	    krb5_clear_error_message(context);
745 	    goto out;
746 	}
747     }
748 
749     /* compare nonces */
750 
751     if (nonce != (unsigned)rep->enc_part.nonce) {
752 	ret = KRB5KRB_AP_ERR_MODIFIED;
753 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
754 	goto out;
755     }
756 
757     /* set kdc-offset */
758 
759     krb5_timeofday (context, &sec_now);
760     if (rep->enc_part.flags.initial
761 	&& (flags & EXTRACT_TICKET_TIMESYNC)
762 	&& context->kdc_sec_offset == 0
763 	&& krb5_config_get_bool (context, NULL,
764 				 "libdefaults",
765 				 "kdc_timesync",
766 				 NULL)) {
767 	context->kdc_sec_offset = rep->enc_part.authtime - sec_now;
768 	krb5_timeofday (context, &sec_now);
769     }
770 
771     /* check all times */
772 
773     if (rep->enc_part.starttime) {
774 	tmp_time = *rep->enc_part.starttime;
775     } else
776 	tmp_time = rep->enc_part.authtime;
777 
778     if (creds->times.starttime == 0
779 	&& abs(tmp_time - sec_now) > context->max_skew) {
780 	ret = KRB5KRB_AP_ERR_SKEW;
781 	krb5_set_error_message (context, ret,
782 				N_("time skew (%d) larger than max (%d)", ""),
783 			       abs(tmp_time - sec_now),
784 			       (int)context->max_skew);
785 	goto out;
786     }
787 
788     if (creds->times.starttime != 0
789 	&& tmp_time != creds->times.starttime) {
790 	krb5_clear_error_message (context);
791 	ret = KRB5KRB_AP_ERR_MODIFIED;
792 	goto out;
793     }
794 
795     creds->times.starttime = tmp_time;
796 
797     if (rep->enc_part.renew_till) {
798 	tmp_time = *rep->enc_part.renew_till;
799     } else
800 	tmp_time = 0;
801 
802     if (creds->times.renew_till != 0
803 	&& tmp_time > creds->times.renew_till) {
804 	krb5_clear_error_message (context);
805 	ret = KRB5KRB_AP_ERR_MODIFIED;
806 	goto out;
807     }
808 
809     creds->times.renew_till = tmp_time;
810 
811     creds->times.authtime = rep->enc_part.authtime;
812 
813     if (creds->times.endtime != 0
814 	&& rep->enc_part.endtime > creds->times.endtime) {
815 	krb5_clear_error_message (context);
816 	ret = KRB5KRB_AP_ERR_MODIFIED;
817 	goto out;
818     }
819 
820     creds->times.endtime  = rep->enc_part.endtime;
821 
822     if(rep->enc_part.caddr)
823 	krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses);
824     else if(addrs)
825 	krb5_copy_addresses (context, addrs, &creds->addresses);
826     else {
827 	creds->addresses.len = 0;
828 	creds->addresses.val = NULL;
829     }
830     creds->flags.b = rep->enc_part.flags;
831 
832     creds->authdata.len = 0;
833     creds->authdata.val = NULL;
834 
835     /* extract ticket */
836     ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
837 		       &rep->kdc_rep.ticket, &len, ret);
838     if(ret)
839 	goto out;
840     if (creds->ticket.length != len)
841 	krb5_abortx(context, "internal error in ASN.1 encoder");
842     creds->second_ticket.length = 0;
843     creds->second_ticket.data   = NULL;
844 
845 
846 out:
847     memset (rep->enc_part.key.keyvalue.data, 0,
848 	    rep->enc_part.key.keyvalue.length);
849     return ret;
850 }
851