1 /*	$NetBSD: ticket.c,v 1.1.1.2 2014/04/24 12:45:51 pettai 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 
52 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_free_ticket(krb5_context context,krb5_ticket * ticket)53 krb5_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 
76 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_ticket(krb5_context context,const krb5_ticket * from,krb5_ticket ** to)77 krb5_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 	krb5_set_error_message(context, ENOMEM,
88 			       N_("malloc: out of memory", ""));
89 	return ENOMEM;
90     }
91     if((ret = copy_EncTicketPart(&from->ticket, &tmp->ticket))){
92 	free(tmp);
93 	return ret;
94     }
95     ret = krb5_copy_principal(context, from->client, &tmp->client);
96     if(ret){
97 	free_EncTicketPart(&tmp->ticket);
98 	free(tmp);
99 	return ret;
100     }
101     ret = krb5_copy_principal(context, from->server, &tmp->server);
102     if(ret){
103 	krb5_free_principal(context, tmp->client);
104 	free_EncTicketPart(&tmp->ticket);
105 	free(tmp);
106 	return ret;
107     }
108     *to = tmp;
109     return 0;
110 }
111 
112 /**
113  * Return client principal in ticket
114  *
115  * @param context a Kerberos 5 context
116  * @param ticket ticket to copy
117  * @param client client principal, free with krb5_free_principal()
118  *
119  * @return Returns 0 to indicate success.  Otherwise an kerberos et
120  * error code is returned, see krb5_get_error_message().
121  *
122  * @ingroup krb5
123  */
124 
125 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ticket_get_client(krb5_context context,const krb5_ticket * ticket,krb5_principal * client)126 krb5_ticket_get_client(krb5_context context,
127 		       const krb5_ticket *ticket,
128 		       krb5_principal *client)
129 {
130     return krb5_copy_principal(context, ticket->client, client);
131 }
132 
133 /**
134  * Return server principal in ticket
135  *
136  * @param context a Kerberos 5 context
137  * @param ticket ticket to copy
138  * @param server server principal, free with krb5_free_principal()
139  *
140  * @return Returns 0 to indicate success.  Otherwise an kerberos et
141  * error code is returned, see krb5_get_error_message().
142  *
143  * @ingroup krb5
144  */
145 
146 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ticket_get_server(krb5_context context,const krb5_ticket * ticket,krb5_principal * server)147 krb5_ticket_get_server(krb5_context context,
148 		       const krb5_ticket *ticket,
149 		       krb5_principal *server)
150 {
151     return krb5_copy_principal(context, ticket->server, server);
152 }
153 
154 /**
155  * Return end time of ticket
156  *
157  * @param context a Kerberos 5 context
158  * @param ticket ticket to copy
159  *
160  * @return end time of ticket
161  *
162  * @ingroup krb5
163  */
164 
165 KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
krb5_ticket_get_endtime(krb5_context context,const krb5_ticket * ticket)166 krb5_ticket_get_endtime(krb5_context context,
167 			const krb5_ticket *ticket)
168 {
169     return ticket->ticket.endtime;
170 }
171 
172 /**
173  * Get the flags from the Kerberos ticket
174  *
175  * @param context Kerberos context
176  * @param ticket Kerberos ticket
177  *
178  * @return ticket flags
179  *
180  * @ingroup krb5_ticket
181  */
182 KRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL
krb5_ticket_get_flags(krb5_context context,const krb5_ticket * ticket)183 krb5_ticket_get_flags(krb5_context context,
184 		      const krb5_ticket *ticket)
185 {
186     return TicketFlags2int(ticket->ticket.flags);
187 }
188 
189 static int
find_type_in_ad(krb5_context context,int type,krb5_data * data,krb5_boolean * found,krb5_boolean failp,krb5_keyblock * sessionkey,const AuthorizationData * ad,int level)190 find_type_in_ad(krb5_context context,
191 		int type,
192 		krb5_data *data,
193 		krb5_boolean *found,
194 		krb5_boolean failp,
195 		krb5_keyblock *sessionkey,
196 		const AuthorizationData *ad,
197 		int level)
198 {
199     krb5_error_code ret = 0;
200     size_t i;
201 
202     if (level > 9) {
203 	ret = ENOENT; /* XXX */
204 	krb5_set_error_message(context, ret,
205 			       N_("Authorization data nested deeper "
206 				  "then %d levels, stop searching", ""),
207 			       level);
208 	goto out;
209     }
210 
211     /*
212      * Only copy out the element the first time we get to it, we need
213      * to run over the whole authorization data fields to check if
214      * there are any container clases we need to care about.
215      */
216     for (i = 0; i < ad->len; i++) {
217 	if (!*found && ad->val[i].ad_type == type) {
218 	    ret = der_copy_octet_string(&ad->val[i].ad_data, data);
219 	    if (ret) {
220 		krb5_set_error_message(context, ret,
221 				       N_("malloc: out of memory", ""));
222 		goto out;
223 	    }
224 	    *found = TRUE;
225 	    continue;
226 	}
227 	switch (ad->val[i].ad_type) {
228 	case KRB5_AUTHDATA_IF_RELEVANT: {
229 	    AuthorizationData child;
230 	    ret = decode_AuthorizationData(ad->val[i].ad_data.data,
231 					   ad->val[i].ad_data.length,
232 					   &child,
233 					   NULL);
234 	    if (ret) {
235 		krb5_set_error_message(context, ret,
236 				       N_("Failed to decode "
237 					  "IF_RELEVANT with %d", ""),
238 				       (int)ret);
239 		goto out;
240 	    }
241 	    ret = find_type_in_ad(context, type, data, found, FALSE,
242 				  sessionkey, &child, level + 1);
243 	    free_AuthorizationData(&child);
244 	    if (ret)
245 		goto out;
246 	    break;
247 	}
248 #if 0 /* XXX test */
249 	case KRB5_AUTHDATA_KDC_ISSUED: {
250 	    AD_KDCIssued child;
251 
252 	    ret = decode_AD_KDCIssued(ad->val[i].ad_data.data,
253 				      ad->val[i].ad_data.length,
254 				      &child,
255 				      NULL);
256 	    if (ret) {
257 		krb5_set_error_message(context, ret,
258 				       N_("Failed to decode "
259 					  "AD_KDCIssued with %d", ""),
260 				       ret);
261 		goto out;
262 	    }
263 	    if (failp) {
264 		krb5_boolean valid;
265 		krb5_data buf;
266 		size_t len;
267 
268 		ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length,
269 				   &child.elements, &len, ret);
270 		if (ret) {
271 		    free_AD_KDCIssued(&child);
272 		    krb5_clear_error_message(context);
273 		    goto out;
274 		}
275 		if(buf.length != len)
276 		    krb5_abortx(context, "internal error in ASN.1 encoder");
277 
278 		ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf,
279 					     &child.ad_checksum, &valid);
280 		krb5_data_free(&buf);
281 		if (ret) {
282 		    free_AD_KDCIssued(&child);
283 		    goto out;
284 		}
285 		if (!valid) {
286 		    krb5_clear_error_message(context);
287 		    ret = ENOENT;
288 		    free_AD_KDCIssued(&child);
289 		    goto out;
290 		}
291 	    }
292 	    ret = find_type_in_ad(context, type, data, found, failp, sessionkey,
293 				  &child.elements, level + 1);
294 	    free_AD_KDCIssued(&child);
295 	    if (ret)
296 		goto out;
297 	    break;
298 	}
299 #endif
300 	case KRB5_AUTHDATA_AND_OR:
301 	    if (!failp)
302 		break;
303 	    ret = ENOENT; /* XXX */
304 	    krb5_set_error_message(context, ret,
305 				   N_("Authorization data contains "
306 				      "AND-OR element that is unknown to the "
307 				      "application", ""));
308 	    goto out;
309 	default:
310 	    if (!failp)
311 		break;
312 	    ret = ENOENT; /* XXX */
313 	    krb5_set_error_message(context, ret,
314 				   N_("Authorization data contains "
315 				      "unknown type (%d) ", ""),
316 				   ad->val[i].ad_type);
317 	    goto out;
318 	}
319     }
320 out:
321     if (ret) {
322 	if (*found) {
323 	    krb5_data_free(data);
324 	    *found = 0;
325 	}
326     }
327     return ret;
328 }
329 
330 /**
331  * Extract the authorization data type of type from the ticket. Store
332  * the field in data. This function is to use for kerberos
333  * applications.
334  *
335  * @param context a Kerberos 5 context
336  * @param ticket Kerberos ticket
337  * @param type type to fetch
338  * @param data returned data, free with krb5_data_free()
339  *
340  * @ingroup krb5
341  */
342 
343 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ticket_get_authorization_data_type(krb5_context context,krb5_ticket * ticket,int type,krb5_data * data)344 krb5_ticket_get_authorization_data_type(krb5_context context,
345 					krb5_ticket *ticket,
346 					int type,
347 					krb5_data *data)
348 {
349     AuthorizationData *ad;
350     krb5_error_code ret;
351     krb5_boolean found = FALSE;
352 
353     krb5_data_zero(data);
354 
355     ad = ticket->ticket.authorization_data;
356     if (ticket->ticket.authorization_data == NULL) {
357 	krb5_set_error_message(context, ENOENT,
358 			       N_("Ticket have not authorization data", ""));
359 	return ENOENT; /* XXX */
360     }
361 
362     ret = find_type_in_ad(context, type, data, &found, TRUE,
363 			  &ticket->ticket.key, ad, 0);
364     if (ret)
365 	return ret;
366     if (!found) {
367 	krb5_set_error_message(context, ENOENT,
368 			       N_("Ticket have not "
369 				  "authorization data of type %d", ""),
370 			       type);
371 	return ENOENT; /* XXX */
372     }
373     return 0;
374 }
375 
376 static krb5_error_code
check_server_referral(krb5_context context,krb5_kdc_rep * rep,unsigned flags,krb5_const_principal requested,krb5_const_principal returned,krb5_keyblock * key)377 check_server_referral(krb5_context context,
378 		      krb5_kdc_rep *rep,
379 		      unsigned flags,
380 		      krb5_const_principal requested,
381 		      krb5_const_principal returned,
382 		      krb5_keyblock * key)
383 {
384     krb5_error_code ret;
385     PA_ServerReferralData ref;
386     krb5_crypto session;
387     EncryptedData ed;
388     size_t len;
389     krb5_data data;
390     PA_DATA *pa;
391     int i = 0, cmp;
392 
393     if (rep->kdc_rep.padata == NULL)
394 	goto noreferral;
395 
396     pa = krb5_find_padata(rep->kdc_rep.padata->val,
397 			  rep->kdc_rep.padata->len,
398 			  KRB5_PADATA_SERVER_REFERRAL, &i);
399     if (pa == NULL)
400 	goto noreferral;
401 
402     memset(&ed, 0, sizeof(ed));
403     memset(&ref, 0, sizeof(ref));
404 
405     ret = decode_EncryptedData(pa->padata_value.data,
406 			       pa->padata_value.length,
407 			       &ed, &len);
408     if (ret)
409 	return ret;
410     if (len != pa->padata_value.length) {
411 	free_EncryptedData(&ed);
412 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
413 			       N_("Referral EncryptedData wrong for realm %s",
414 				  "realm"), requested->realm);
415 	return KRB5KRB_AP_ERR_MODIFIED;
416     }
417 
418     ret = krb5_crypto_init(context, key, 0, &session);
419     if (ret) {
420 	free_EncryptedData(&ed);
421 	return ret;
422     }
423 
424     ret = krb5_decrypt_EncryptedData(context, session,
425 				     KRB5_KU_PA_SERVER_REFERRAL,
426 				     &ed, &data);
427     free_EncryptedData(&ed);
428     krb5_crypto_destroy(context, session);
429     if (ret)
430 	return ret;
431 
432     ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len);
433     if (ret) {
434 	krb5_data_free(&data);
435 	return ret;
436     }
437     krb5_data_free(&data);
438 
439     if (strcmp(requested->realm, returned->realm) != 0) {
440 	free_PA_ServerReferralData(&ref);
441 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
442 			       N_("server ref realm mismatch, "
443 				  "requested realm %s got back %s", ""),
444 			       requested->realm, returned->realm);
445 	return KRB5KRB_AP_ERR_MODIFIED;
446     }
447 
448     if (krb5_principal_is_krbtgt(context, returned)) {
449 	const char *realm = returned->name.name_string.val[1];
450 
451 	if (ref.referred_realm == NULL
452 	    || strcmp(*ref.referred_realm, realm) != 0)
453 	{
454 	    free_PA_ServerReferralData(&ref);
455 	    krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
456 				   N_("tgt returned with wrong ref", ""));
457 	    return KRB5KRB_AP_ERR_MODIFIED;
458 	}
459     } else if (krb5_principal_compare(context, returned, requested) == 0) {
460 	free_PA_ServerReferralData(&ref);
461 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
462 			       N_("req princ no same as returned", ""));
463 	return KRB5KRB_AP_ERR_MODIFIED;
464     }
465 
466     if (ref.requested_principal_name) {
467 	cmp = _krb5_principal_compare_PrincipalName(context,
468 						    requested,
469 						    ref.requested_principal_name);
470 	if (!cmp) {
471 	    free_PA_ServerReferralData(&ref);
472 	    krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
473 				   N_("referred principal not same "
474 				      "as requested", ""));
475 	    return KRB5KRB_AP_ERR_MODIFIED;
476 	}
477     } else if (flags & EXTRACT_TICKET_AS_REQ) {
478 	free_PA_ServerReferralData(&ref);
479 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
480 			       N_("Requested principal missing on AS-REQ", ""));
481 	return KRB5KRB_AP_ERR_MODIFIED;
482     }
483 
484     free_PA_ServerReferralData(&ref);
485 
486     return ret;
487 noreferral:
488     /*
489      * Expect excact match or that we got a krbtgt
490      */
491     if (krb5_principal_compare(context, requested, returned) != TRUE &&
492 	(krb5_realm_compare(context, requested, returned) != TRUE &&
493 	 krb5_principal_is_krbtgt(context, returned) != TRUE))
494     {
495 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
496 			       N_("Not same server principal returned "
497 				  "as requested", ""));
498 	return KRB5KRB_AP_ERR_MODIFIED;
499     }
500     return 0;
501 }
502 
503 
504 /*
505  * Verify referral data
506  */
507 
508 
509 static krb5_error_code
check_client_referral(krb5_context context,krb5_kdc_rep * rep,krb5_const_principal requested,krb5_const_principal mapped,krb5_keyblock const * key)510 check_client_referral(krb5_context context,
511 		      krb5_kdc_rep *rep,
512 		      krb5_const_principal requested,
513 		      krb5_const_principal mapped,
514 		      krb5_keyblock const * key)
515 {
516     krb5_error_code ret;
517     PA_ClientCanonicalized canon;
518     krb5_crypto crypto;
519     krb5_data data;
520     PA_DATA *pa;
521     size_t len;
522     int i = 0;
523 
524     if (rep->kdc_rep.padata == NULL)
525 	goto noreferral;
526 
527     pa = krb5_find_padata(rep->kdc_rep.padata->val,
528 			  rep->kdc_rep.padata->len,
529 			  KRB5_PADATA_CLIENT_CANONICALIZED, &i);
530     if (pa == NULL)
531 	goto noreferral;
532 
533     ret = decode_PA_ClientCanonicalized(pa->padata_value.data,
534 					pa->padata_value.length,
535 					&canon, &len);
536     if (ret) {
537 	krb5_set_error_message(context, ret,
538 			       N_("Failed to decode ClientCanonicalized "
539 				  "from realm %s", ""), requested->realm);
540 	return ret;
541     }
542 
543     ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
544 		       &canon.names, &len, ret);
545     if (ret) {
546 	free_PA_ClientCanonicalized(&canon);
547 	return ret;
548     }
549     if (data.length != len)
550 	krb5_abortx(context, "internal asn.1 error");
551 
552     ret = krb5_crypto_init(context, key, 0, &crypto);
553     if (ret) {
554 	free(data.data);
555 	free_PA_ClientCanonicalized(&canon);
556 	return ret;
557     }
558 
559     ret = krb5_verify_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES,
560 			       data.data, data.length,
561 			       &canon.canon_checksum);
562     krb5_crypto_destroy(context, crypto);
563     free(data.data);
564     if (ret) {
565 	krb5_set_error_message(context, ret,
566 			       N_("Failed to verify client canonicalized "
567 				  "data from realm %s", ""),
568 			       requested->realm);
569 	free_PA_ClientCanonicalized(&canon);
570 	return ret;
571     }
572 
573     if (!_krb5_principal_compare_PrincipalName(context,
574 					       requested,
575 					       &canon.names.requested_name))
576     {
577 	free_PA_ClientCanonicalized(&canon);
578 	krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
579 			       N_("Requested name doesn't match"
580 				  " in client referral", ""));
581 	return KRB5_PRINC_NOMATCH;
582     }
583     if (!_krb5_principal_compare_PrincipalName(context,
584 					       mapped,
585 					       &canon.names.mapped_name))
586     {
587 	free_PA_ClientCanonicalized(&canon);
588 	krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
589 			       N_("Mapped name doesn't match"
590 				  " in client referral", ""));
591 	return KRB5_PRINC_NOMATCH;
592     }
593 
594     return 0;
595 
596 noreferral:
597     if (krb5_principal_compare(context, requested, mapped) == FALSE) {
598 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
599 			       N_("Not same client principal returned "
600 				  "as requested", ""));
601 	return KRB5KRB_AP_ERR_MODIFIED;
602     }
603     return 0;
604 }
605 
606 
607 static krb5_error_code KRB5_CALLCONV
decrypt_tkt(krb5_context context,krb5_keyblock * key,krb5_key_usage usage,krb5_const_pointer decrypt_arg,krb5_kdc_rep * dec_rep)608 decrypt_tkt (krb5_context context,
609 	     krb5_keyblock *key,
610 	     krb5_key_usage usage,
611 	     krb5_const_pointer decrypt_arg,
612 	     krb5_kdc_rep *dec_rep)
613 {
614     krb5_error_code ret;
615     krb5_data data;
616     size_t size;
617     krb5_crypto crypto;
618 
619     ret = krb5_crypto_init(context, key, 0, &crypto);
620     if (ret)
621 	return ret;
622 
623     ret = krb5_decrypt_EncryptedData (context,
624 				      crypto,
625 				      usage,
626 				      &dec_rep->kdc_rep.enc_part,
627 				      &data);
628     krb5_crypto_destroy(context, crypto);
629 
630     if (ret)
631 	return ret;
632 
633     ret = decode_EncASRepPart(data.data,
634 			      data.length,
635 			      &dec_rep->enc_part,
636 			      &size);
637     if (ret)
638 	ret = decode_EncTGSRepPart(data.data,
639 				   data.length,
640 				   &dec_rep->enc_part,
641 				   &size);
642     krb5_data_free (&data);
643     if (ret) {
644         krb5_set_error_message(context, ret,
645 			       N_("Failed to decode encpart in ticket", ""));
646 	return ret;
647     }
648     return 0;
649 }
650 
651 int
_krb5_extract_ticket(krb5_context context,krb5_kdc_rep * rep,krb5_creds * creds,krb5_keyblock * key,krb5_const_pointer keyseed,krb5_key_usage key_usage,krb5_addresses * addrs,unsigned nonce,unsigned flags,krb5_decrypt_proc decrypt_proc,krb5_const_pointer decryptarg)652 _krb5_extract_ticket(krb5_context context,
653 		     krb5_kdc_rep *rep,
654 		     krb5_creds *creds,
655 		     krb5_keyblock *key,
656 		     krb5_const_pointer keyseed,
657 		     krb5_key_usage key_usage,
658 		     krb5_addresses *addrs,
659 		     unsigned nonce,
660 		     unsigned flags,
661 		     krb5_decrypt_proc decrypt_proc,
662 		     krb5_const_pointer decryptarg)
663 {
664     krb5_error_code ret;
665     krb5_principal tmp_principal;
666     size_t len = 0;
667     time_t tmp_time;
668     krb5_timestamp sec_now;
669 
670     /* decrypt */
671 
672     if (decrypt_proc == NULL)
673 	decrypt_proc = decrypt_tkt;
674 
675     ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep);
676     if (ret)
677 	goto out;
678 
679     /* save session key */
680 
681     creds->session.keyvalue.length = 0;
682     creds->session.keyvalue.data   = NULL;
683     creds->session.keytype = rep->enc_part.key.keytype;
684     ret = krb5_data_copy (&creds->session.keyvalue,
685 			  rep->enc_part.key.keyvalue.data,
686 			  rep->enc_part.key.keyvalue.length);
687     if (ret) {
688 	krb5_clear_error_message(context);
689 	goto out;
690     }
691 
692     /* compare client and save */
693     ret = _krb5_principalname2krb5_principal (context,
694 					      &tmp_principal,
695 					      rep->kdc_rep.cname,
696 					      rep->kdc_rep.crealm);
697     if (ret)
698 	goto out;
699 
700     /* check client referral and save principal */
701     /* anonymous here ? */
702     if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) {
703 	ret = check_client_referral(context, rep,
704 				    creds->client,
705 				    tmp_principal,
706 				    &creds->session);
707 	if (ret) {
708 	    krb5_free_principal (context, tmp_principal);
709 	    goto out;
710 	}
711     }
712     krb5_free_principal (context, creds->client);
713     creds->client = tmp_principal;
714 
715     /* check server referral and save principal */
716     ret = _krb5_principalname2krb5_principal (context,
717 					      &tmp_principal,
718 					      rep->kdc_rep.ticket.sname,
719 					      rep->kdc_rep.ticket.realm);
720     if (ret)
721 	goto out;
722     if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){
723 	ret = check_server_referral(context,
724 				    rep,
725 				    flags,
726 				    creds->server,
727 				    tmp_principal,
728 				    &creds->session);
729 	if (ret) {
730 	    krb5_free_principal (context, tmp_principal);
731 	    goto out;
732 	}
733     }
734     krb5_free_principal(context, creds->server);
735     creds->server = tmp_principal;
736 
737     /* verify names */
738     if(flags & EXTRACT_TICKET_MATCH_REALM){
739 	const char *srealm = krb5_principal_get_realm(context, creds->server);
740 	const char *crealm = krb5_principal_get_realm(context, creds->client);
741 
742 	if (strcmp(rep->enc_part.srealm, srealm) != 0 ||
743 	    strcmp(rep->enc_part.srealm, crealm) != 0)
744 	{
745 	    ret = KRB5KRB_AP_ERR_MODIFIED;
746 	    krb5_clear_error_message(context);
747 	    goto out;
748 	}
749     }
750 
751     /* compare nonces */
752 
753     if (nonce != (unsigned)rep->enc_part.nonce) {
754 	ret = KRB5KRB_AP_ERR_MODIFIED;
755 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
756 	goto out;
757     }
758 
759     /* set kdc-offset */
760 
761     krb5_timeofday (context, &sec_now);
762     if (rep->enc_part.flags.initial
763 	&& (flags & EXTRACT_TICKET_TIMESYNC)
764 	&& context->kdc_sec_offset == 0
765 	&& krb5_config_get_bool (context, NULL,
766 				 "libdefaults",
767 				 "kdc_timesync",
768 				 NULL)) {
769 	context->kdc_sec_offset = rep->enc_part.authtime - sec_now;
770 	krb5_timeofday (context, &sec_now);
771     }
772 
773     /* check all times */
774 
775     if (rep->enc_part.starttime) {
776 	tmp_time = *rep->enc_part.starttime;
777     } else
778 	tmp_time = rep->enc_part.authtime;
779 
780     if (creds->times.starttime == 0
781 	&& abs(tmp_time - sec_now) > context->max_skew) {
782 	ret = KRB5KRB_AP_ERR_SKEW;
783 	krb5_set_error_message (context, ret,
784 				N_("time skew (%d) larger than max (%d)", ""),
785 			       abs(tmp_time - sec_now),
786 			       (int)context->max_skew);
787 	goto out;
788     }
789 
790     if (creds->times.starttime != 0
791 	&& tmp_time != creds->times.starttime) {
792 	krb5_clear_error_message (context);
793 	ret = KRB5KRB_AP_ERR_MODIFIED;
794 	goto out;
795     }
796 
797     creds->times.starttime = tmp_time;
798 
799     if (rep->enc_part.renew_till) {
800 	tmp_time = *rep->enc_part.renew_till;
801     } else
802 	tmp_time = 0;
803 
804     if (creds->times.renew_till != 0
805 	&& tmp_time > creds->times.renew_till) {
806 	krb5_clear_error_message (context);
807 	ret = KRB5KRB_AP_ERR_MODIFIED;
808 	goto out;
809     }
810 
811     creds->times.renew_till = tmp_time;
812 
813     creds->times.authtime = rep->enc_part.authtime;
814 
815     if (creds->times.endtime != 0
816 	&& rep->enc_part.endtime > creds->times.endtime) {
817 	krb5_clear_error_message (context);
818 	ret = KRB5KRB_AP_ERR_MODIFIED;
819 	goto out;
820     }
821 
822     creds->times.endtime  = rep->enc_part.endtime;
823 
824     if(rep->enc_part.caddr)
825 	krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses);
826     else if(addrs)
827 	krb5_copy_addresses (context, addrs, &creds->addresses);
828     else {
829 	creds->addresses.len = 0;
830 	creds->addresses.val = NULL;
831     }
832     creds->flags.b = rep->enc_part.flags;
833 
834     creds->authdata.len = 0;
835     creds->authdata.val = NULL;
836 
837     /* extract ticket */
838     ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
839 		       &rep->kdc_rep.ticket, &len, ret);
840     if(ret)
841 	goto out;
842     if (creds->ticket.length != len)
843 	krb5_abortx(context, "internal error in ASN.1 encoder");
844     creds->second_ticket.length = 0;
845     creds->second_ticket.data   = NULL;
846 
847 
848 out:
849     memset (rep->enc_part.key.keyvalue.data, 0,
850 	    rep->enc_part.key.keyvalue.length);
851     return ret;
852 }
853