155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2001 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
755682Smarkm *
8233294Sstas * Redistribution and use in source and binary forms, with or without
9233294Sstas * modification, are permitted provided that the following conditions
10233294Sstas * are met:
1155682Smarkm *
12233294Sstas * 1. Redistributions of source code must retain the above copyright
13233294Sstas *    notice, this list of conditions and the following disclaimer.
1455682Smarkm *
15233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
16233294Sstas *    notice, this list of conditions and the following disclaimer in the
17233294Sstas *    documentation and/or other materials provided with the distribution.
1855682Smarkm *
19233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
20233294Sstas *    may be used to endorse or promote products derived from this software
21233294Sstas *    without specific prior written permission.
22233294Sstas *
23233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33233294Sstas * SUCH DAMAGE.
3455682Smarkm */
3555682Smarkm
3655682Smarkm#include "krb5_locl.h"
3755682Smarkm
38233294Sstas/**
39233294Sstas * Free ticket and content
40233294Sstas *
41233294Sstas * @param context a Kerberos 5 context
42233294Sstas * @param ticket ticket to free
43233294Sstas *
44233294Sstas * @return Returns 0 to indicate success.  Otherwise an kerberos et
45233294Sstas * error code is returned, see krb5_get_error_message().
46233294Sstas *
47233294Sstas * @ingroup krb5
48233294Sstas */
4955682Smarkm
50233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
5155682Smarkmkrb5_free_ticket(krb5_context context,
5255682Smarkm		 krb5_ticket *ticket)
5355682Smarkm{
5455682Smarkm    free_EncTicketPart(&ticket->ticket);
5555682Smarkm    krb5_free_principal(context, ticket->client);
5655682Smarkm    krb5_free_principal(context, ticket->server);
57178825Sdfr    free(ticket);
5855682Smarkm    return 0;
5955682Smarkm}
6055682Smarkm
61233294Sstas/**
62233294Sstas * Copy ticket and content
63233294Sstas *
64233294Sstas * @param context a Kerberos 5 context
65233294Sstas * @param from ticket to copy
66233294Sstas * @param to new copy of ticket, free with krb5_free_ticket()
67233294Sstas *
68233294Sstas * @return Returns 0 to indicate success.  Otherwise an kerberos et
69233294Sstas * error code is returned, see krb5_get_error_message().
70233294Sstas *
71233294Sstas * @ingroup krb5
72233294Sstas */
73233294Sstas
74233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
7555682Smarkmkrb5_copy_ticket(krb5_context context,
7655682Smarkm		 const krb5_ticket *from,
7755682Smarkm		 krb5_ticket **to)
7855682Smarkm{
7955682Smarkm    krb5_error_code ret;
80127808Snectar    krb5_ticket *tmp;
81127808Snectar
82127808Snectar    *to = NULL;
83127808Snectar    tmp = malloc(sizeof(*tmp));
8478527Sassar    if(tmp == NULL) {
85233294Sstas	krb5_set_error_message(context, ENOMEM,
86233294Sstas			       N_("malloc: out of memory", ""));
8755682Smarkm	return ENOMEM;
8878527Sassar    }
8955682Smarkm    if((ret = copy_EncTicketPart(&from->ticket, &tmp->ticket))){
9055682Smarkm	free(tmp);
9155682Smarkm	return ret;
9255682Smarkm    }
9355682Smarkm    ret = krb5_copy_principal(context, from->client, &tmp->client);
9455682Smarkm    if(ret){
9555682Smarkm	free_EncTicketPart(&tmp->ticket);
96127808Snectar	free(tmp);
9755682Smarkm	return ret;
9855682Smarkm    }
99127808Snectar    ret = krb5_copy_principal(context, from->server, &tmp->server);
10055682Smarkm    if(ret){
10155682Smarkm	krb5_free_principal(context, tmp->client);
10255682Smarkm	free_EncTicketPart(&tmp->ticket);
103127808Snectar	free(tmp);
10455682Smarkm	return ret;
10555682Smarkm    }
10655682Smarkm    *to = tmp;
10755682Smarkm    return 0;
10855682Smarkm}
109178825Sdfr
110233294Sstas/**
111233294Sstas * Return client principal in ticket
112233294Sstas *
113233294Sstas * @param context a Kerberos 5 context
114233294Sstas * @param ticket ticket to copy
115233294Sstas * @param client client principal, free with krb5_free_principal()
116233294Sstas *
117233294Sstas * @return Returns 0 to indicate success.  Otherwise an kerberos et
118233294Sstas * error code is returned, see krb5_get_error_message().
119233294Sstas *
120233294Sstas * @ingroup krb5
121233294Sstas */
122233294Sstas
123233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
124178825Sdfrkrb5_ticket_get_client(krb5_context context,
125178825Sdfr		       const krb5_ticket *ticket,
126178825Sdfr		       krb5_principal *client)
127178825Sdfr{
128178825Sdfr    return krb5_copy_principal(context, ticket->client, client);
129178825Sdfr}
130178825Sdfr
131233294Sstas/**
132233294Sstas * Return server principal in ticket
133233294Sstas *
134233294Sstas * @param context a Kerberos 5 context
135233294Sstas * @param ticket ticket to copy
136233294Sstas * @param server server principal, free with krb5_free_principal()
137233294Sstas *
138233294Sstas * @return Returns 0 to indicate success.  Otherwise an kerberos et
139233294Sstas * error code is returned, see krb5_get_error_message().
140233294Sstas *
141233294Sstas * @ingroup krb5
142233294Sstas */
143233294Sstas
144233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
145178825Sdfrkrb5_ticket_get_server(krb5_context context,
146178825Sdfr		       const krb5_ticket *ticket,
147178825Sdfr		       krb5_principal *server)
148178825Sdfr{
149178825Sdfr    return krb5_copy_principal(context, ticket->server, server);
150178825Sdfr}
151178825Sdfr
152233294Sstas/**
153233294Sstas * Return end time of ticket
154233294Sstas *
155233294Sstas * @param context a Kerberos 5 context
156233294Sstas * @param ticket ticket to copy
157233294Sstas *
158233294Sstas * @return end time of ticket
159233294Sstas *
160233294Sstas * @ingroup krb5
161233294Sstas */
162233294Sstas
163233294SstasKRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
164178825Sdfrkrb5_ticket_get_endtime(krb5_context context,
165178825Sdfr			const krb5_ticket *ticket)
166178825Sdfr{
167178825Sdfr    return ticket->ticket.endtime;
168178825Sdfr}
169178825Sdfr
170233294Sstas/**
171233294Sstas * Get the flags from the Kerberos ticket
172233294Sstas *
173233294Sstas * @param context Kerberos context
174233294Sstas * @param ticket Kerberos ticket
175233294Sstas *
176233294Sstas * @return ticket flags
177233294Sstas *
178233294Sstas * @ingroup krb5_ticket
179233294Sstas */
180233294SstasKRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL
181233294Sstaskrb5_ticket_get_flags(krb5_context context,
182233294Sstas		      const krb5_ticket *ticket)
183233294Sstas{
184233294Sstas    return TicketFlags2int(ticket->ticket.flags);
185233294Sstas}
186233294Sstas
187178825Sdfrstatic int
188178825Sdfrfind_type_in_ad(krb5_context context,
189233294Sstas		int type,
190178825Sdfr		krb5_data *data,
191178825Sdfr		krb5_boolean *found,
192178825Sdfr		krb5_boolean failp,
193178825Sdfr		krb5_keyblock *sessionkey,
194178825Sdfr		const AuthorizationData *ad,
195178825Sdfr		int level)
196178825Sdfr{
197178825Sdfr    krb5_error_code ret = 0;
198233294Sstas    size_t i;
199178825Sdfr
200178825Sdfr    if (level > 9) {
201178825Sdfr	ret = ENOENT; /* XXX */
202233294Sstas	krb5_set_error_message(context, ret,
203233294Sstas			       N_("Authorization data nested deeper "
204233294Sstas				  "then %d levels, stop searching", ""),
205233294Sstas			       level);
206178825Sdfr	goto out;
207178825Sdfr    }
208178825Sdfr
209178825Sdfr    /*
210178825Sdfr     * Only copy out the element the first time we get to it, we need
211178825Sdfr     * to run over the whole authorization data fields to check if
212178825Sdfr     * there are any container clases we need to care about.
213178825Sdfr     */
214178825Sdfr    for (i = 0; i < ad->len; i++) {
215178825Sdfr	if (!*found && ad->val[i].ad_type == type) {
216178825Sdfr	    ret = der_copy_octet_string(&ad->val[i].ad_data, data);
217178825Sdfr	    if (ret) {
218233294Sstas		krb5_set_error_message(context, ret,
219233294Sstas				       N_("malloc: out of memory", ""));
220178825Sdfr		goto out;
221178825Sdfr	    }
222178825Sdfr	    *found = TRUE;
223178825Sdfr	    continue;
224178825Sdfr	}
225178825Sdfr	switch (ad->val[i].ad_type) {
226178825Sdfr	case KRB5_AUTHDATA_IF_RELEVANT: {
227178825Sdfr	    AuthorizationData child;
228178825Sdfr	    ret = decode_AuthorizationData(ad->val[i].ad_data.data,
229178825Sdfr					   ad->val[i].ad_data.length,
230178825Sdfr					   &child,
231178825Sdfr					   NULL);
232178825Sdfr	    if (ret) {
233233294Sstas		krb5_set_error_message(context, ret,
234233294Sstas				       N_("Failed to decode "
235233294Sstas					  "IF_RELEVANT with %d", ""),
236233294Sstas				       (int)ret);
237178825Sdfr		goto out;
238178825Sdfr	    }
239178825Sdfr	    ret = find_type_in_ad(context, type, data, found, FALSE,
240178825Sdfr				  sessionkey, &child, level + 1);
241178825Sdfr	    free_AuthorizationData(&child);
242178825Sdfr	    if (ret)
243178825Sdfr		goto out;
244178825Sdfr	    break;
245178825Sdfr	}
246178825Sdfr#if 0 /* XXX test */
247178825Sdfr	case KRB5_AUTHDATA_KDC_ISSUED: {
248178825Sdfr	    AD_KDCIssued child;
249178825Sdfr
250178825Sdfr	    ret = decode_AD_KDCIssued(ad->val[i].ad_data.data,
251178825Sdfr				      ad->val[i].ad_data.length,
252178825Sdfr				      &child,
253178825Sdfr				      NULL);
254178825Sdfr	    if (ret) {
255233294Sstas		krb5_set_error_message(context, ret,
256233294Sstas				       N_("Failed to decode "
257233294Sstas					  "AD_KDCIssued with %d", ""),
258233294Sstas				       ret);
259178825Sdfr		goto out;
260178825Sdfr	    }
261178825Sdfr	    if (failp) {
262178825Sdfr		krb5_boolean valid;
263178825Sdfr		krb5_data buf;
264178825Sdfr		size_t len;
265178825Sdfr
266233294Sstas		ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length,
267178825Sdfr				   &child.elements, &len, ret);
268178825Sdfr		if (ret) {
269178825Sdfr		    free_AD_KDCIssued(&child);
270233294Sstas		    krb5_clear_error_message(context);
271178825Sdfr		    goto out;
272178825Sdfr		}
273178825Sdfr		if(buf.length != len)
274178825Sdfr		    krb5_abortx(context, "internal error in ASN.1 encoder");
275178825Sdfr
276178825Sdfr		ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf,
277178825Sdfr					     &child.ad_checksum, &valid);
278178825Sdfr		krb5_data_free(&buf);
279178825Sdfr		if (ret) {
280178825Sdfr		    free_AD_KDCIssued(&child);
281178825Sdfr		    goto out;
282178825Sdfr		}
283178825Sdfr		if (!valid) {
284233294Sstas		    krb5_clear_error_message(context);
285178825Sdfr		    ret = ENOENT;
286178825Sdfr		    free_AD_KDCIssued(&child);
287178825Sdfr		    goto out;
288178825Sdfr		}
289178825Sdfr	    }
290178825Sdfr	    ret = find_type_in_ad(context, type, data, found, failp, sessionkey,
291178825Sdfr				  &child.elements, level + 1);
292178825Sdfr	    free_AD_KDCIssued(&child);
293178825Sdfr	    if (ret)
294178825Sdfr		goto out;
295178825Sdfr	    break;
296178825Sdfr	}
297178825Sdfr#endif
298178825Sdfr	case KRB5_AUTHDATA_AND_OR:
299178825Sdfr	    if (!failp)
300178825Sdfr		break;
301178825Sdfr	    ret = ENOENT; /* XXX */
302233294Sstas	    krb5_set_error_message(context, ret,
303233294Sstas				   N_("Authorization data contains "
304233294Sstas				      "AND-OR element that is unknown to the "
305233294Sstas				      "application", ""));
306178825Sdfr	    goto out;
307178825Sdfr	default:
308178825Sdfr	    if (!failp)
309178825Sdfr		break;
310178825Sdfr	    ret = ENOENT; /* XXX */
311233294Sstas	    krb5_set_error_message(context, ret,
312233294Sstas				   N_("Authorization data contains "
313233294Sstas				      "unknown type (%d) ", ""),
314233294Sstas				   ad->val[i].ad_type);
315178825Sdfr	    goto out;
316178825Sdfr	}
317178825Sdfr    }
318178825Sdfrout:
319178825Sdfr    if (ret) {
320178825Sdfr	if (*found) {
321178825Sdfr	    krb5_data_free(data);
322178825Sdfr	    *found = 0;
323178825Sdfr	}
324178825Sdfr    }
325178825Sdfr    return ret;
326178825Sdfr}
327178825Sdfr
328233294Sstas/**
329233294Sstas * Extract the authorization data type of type from the ticket. Store
330233294Sstas * the field in data. This function is to use for kerberos
331233294Sstas * applications.
332233294Sstas *
333233294Sstas * @param context a Kerberos 5 context
334233294Sstas * @param ticket Kerberos ticket
335233294Sstas * @param type type to fetch
336233294Sstas * @param data returned data, free with krb5_data_free()
337233294Sstas *
338233294Sstas * @ingroup krb5
339178825Sdfr */
340178825Sdfr
341233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
342178825Sdfrkrb5_ticket_get_authorization_data_type(krb5_context context,
343178825Sdfr					krb5_ticket *ticket,
344178825Sdfr					int type,
345178825Sdfr					krb5_data *data)
346178825Sdfr{
347178825Sdfr    AuthorizationData *ad;
348178825Sdfr    krb5_error_code ret;
349178825Sdfr    krb5_boolean found = FALSE;
350178825Sdfr
351178825Sdfr    krb5_data_zero(data);
352178825Sdfr
353178825Sdfr    ad = ticket->ticket.authorization_data;
354178825Sdfr    if (ticket->ticket.authorization_data == NULL) {
355233294Sstas	krb5_set_error_message(context, ENOENT,
356233294Sstas			       N_("Ticket have not authorization data", ""));
357178825Sdfr	return ENOENT; /* XXX */
358178825Sdfr    }
359178825Sdfr
360178825Sdfr    ret = find_type_in_ad(context, type, data, &found, TRUE,
361178825Sdfr			  &ticket->ticket.key, ad, 0);
362178825Sdfr    if (ret)
363178825Sdfr	return ret;
364178825Sdfr    if (!found) {
365233294Sstas	krb5_set_error_message(context, ENOENT,
366233294Sstas			       N_("Ticket have not "
367233294Sstas				  "authorization data of type %d", ""),
368233294Sstas			       type);
369178825Sdfr	return ENOENT; /* XXX */
370178825Sdfr    }
371178825Sdfr    return 0;
372178825Sdfr}
373233294Sstas
374233294Sstasstatic krb5_error_code
375233294Sstascheck_server_referral(krb5_context context,
376233294Sstas		      krb5_kdc_rep *rep,
377233294Sstas		      unsigned flags,
378233294Sstas		      krb5_const_principal requested,
379233294Sstas		      krb5_const_principal returned,
380233294Sstas		      krb5_keyblock * key)
381233294Sstas{
382233294Sstas    krb5_error_code ret;
383233294Sstas    PA_ServerReferralData ref;
384233294Sstas    krb5_crypto session;
385233294Sstas    EncryptedData ed;
386233294Sstas    size_t len;
387233294Sstas    krb5_data data;
388233294Sstas    PA_DATA *pa;
389233294Sstas    int i = 0, cmp;
390233294Sstas
391233294Sstas    if (rep->kdc_rep.padata == NULL)
392233294Sstas	goto noreferral;
393233294Sstas
394233294Sstas    pa = krb5_find_padata(rep->kdc_rep.padata->val,
395233294Sstas			  rep->kdc_rep.padata->len,
396233294Sstas			  KRB5_PADATA_SERVER_REFERRAL, &i);
397233294Sstas    if (pa == NULL)
398233294Sstas	goto noreferral;
399233294Sstas
400233294Sstas    memset(&ed, 0, sizeof(ed));
401233294Sstas    memset(&ref, 0, sizeof(ref));
402233294Sstas
403233294Sstas    ret = decode_EncryptedData(pa->padata_value.data,
404233294Sstas			       pa->padata_value.length,
405233294Sstas			       &ed, &len);
406233294Sstas    if (ret)
407233294Sstas	return ret;
408233294Sstas    if (len != pa->padata_value.length) {
409233294Sstas	free_EncryptedData(&ed);
410233294Sstas	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
411233294Sstas			       N_("Referral EncryptedData wrong for realm %s",
412233294Sstas				  "realm"), requested->realm);
413233294Sstas	return KRB5KRB_AP_ERR_MODIFIED;
414233294Sstas    }
415233294Sstas
416233294Sstas    ret = krb5_crypto_init(context, key, 0, &session);
417233294Sstas    if (ret) {
418233294Sstas	free_EncryptedData(&ed);
419233294Sstas	return ret;
420233294Sstas    }
421233294Sstas
422233294Sstas    ret = krb5_decrypt_EncryptedData(context, session,
423233294Sstas				     KRB5_KU_PA_SERVER_REFERRAL,
424233294Sstas				     &ed, &data);
425233294Sstas    free_EncryptedData(&ed);
426233294Sstas    krb5_crypto_destroy(context, session);
427233294Sstas    if (ret)
428233294Sstas	return ret;
429233294Sstas
430233294Sstas    ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len);
431233294Sstas    if (ret) {
432233294Sstas	krb5_data_free(&data);
433233294Sstas	return ret;
434233294Sstas    }
435233294Sstas    krb5_data_free(&data);
436233294Sstas
437233294Sstas    if (strcmp(requested->realm, returned->realm) != 0) {
438233294Sstas	free_PA_ServerReferralData(&ref);
439233294Sstas	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
440233294Sstas			       N_("server ref realm mismatch, "
441233294Sstas				  "requested realm %s got back %s", ""),
442233294Sstas			       requested->realm, returned->realm);
443233294Sstas	return KRB5KRB_AP_ERR_MODIFIED;
444233294Sstas    }
445233294Sstas
446233294Sstas    if (krb5_principal_is_krbtgt(context, returned)) {
447233294Sstas	const char *realm = returned->name.name_string.val[1];
448233294Sstas
449233294Sstas	if (ref.referred_realm == NULL
450233294Sstas	    || strcmp(*ref.referred_realm, realm) != 0)
451233294Sstas	{
452233294Sstas	    free_PA_ServerReferralData(&ref);
453233294Sstas	    krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
454233294Sstas				   N_("tgt returned with wrong ref", ""));
455233294Sstas	    return KRB5KRB_AP_ERR_MODIFIED;
456233294Sstas	}
457233294Sstas    } else if (krb5_principal_compare(context, returned, requested) == 0) {
458233294Sstas	free_PA_ServerReferralData(&ref);
459233294Sstas	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
460233294Sstas			       N_("req princ no same as returned", ""));
461233294Sstas	return KRB5KRB_AP_ERR_MODIFIED;
462233294Sstas    }
463233294Sstas
464233294Sstas    if (ref.requested_principal_name) {
465233294Sstas	cmp = _krb5_principal_compare_PrincipalName(context,
466233294Sstas						    requested,
467233294Sstas						    ref.requested_principal_name);
468233294Sstas	if (!cmp) {
469233294Sstas	    free_PA_ServerReferralData(&ref);
470233294Sstas	    krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
471233294Sstas				   N_("referred principal not same "
472233294Sstas				      "as requested", ""));
473233294Sstas	    return KRB5KRB_AP_ERR_MODIFIED;
474233294Sstas	}
475233294Sstas    } else if (flags & EXTRACT_TICKET_AS_REQ) {
476233294Sstas	free_PA_ServerReferralData(&ref);
477233294Sstas	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
478233294Sstas			       N_("Requested principal missing on AS-REQ", ""));
479233294Sstas	return KRB5KRB_AP_ERR_MODIFIED;
480233294Sstas    }
481233294Sstas
482233294Sstas    free_PA_ServerReferralData(&ref);
483233294Sstas
484233294Sstas    return ret;
485233294Sstasnoreferral:
486233294Sstas    /*
487233294Sstas     * Expect excact match or that we got a krbtgt
488233294Sstas     */
489233294Sstas    if (krb5_principal_compare(context, requested, returned) != TRUE &&
490233294Sstas	(krb5_realm_compare(context, requested, returned) != TRUE &&
491233294Sstas	 krb5_principal_is_krbtgt(context, returned) != TRUE))
492233294Sstas    {
493233294Sstas	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
494233294Sstas			       N_("Not same server principal returned "
495233294Sstas				  "as requested", ""));
496233294Sstas	return KRB5KRB_AP_ERR_MODIFIED;
497233294Sstas    }
498233294Sstas    return 0;
499233294Sstas}
500233294Sstas
501233294Sstas
502233294Sstas/*
503233294Sstas * Verify referral data
504233294Sstas */
505233294Sstas
506233294Sstas
507233294Sstasstatic krb5_error_code
508233294Sstascheck_client_referral(krb5_context context,
509233294Sstas		      krb5_kdc_rep *rep,
510233294Sstas		      krb5_const_principal requested,
511233294Sstas		      krb5_const_principal mapped,
512233294Sstas		      krb5_keyblock const * key)
513233294Sstas{
514233294Sstas    krb5_error_code ret;
515233294Sstas    PA_ClientCanonicalized canon;
516233294Sstas    krb5_crypto crypto;
517233294Sstas    krb5_data data;
518233294Sstas    PA_DATA *pa;
519233294Sstas    size_t len;
520233294Sstas    int i = 0;
521233294Sstas
522233294Sstas    if (rep->kdc_rep.padata == NULL)
523233294Sstas	goto noreferral;
524233294Sstas
525233294Sstas    pa = krb5_find_padata(rep->kdc_rep.padata->val,
526233294Sstas			  rep->kdc_rep.padata->len,
527233294Sstas			  KRB5_PADATA_CLIENT_CANONICALIZED, &i);
528233294Sstas    if (pa == NULL)
529233294Sstas	goto noreferral;
530233294Sstas
531233294Sstas    ret = decode_PA_ClientCanonicalized(pa->padata_value.data,
532233294Sstas					pa->padata_value.length,
533233294Sstas					&canon, &len);
534233294Sstas    if (ret) {
535233294Sstas	krb5_set_error_message(context, ret,
536233294Sstas			       N_("Failed to decode ClientCanonicalized "
537233294Sstas				  "from realm %s", ""), requested->realm);
538233294Sstas	return ret;
539233294Sstas    }
540233294Sstas
541233294Sstas    ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
542233294Sstas		       &canon.names, &len, ret);
543233294Sstas    if (ret) {
544233294Sstas	free_PA_ClientCanonicalized(&canon);
545233294Sstas	return ret;
546233294Sstas    }
547233294Sstas    if (data.length != len)
548233294Sstas	krb5_abortx(context, "internal asn.1 error");
549233294Sstas
550233294Sstas    ret = krb5_crypto_init(context, key, 0, &crypto);
551233294Sstas    if (ret) {
552233294Sstas	free(data.data);
553233294Sstas	free_PA_ClientCanonicalized(&canon);
554233294Sstas	return ret;
555233294Sstas    }
556233294Sstas
557233294Sstas    ret = krb5_verify_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES,
558233294Sstas			       data.data, data.length,
559233294Sstas			       &canon.canon_checksum);
560233294Sstas    krb5_crypto_destroy(context, crypto);
561233294Sstas    free(data.data);
562233294Sstas    if (ret) {
563233294Sstas	krb5_set_error_message(context, ret,
564233294Sstas			       N_("Failed to verify client canonicalized "
565233294Sstas				  "data from realm %s", ""),
566233294Sstas			       requested->realm);
567233294Sstas	free_PA_ClientCanonicalized(&canon);
568233294Sstas	return ret;
569233294Sstas    }
570233294Sstas
571233294Sstas    if (!_krb5_principal_compare_PrincipalName(context,
572233294Sstas					       requested,
573233294Sstas					       &canon.names.requested_name))
574233294Sstas    {
575233294Sstas	free_PA_ClientCanonicalized(&canon);
576233294Sstas	krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
577233294Sstas			       N_("Requested name doesn't match"
578233294Sstas				  " in client referral", ""));
579233294Sstas	return KRB5_PRINC_NOMATCH;
580233294Sstas    }
581233294Sstas    if (!_krb5_principal_compare_PrincipalName(context,
582233294Sstas					       mapped,
583233294Sstas					       &canon.names.mapped_name))
584233294Sstas    {
585233294Sstas	free_PA_ClientCanonicalized(&canon);
586233294Sstas	krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
587233294Sstas			       N_("Mapped name doesn't match"
588233294Sstas				  " in client referral", ""));
589233294Sstas	return KRB5_PRINC_NOMATCH;
590233294Sstas    }
591233294Sstas
592233294Sstas    return 0;
593233294Sstas
594233294Sstasnoreferral:
595233294Sstas    if (krb5_principal_compare(context, requested, mapped) == FALSE) {
596233294Sstas	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
597233294Sstas			       N_("Not same client principal returned "
598233294Sstas				  "as requested", ""));
599233294Sstas	return KRB5KRB_AP_ERR_MODIFIED;
600233294Sstas    }
601233294Sstas    return 0;
602233294Sstas}
603233294Sstas
604233294Sstas
605233294Sstasstatic krb5_error_code KRB5_CALLCONV
606233294Sstasdecrypt_tkt (krb5_context context,
607233294Sstas	     krb5_keyblock *key,
608233294Sstas	     krb5_key_usage usage,
609233294Sstas	     krb5_const_pointer decrypt_arg,
610233294Sstas	     krb5_kdc_rep *dec_rep)
611233294Sstas{
612233294Sstas    krb5_error_code ret;
613233294Sstas    krb5_data data;
614233294Sstas    size_t size;
615233294Sstas    krb5_crypto crypto;
616233294Sstas
617233294Sstas    ret = krb5_crypto_init(context, key, 0, &crypto);
618233294Sstas    if (ret)
619233294Sstas	return ret;
620233294Sstas
621233294Sstas    ret = krb5_decrypt_EncryptedData (context,
622233294Sstas				      crypto,
623233294Sstas				      usage,
624233294Sstas				      &dec_rep->kdc_rep.enc_part,
625233294Sstas				      &data);
626233294Sstas    krb5_crypto_destroy(context, crypto);
627233294Sstas
628233294Sstas    if (ret)
629233294Sstas	return ret;
630233294Sstas
631233294Sstas    ret = decode_EncASRepPart(data.data,
632233294Sstas			      data.length,
633233294Sstas			      &dec_rep->enc_part,
634233294Sstas			      &size);
635233294Sstas    if (ret)
636233294Sstas	ret = decode_EncTGSRepPart(data.data,
637233294Sstas				   data.length,
638233294Sstas				   &dec_rep->enc_part,
639233294Sstas				   &size);
640233294Sstas    krb5_data_free (&data);
641233294Sstas    if (ret) {
642233294Sstas        krb5_set_error_message(context, ret,
643233294Sstas			       N_("Failed to decode encpart in ticket", ""));
644233294Sstas	return ret;
645233294Sstas    }
646233294Sstas    return 0;
647233294Sstas}
648233294Sstas
649233294Sstasint
650233294Sstas_krb5_extract_ticket(krb5_context context,
651233294Sstas		     krb5_kdc_rep *rep,
652233294Sstas		     krb5_creds *creds,
653233294Sstas		     krb5_keyblock *key,
654233294Sstas		     krb5_const_pointer keyseed,
655233294Sstas		     krb5_key_usage key_usage,
656233294Sstas		     krb5_addresses *addrs,
657233294Sstas		     unsigned nonce,
658233294Sstas		     unsigned flags,
659233294Sstas		     krb5_decrypt_proc decrypt_proc,
660233294Sstas		     krb5_const_pointer decryptarg)
661233294Sstas{
662233294Sstas    krb5_error_code ret;
663233294Sstas    krb5_principal tmp_principal;
664233294Sstas    size_t len = 0;
665233294Sstas    time_t tmp_time;
666233294Sstas    krb5_timestamp sec_now;
667233294Sstas
668233294Sstas    /* decrypt */
669233294Sstas
670233294Sstas    if (decrypt_proc == NULL)
671233294Sstas	decrypt_proc = decrypt_tkt;
672233294Sstas
673233294Sstas    ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep);
674233294Sstas    if (ret)
675233294Sstas	goto out;
676233294Sstas
677233294Sstas    /* save session key */
678233294Sstas
679233294Sstas    creds->session.keyvalue.length = 0;
680233294Sstas    creds->session.keyvalue.data   = NULL;
681233294Sstas    creds->session.keytype = rep->enc_part.key.keytype;
682233294Sstas    ret = krb5_data_copy (&creds->session.keyvalue,
683233294Sstas			  rep->enc_part.key.keyvalue.data,
684233294Sstas			  rep->enc_part.key.keyvalue.length);
685233294Sstas    if (ret) {
686233294Sstas	krb5_clear_error_message(context);
687233294Sstas	goto out;
688233294Sstas    }
689233294Sstas
690233294Sstas    /* compare client and save */
691233294Sstas    ret = _krb5_principalname2krb5_principal (context,
692233294Sstas					      &tmp_principal,
693233294Sstas					      rep->kdc_rep.cname,
694233294Sstas					      rep->kdc_rep.crealm);
695233294Sstas    if (ret)
696233294Sstas	goto out;
697233294Sstas
698233294Sstas    /* check client referral and save principal */
699233294Sstas    /* anonymous here ? */
700233294Sstas    if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) {
701233294Sstas	ret = check_client_referral(context, rep,
702233294Sstas				    creds->client,
703233294Sstas				    tmp_principal,
704233294Sstas				    &creds->session);
705233294Sstas	if (ret) {
706233294Sstas	    krb5_free_principal (context, tmp_principal);
707233294Sstas	    goto out;
708233294Sstas	}
709233294Sstas    }
710233294Sstas    krb5_free_principal (context, creds->client);
711233294Sstas    creds->client = tmp_principal;
712233294Sstas
713233294Sstas    /* check server referral and save principal */
714233294Sstas    ret = _krb5_principalname2krb5_principal (context,
715233294Sstas					      &tmp_principal,
716320907Sdelphij					      rep->enc_part.sname,
717320907Sdelphij					      rep->enc_part.srealm);
718233294Sstas    if (ret)
719233294Sstas	goto out;
720233294Sstas    if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){
721233294Sstas	ret = check_server_referral(context,
722233294Sstas				    rep,
723233294Sstas				    flags,
724233294Sstas				    creds->server,
725233294Sstas				    tmp_principal,
726233294Sstas				    &creds->session);
727233294Sstas	if (ret) {
728233294Sstas	    krb5_free_principal (context, tmp_principal);
729233294Sstas	    goto out;
730233294Sstas	}
731233294Sstas    }
732233294Sstas    krb5_free_principal(context, creds->server);
733233294Sstas    creds->server = tmp_principal;
734233294Sstas
735233294Sstas    /* verify names */
736233294Sstas    if(flags & EXTRACT_TICKET_MATCH_REALM){
737233294Sstas	const char *srealm = krb5_principal_get_realm(context, creds->server);
738233294Sstas	const char *crealm = krb5_principal_get_realm(context, creds->client);
739233294Sstas
740233294Sstas	if (strcmp(rep->enc_part.srealm, srealm) != 0 ||
741233294Sstas	    strcmp(rep->enc_part.srealm, crealm) != 0)
742233294Sstas	{
743233294Sstas	    ret = KRB5KRB_AP_ERR_MODIFIED;
744233294Sstas	    krb5_clear_error_message(context);
745233294Sstas	    goto out;
746233294Sstas	}
747233294Sstas    }
748233294Sstas
749233294Sstas    /* compare nonces */
750233294Sstas
751233294Sstas    if (nonce != (unsigned)rep->enc_part.nonce) {
752233294Sstas	ret = KRB5KRB_AP_ERR_MODIFIED;
753233294Sstas	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
754233294Sstas	goto out;
755233294Sstas    }
756233294Sstas
757233294Sstas    /* set kdc-offset */
758233294Sstas
759233294Sstas    krb5_timeofday (context, &sec_now);
760233294Sstas    if (rep->enc_part.flags.initial
761233294Sstas	&& (flags & EXTRACT_TICKET_TIMESYNC)
762233294Sstas	&& context->kdc_sec_offset == 0
763233294Sstas	&& krb5_config_get_bool (context, NULL,
764233294Sstas				 "libdefaults",
765233294Sstas				 "kdc_timesync",
766233294Sstas				 NULL)) {
767233294Sstas	context->kdc_sec_offset = rep->enc_part.authtime - sec_now;
768233294Sstas	krb5_timeofday (context, &sec_now);
769233294Sstas    }
770233294Sstas
771233294Sstas    /* check all times */
772233294Sstas
773233294Sstas    if (rep->enc_part.starttime) {
774233294Sstas	tmp_time = *rep->enc_part.starttime;
775233294Sstas    } else
776233294Sstas	tmp_time = rep->enc_part.authtime;
777233294Sstas
778233294Sstas    if (creds->times.starttime == 0
779233294Sstas	&& abs(tmp_time - sec_now) > context->max_skew) {
780233294Sstas	ret = KRB5KRB_AP_ERR_SKEW;
781233294Sstas	krb5_set_error_message (context, ret,
782233294Sstas				N_("time skew (%d) larger than max (%d)", ""),
783233294Sstas			       abs(tmp_time - sec_now),
784233294Sstas			       (int)context->max_skew);
785233294Sstas	goto out;
786233294Sstas    }
787233294Sstas
788233294Sstas    if (creds->times.starttime != 0
789233294Sstas	&& tmp_time != creds->times.starttime) {
790233294Sstas	krb5_clear_error_message (context);
791233294Sstas	ret = KRB5KRB_AP_ERR_MODIFIED;
792233294Sstas	goto out;
793233294Sstas    }
794233294Sstas
795233294Sstas    creds->times.starttime = tmp_time;
796233294Sstas
797233294Sstas    if (rep->enc_part.renew_till) {
798233294Sstas	tmp_time = *rep->enc_part.renew_till;
799233294Sstas    } else
800233294Sstas	tmp_time = 0;
801233294Sstas
802233294Sstas    if (creds->times.renew_till != 0
803233294Sstas	&& tmp_time > creds->times.renew_till) {
804233294Sstas	krb5_clear_error_message (context);
805233294Sstas	ret = KRB5KRB_AP_ERR_MODIFIED;
806233294Sstas	goto out;
807233294Sstas    }
808233294Sstas
809233294Sstas    creds->times.renew_till = tmp_time;
810233294Sstas
811233294Sstas    creds->times.authtime = rep->enc_part.authtime;
812233294Sstas
813233294Sstas    if (creds->times.endtime != 0
814233294Sstas	&& rep->enc_part.endtime > creds->times.endtime) {
815233294Sstas	krb5_clear_error_message (context);
816233294Sstas	ret = KRB5KRB_AP_ERR_MODIFIED;
817233294Sstas	goto out;
818233294Sstas    }
819233294Sstas
820233294Sstas    creds->times.endtime  = rep->enc_part.endtime;
821233294Sstas
822233294Sstas    if(rep->enc_part.caddr)
823233294Sstas	krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses);
824233294Sstas    else if(addrs)
825233294Sstas	krb5_copy_addresses (context, addrs, &creds->addresses);
826233294Sstas    else {
827233294Sstas	creds->addresses.len = 0;
828233294Sstas	creds->addresses.val = NULL;
829233294Sstas    }
830233294Sstas    creds->flags.b = rep->enc_part.flags;
831233294Sstas
832233294Sstas    creds->authdata.len = 0;
833233294Sstas    creds->authdata.val = NULL;
834233294Sstas
835233294Sstas    /* extract ticket */
836233294Sstas    ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
837233294Sstas		       &rep->kdc_rep.ticket, &len, ret);
838233294Sstas    if(ret)
839233294Sstas	goto out;
840233294Sstas    if (creds->ticket.length != len)
841233294Sstas	krb5_abortx(context, "internal error in ASN.1 encoder");
842233294Sstas    creds->second_ticket.length = 0;
843233294Sstas    creds->second_ticket.data   = NULL;
844233294Sstas
845233294Sstas
846233294Sstasout:
847233294Sstas    memset (rep->enc_part.key.keyvalue.data, 0,
848233294Sstas	    rep->enc_part.key.keyvalue.length);
849233294Sstas    return ret;
850233294Sstas}
851