xref: /freebsd/crypto/heimdal/kdc/krb5tgs.c (revision 24339377)
1c19800e8SDoug Rabson /*
2ae771770SStanislav Sedov  * Copyright (c) 1997-2008 Kungliga Tekniska Högskolan
3c19800e8SDoug Rabson  * (Royal Institute of Technology, Stockholm, Sweden).
4c19800e8SDoug Rabson  * All rights reserved.
5c19800e8SDoug Rabson  *
6c19800e8SDoug Rabson  * Redistribution and use in source and binary forms, with or without
7c19800e8SDoug Rabson  * modification, are permitted provided that the following conditions
8c19800e8SDoug Rabson  * are met:
9c19800e8SDoug Rabson  *
10c19800e8SDoug Rabson  * 1. Redistributions of source code must retain the above copyright
11c19800e8SDoug Rabson  *    notice, this list of conditions and the following disclaimer.
12c19800e8SDoug Rabson  *
13c19800e8SDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
14c19800e8SDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
15c19800e8SDoug Rabson  *    documentation and/or other materials provided with the distribution.
16c19800e8SDoug Rabson  *
17c19800e8SDoug Rabson  * 3. Neither the name of the Institute nor the names of its contributors
18c19800e8SDoug Rabson  *    may be used to endorse or promote products derived from this software
19c19800e8SDoug Rabson  *    without specific prior written permission.
20c19800e8SDoug Rabson  *
21c19800e8SDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22c19800e8SDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23c19800e8SDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24c19800e8SDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25c19800e8SDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26c19800e8SDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27c19800e8SDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28c19800e8SDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29c19800e8SDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30c19800e8SDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31c19800e8SDoug Rabson  * SUCH DAMAGE.
32c19800e8SDoug Rabson  */
33c19800e8SDoug Rabson 
34c19800e8SDoug Rabson #include "kdc_locl.h"
35c19800e8SDoug Rabson 
36c19800e8SDoug Rabson /*
37c19800e8SDoug Rabson  * return the realm of a krbtgt-ticket or NULL
38c19800e8SDoug Rabson  */
39c19800e8SDoug Rabson 
40c19800e8SDoug Rabson static Realm
get_krbtgt_realm(const PrincipalName * p)41c19800e8SDoug Rabson get_krbtgt_realm(const PrincipalName *p)
42c19800e8SDoug Rabson {
43c19800e8SDoug Rabson     if(p->name_string.len == 2
44c19800e8SDoug Rabson        && strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0)
45c19800e8SDoug Rabson 	return p->name_string.val[1];
46c19800e8SDoug Rabson     else
47c19800e8SDoug Rabson 	return NULL;
48c19800e8SDoug Rabson }
49c19800e8SDoug Rabson 
50c19800e8SDoug Rabson /*
51c19800e8SDoug Rabson  * The KDC might add a signed path to the ticket authorization data
52c19800e8SDoug Rabson  * field. This is to avoid server impersonating clients and the
53c19800e8SDoug Rabson  * request constrained delegation.
54c19800e8SDoug Rabson  *
55c19800e8SDoug Rabson  * This is done by storing a KRB5_AUTHDATA_IF_RELEVANT with a single
56c19800e8SDoug Rabson  * entry of type KRB5SignedPath.
57c19800e8SDoug Rabson  */
58c19800e8SDoug Rabson 
59c19800e8SDoug Rabson static krb5_error_code
find_KRB5SignedPath(krb5_context context,const AuthorizationData * ad,krb5_data * data)60c19800e8SDoug Rabson find_KRB5SignedPath(krb5_context context,
61c19800e8SDoug Rabson 		    const AuthorizationData *ad,
62c19800e8SDoug Rabson 		    krb5_data *data)
63c19800e8SDoug Rabson {
64c19800e8SDoug Rabson     AuthorizationData child;
65c19800e8SDoug Rabson     krb5_error_code ret;
66c19800e8SDoug Rabson     int pos;
67c19800e8SDoug Rabson 
68c19800e8SDoug Rabson     if (ad == NULL || ad->len == 0)
69c19800e8SDoug Rabson 	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
70c19800e8SDoug Rabson 
71c19800e8SDoug Rabson     pos = ad->len - 1;
72c19800e8SDoug Rabson 
73c19800e8SDoug Rabson     if (ad->val[pos].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
74c19800e8SDoug Rabson 	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
75c19800e8SDoug Rabson 
76c19800e8SDoug Rabson     ret = decode_AuthorizationData(ad->val[pos].ad_data.data,
77c19800e8SDoug Rabson 				   ad->val[pos].ad_data.length,
78c19800e8SDoug Rabson 				   &child,
79c19800e8SDoug Rabson 				   NULL);
80c19800e8SDoug Rabson     if (ret) {
81ae771770SStanislav Sedov 	krb5_set_error_message(context, ret, "Failed to decode "
82c19800e8SDoug Rabson 			       "IF_RELEVANT with %d", ret);
83c19800e8SDoug Rabson 	return ret;
84c19800e8SDoug Rabson     }
85c19800e8SDoug Rabson 
86c19800e8SDoug Rabson     if (child.len != 1) {
87c19800e8SDoug Rabson 	free_AuthorizationData(&child);
88c19800e8SDoug Rabson 	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
89c19800e8SDoug Rabson     }
90c19800e8SDoug Rabson 
91c19800e8SDoug Rabson     if (child.val[0].ad_type != KRB5_AUTHDATA_SIGNTICKET) {
92c19800e8SDoug Rabson 	free_AuthorizationData(&child);
93c19800e8SDoug Rabson 	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
94c19800e8SDoug Rabson     }
95c19800e8SDoug Rabson 
96c19800e8SDoug Rabson     if (data)
97c19800e8SDoug Rabson 	ret = der_copy_octet_string(&child.val[0].ad_data, data);
98c19800e8SDoug Rabson     free_AuthorizationData(&child);
99c19800e8SDoug Rabson     return ret;
100c19800e8SDoug Rabson }
101c19800e8SDoug Rabson 
102c19800e8SDoug Rabson krb5_error_code
_kdc_add_KRB5SignedPath(krb5_context context,krb5_kdc_configuration * config,hdb_entry_ex * krbtgt,krb5_enctype enctype,krb5_principal client,krb5_const_principal server,krb5_principals principals,EncTicketPart * tkt)103c19800e8SDoug Rabson _kdc_add_KRB5SignedPath(krb5_context context,
104c19800e8SDoug Rabson 			krb5_kdc_configuration *config,
105c19800e8SDoug Rabson 			hdb_entry_ex *krbtgt,
106c19800e8SDoug Rabson 			krb5_enctype enctype,
107ae771770SStanislav Sedov 			krb5_principal client,
108c19800e8SDoug Rabson 			krb5_const_principal server,
109ae771770SStanislav Sedov 			krb5_principals principals,
110c19800e8SDoug Rabson 			EncTicketPart *tkt)
111c19800e8SDoug Rabson {
112c19800e8SDoug Rabson     krb5_error_code ret;
113c19800e8SDoug Rabson     KRB5SignedPath sp;
114c19800e8SDoug Rabson     krb5_data data;
115c19800e8SDoug Rabson     krb5_crypto crypto = NULL;
116ae771770SStanislav Sedov     size_t size = 0;
117c19800e8SDoug Rabson 
118c19800e8SDoug Rabson     if (server && principals) {
119ae771770SStanislav Sedov 	ret = add_Principals(principals, server);
120c19800e8SDoug Rabson 	if (ret)
121c19800e8SDoug Rabson 	    return ret;
122c19800e8SDoug Rabson     }
123c19800e8SDoug Rabson 
124c19800e8SDoug Rabson     {
125c19800e8SDoug Rabson 	KRB5SignedPathData spd;
126c19800e8SDoug Rabson 
127ae771770SStanislav Sedov 	spd.client = client;
128ae771770SStanislav Sedov 	spd.authtime = tkt->authtime;
129c19800e8SDoug Rabson 	spd.delegated = principals;
130ae771770SStanislav Sedov 	spd.method_data = NULL;
131c19800e8SDoug Rabson 
132c19800e8SDoug Rabson 	ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
133c19800e8SDoug Rabson 			   &spd, &size, ret);
134c19800e8SDoug Rabson 	if (ret)
135c19800e8SDoug Rabson 	    return ret;
136c19800e8SDoug Rabson 	if (data.length != size)
137c19800e8SDoug Rabson 	    krb5_abortx(context, "internal asn.1 encoder error");
138c19800e8SDoug Rabson     }
139c19800e8SDoug Rabson 
140c19800e8SDoug Rabson     {
141c19800e8SDoug Rabson 	Key *key;
142c19800e8SDoug Rabson 	ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key);
143c19800e8SDoug Rabson 	if (ret == 0)
144c19800e8SDoug Rabson 	    ret = krb5_crypto_init(context, &key->key, 0, &crypto);
145c19800e8SDoug Rabson 	if (ret) {
146c19800e8SDoug Rabson 	    free(data.data);
147c19800e8SDoug Rabson 	    return ret;
148c19800e8SDoug Rabson 	}
149c19800e8SDoug Rabson     }
150c19800e8SDoug Rabson 
151c19800e8SDoug Rabson     /*
152c19800e8SDoug Rabson      * Fill in KRB5SignedPath
153c19800e8SDoug Rabson      */
154c19800e8SDoug Rabson 
155c19800e8SDoug Rabson     sp.etype = enctype;
156c19800e8SDoug Rabson     sp.delegated = principals;
157ae771770SStanislav Sedov     sp.method_data = NULL;
158c19800e8SDoug Rabson 
159c19800e8SDoug Rabson     ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0,
160c19800e8SDoug Rabson 			       data.data, data.length, &sp.cksum);
161c19800e8SDoug Rabson     krb5_crypto_destroy(context, crypto);
162c19800e8SDoug Rabson     free(data.data);
163c19800e8SDoug Rabson     if (ret)
164c19800e8SDoug Rabson 	return ret;
165c19800e8SDoug Rabson 
166c19800e8SDoug Rabson     ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret);
167c19800e8SDoug Rabson     free_Checksum(&sp.cksum);
168c19800e8SDoug Rabson     if (ret)
169c19800e8SDoug Rabson 	return ret;
170c19800e8SDoug Rabson     if (data.length != size)
171c19800e8SDoug Rabson 	krb5_abortx(context, "internal asn.1 encoder error");
172c19800e8SDoug Rabson 
173c19800e8SDoug Rabson 
174c19800e8SDoug Rabson     /*
175c19800e8SDoug Rabson      * Add IF-RELEVANT(KRB5SignedPath) to the last slot in
176c19800e8SDoug Rabson      * authorization data field.
177c19800e8SDoug Rabson      */
178c19800e8SDoug Rabson 
179c19800e8SDoug Rabson     ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
180c19800e8SDoug Rabson 				      KRB5_AUTHDATA_SIGNTICKET, &data);
181c19800e8SDoug Rabson     krb5_data_free(&data);
182c19800e8SDoug Rabson 
183c19800e8SDoug Rabson     return ret;
184c19800e8SDoug Rabson }
185c19800e8SDoug Rabson 
186c19800e8SDoug Rabson static krb5_error_code
check_KRB5SignedPath(krb5_context context,krb5_kdc_configuration * config,hdb_entry_ex * krbtgt,krb5_principal cp,EncTicketPart * tkt,krb5_principals * delegated,int * signedpath)187c19800e8SDoug Rabson check_KRB5SignedPath(krb5_context context,
188c19800e8SDoug Rabson 		     krb5_kdc_configuration *config,
189c19800e8SDoug Rabson 		     hdb_entry_ex *krbtgt,
190ae771770SStanislav Sedov 		     krb5_principal cp,
191c19800e8SDoug Rabson 		     EncTicketPart *tkt,
192ae771770SStanislav Sedov 		     krb5_principals *delegated,
193ae771770SStanislav Sedov 		     int *signedpath)
194c19800e8SDoug Rabson {
195c19800e8SDoug Rabson     krb5_error_code ret;
196c19800e8SDoug Rabson     krb5_data data;
197c19800e8SDoug Rabson     krb5_crypto crypto = NULL;
198c19800e8SDoug Rabson 
199ae771770SStanislav Sedov     if (delegated)
200c19800e8SDoug Rabson 	*delegated = NULL;
201c19800e8SDoug Rabson 
202c19800e8SDoug Rabson     ret = find_KRB5SignedPath(context, tkt->authorization_data, &data);
203c19800e8SDoug Rabson     if (ret == 0) {
204c19800e8SDoug Rabson 	KRB5SignedPathData spd;
205c19800e8SDoug Rabson 	KRB5SignedPath sp;
206ae771770SStanislav Sedov 	size_t size = 0;
207c19800e8SDoug Rabson 
208c19800e8SDoug Rabson 	ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL);
209c19800e8SDoug Rabson 	krb5_data_free(&data);
210c19800e8SDoug Rabson 	if (ret)
211c19800e8SDoug Rabson 	    return ret;
212c19800e8SDoug Rabson 
213ae771770SStanislav Sedov 	spd.client = cp;
214ae771770SStanislav Sedov 	spd.authtime = tkt->authtime;
215c19800e8SDoug Rabson 	spd.delegated = sp.delegated;
216ae771770SStanislav Sedov 	spd.method_data = sp.method_data;
217c19800e8SDoug Rabson 
218c19800e8SDoug Rabson 	ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
219c19800e8SDoug Rabson 			   &spd, &size, ret);
220c19800e8SDoug Rabson 	if (ret) {
221c19800e8SDoug Rabson 	    free_KRB5SignedPath(&sp);
222c19800e8SDoug Rabson 	    return ret;
223c19800e8SDoug Rabson 	}
224c19800e8SDoug Rabson 	if (data.length != size)
225c19800e8SDoug Rabson 	    krb5_abortx(context, "internal asn.1 encoder error");
226c19800e8SDoug Rabson 
227c19800e8SDoug Rabson 	{
228c19800e8SDoug Rabson 	    Key *key;
229c19800e8SDoug Rabson 	    ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key);
230c19800e8SDoug Rabson 	    if (ret == 0)
231c19800e8SDoug Rabson 		ret = krb5_crypto_init(context, &key->key, 0, &crypto);
232c19800e8SDoug Rabson 	    if (ret) {
233c19800e8SDoug Rabson 		free(data.data);
234c19800e8SDoug Rabson 		free_KRB5SignedPath(&sp);
235c19800e8SDoug Rabson 		return ret;
236c19800e8SDoug Rabson 	    }
237c19800e8SDoug Rabson 	}
238c19800e8SDoug Rabson 	ret = krb5_verify_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH,
239c19800e8SDoug Rabson 				   data.data, data.length,
240c19800e8SDoug Rabson 				   &sp.cksum);
241c19800e8SDoug Rabson 	krb5_crypto_destroy(context, crypto);
242c19800e8SDoug Rabson 	free(data.data);
243c19800e8SDoug Rabson 	if (ret) {
244c19800e8SDoug Rabson 	    free_KRB5SignedPath(&sp);
245ae771770SStanislav Sedov 	    kdc_log(context, config, 5,
246ae771770SStanislav Sedov 		    "KRB5SignedPath not signed correctly, not marking as signed");
247ae771770SStanislav Sedov 	    return 0;
248c19800e8SDoug Rabson 	}
249c19800e8SDoug Rabson 
250ae771770SStanislav Sedov 	if (delegated && sp.delegated) {
251c19800e8SDoug Rabson 
252c19800e8SDoug Rabson 	    *delegated = malloc(sizeof(*sp.delegated));
253c19800e8SDoug Rabson 	    if (*delegated == NULL) {
254c19800e8SDoug Rabson 		free_KRB5SignedPath(&sp);
255c19800e8SDoug Rabson 		return ENOMEM;
256c19800e8SDoug Rabson 	    }
257c19800e8SDoug Rabson 
258ae771770SStanislav Sedov 	    ret = copy_Principals(*delegated, sp.delegated);
259c19800e8SDoug Rabson 	    if (ret) {
260c19800e8SDoug Rabson 		free_KRB5SignedPath(&sp);
261c19800e8SDoug Rabson 		free(*delegated);
262c19800e8SDoug Rabson 		*delegated = NULL;
263c19800e8SDoug Rabson 		return ret;
264c19800e8SDoug Rabson 	    }
265c19800e8SDoug Rabson 	}
266c19800e8SDoug Rabson 	free_KRB5SignedPath(&sp);
267c19800e8SDoug Rabson 
268ae771770SStanislav Sedov 	*signedpath = 1;
269c19800e8SDoug Rabson     }
270c19800e8SDoug Rabson 
271c19800e8SDoug Rabson     return 0;
272c19800e8SDoug Rabson }
273c19800e8SDoug Rabson 
274c19800e8SDoug Rabson /*
275c19800e8SDoug Rabson  *
276c19800e8SDoug Rabson  */
277c19800e8SDoug Rabson 
278c19800e8SDoug Rabson static krb5_error_code
check_PAC(krb5_context context,krb5_kdc_configuration * config,const krb5_principal client_principal,const krb5_principal delegated_proxy_principal,hdb_entry_ex * client,hdb_entry_ex * server,hdb_entry_ex * krbtgt,const EncryptionKey * server_check_key,const EncryptionKey * krbtgt_check_key,const EncryptionKey * server_sign_key,const EncryptionKey * krbtgt_sign_key,EncTicketPart * tkt,krb5_data * rspac,int * signedpath)279c19800e8SDoug Rabson check_PAC(krb5_context context,
280c19800e8SDoug Rabson 	  krb5_kdc_configuration *config,
281c19800e8SDoug Rabson 	  const krb5_principal client_principal,
282ae771770SStanislav Sedov 	  const krb5_principal delegated_proxy_principal,
283c19800e8SDoug Rabson 	  hdb_entry_ex *client,
284c19800e8SDoug Rabson 	  hdb_entry_ex *server,
285ae771770SStanislav Sedov 	  hdb_entry_ex *krbtgt,
286ae771770SStanislav Sedov 	  const EncryptionKey *server_check_key,
287ae771770SStanislav Sedov 	  const EncryptionKey *krbtgt_check_key,
288ae771770SStanislav Sedov 	  const EncryptionKey *server_sign_key,
289ae771770SStanislav Sedov 	  const EncryptionKey *krbtgt_sign_key,
290c19800e8SDoug Rabson 	  EncTicketPart *tkt,
291c19800e8SDoug Rabson 	  krb5_data *rspac,
292ae771770SStanislav Sedov 	  int *signedpath)
293c19800e8SDoug Rabson {
294c19800e8SDoug Rabson     AuthorizationData *ad = tkt->authorization_data;
295c19800e8SDoug Rabson     unsigned i, j;
296c19800e8SDoug Rabson     krb5_error_code ret;
297c19800e8SDoug Rabson 
298c19800e8SDoug Rabson     if (ad == NULL || ad->len == 0)
299c19800e8SDoug Rabson 	return 0;
300c19800e8SDoug Rabson 
301c19800e8SDoug Rabson     for (i = 0; i < ad->len; i++) {
302c19800e8SDoug Rabson 	AuthorizationData child;
303c19800e8SDoug Rabson 
304c19800e8SDoug Rabson 	if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
305c19800e8SDoug Rabson 	    continue;
306c19800e8SDoug Rabson 
307c19800e8SDoug Rabson 	ret = decode_AuthorizationData(ad->val[i].ad_data.data,
308c19800e8SDoug Rabson 				       ad->val[i].ad_data.length,
309c19800e8SDoug Rabson 				       &child,
310c19800e8SDoug Rabson 				       NULL);
311c19800e8SDoug Rabson 	if (ret) {
312ae771770SStanislav Sedov 	    krb5_set_error_message(context, ret, "Failed to decode "
313c19800e8SDoug Rabson 				   "IF_RELEVANT with %d", ret);
314c19800e8SDoug Rabson 	    return ret;
315c19800e8SDoug Rabson 	}
316c19800e8SDoug Rabson 	for (j = 0; j < child.len; j++) {
317c19800e8SDoug Rabson 
318c19800e8SDoug Rabson 	    if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
319ae771770SStanislav Sedov 		int signed_pac = 0;
320c19800e8SDoug Rabson 		krb5_pac pac;
321c19800e8SDoug Rabson 
322c19800e8SDoug Rabson 		/* Found PAC */
323c19800e8SDoug Rabson 		ret = krb5_pac_parse(context,
324c19800e8SDoug Rabson 				     child.val[j].ad_data.data,
325c19800e8SDoug Rabson 				     child.val[j].ad_data.length,
326c19800e8SDoug Rabson 				     &pac);
327c19800e8SDoug Rabson 		free_AuthorizationData(&child);
328c19800e8SDoug Rabson 		if (ret)
329c19800e8SDoug Rabson 		    return ret;
330c19800e8SDoug Rabson 
331c19800e8SDoug Rabson 		ret = krb5_pac_verify(context, pac, tkt->authtime,
332c19800e8SDoug Rabson 				      client_principal,
333ae771770SStanislav Sedov 				      server_check_key, krbtgt_check_key);
334c19800e8SDoug Rabson 		if (ret) {
335c19800e8SDoug Rabson 		    krb5_pac_free(context, pac);
336c19800e8SDoug Rabson 		    return ret;
337c19800e8SDoug Rabson 		}
338c19800e8SDoug Rabson 
339c19800e8SDoug Rabson 		ret = _kdc_pac_verify(context, client_principal,
340ae771770SStanislav Sedov 				      delegated_proxy_principal,
341ae771770SStanislav Sedov 				      client, server, krbtgt, &pac, &signed_pac);
342c19800e8SDoug Rabson 		if (ret) {
343c19800e8SDoug Rabson 		    krb5_pac_free(context, pac);
344c19800e8SDoug Rabson 		    return ret;
345c19800e8SDoug Rabson 		}
346c19800e8SDoug Rabson 
347ae771770SStanislav Sedov 		/*
348ae771770SStanislav Sedov 		 * Only re-sign PAC if we could verify it with the PAC
349ae771770SStanislav Sedov 		 * function. The no-verify case happens when we get in
350ae771770SStanislav Sedov 		 * a PAC from cross realm from a Windows domain and
351ae771770SStanislav Sedov 		 * that there is no PAC verification function.
352ae771770SStanislav Sedov 		 */
353ae771770SStanislav Sedov 		if (signed_pac) {
354ae771770SStanislav Sedov 		    *signedpath = 1;
355c19800e8SDoug Rabson 		    ret = _krb5_pac_sign(context, pac, tkt->authtime,
356c19800e8SDoug Rabson 					 client_principal,
357ae771770SStanislav Sedov 					 server_sign_key, krbtgt_sign_key, rspac);
358ae771770SStanislav Sedov 		}
359c19800e8SDoug Rabson 		krb5_pac_free(context, pac);
360c19800e8SDoug Rabson 
361c19800e8SDoug Rabson 		return ret;
362c19800e8SDoug Rabson 	    }
363c19800e8SDoug Rabson 	}
364c19800e8SDoug Rabson 	free_AuthorizationData(&child);
365c19800e8SDoug Rabson     }
366c19800e8SDoug Rabson     return 0;
367c19800e8SDoug Rabson }
368c19800e8SDoug Rabson 
369c19800e8SDoug Rabson /*
370c19800e8SDoug Rabson  *
371c19800e8SDoug Rabson  */
372c19800e8SDoug Rabson 
373c19800e8SDoug Rabson static krb5_error_code
check_tgs_flags(krb5_context context,krb5_kdc_configuration * config,KDC_REQ_BODY * b,const EncTicketPart * tgt,EncTicketPart * et)374c19800e8SDoug Rabson check_tgs_flags(krb5_context context,
375c19800e8SDoug Rabson 		krb5_kdc_configuration *config,
376c19800e8SDoug Rabson 		KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et)
377c19800e8SDoug Rabson {
378c19800e8SDoug Rabson     KDCOptions f = b->kdc_options;
379c19800e8SDoug Rabson 
380c19800e8SDoug Rabson     if(f.validate){
381c19800e8SDoug Rabson 	if(!tgt->flags.invalid || tgt->starttime == NULL){
382c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
383c19800e8SDoug Rabson 		    "Bad request to validate ticket");
384c19800e8SDoug Rabson 	    return KRB5KDC_ERR_BADOPTION;
385c19800e8SDoug Rabson 	}
386c19800e8SDoug Rabson 	if(*tgt->starttime > kdc_time){
387c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
388c19800e8SDoug Rabson 		    "Early request to validate ticket");
389c19800e8SDoug Rabson 	    return KRB5KRB_AP_ERR_TKT_NYV;
390c19800e8SDoug Rabson 	}
391c19800e8SDoug Rabson 	/* XXX  tkt = tgt */
392c19800e8SDoug Rabson 	et->flags.invalid = 0;
393c19800e8SDoug Rabson     }else if(tgt->flags.invalid){
394c19800e8SDoug Rabson 	kdc_log(context, config, 0,
395c19800e8SDoug Rabson 		"Ticket-granting ticket has INVALID flag set");
396c19800e8SDoug Rabson 	return KRB5KRB_AP_ERR_TKT_INVALID;
397c19800e8SDoug Rabson     }
398c19800e8SDoug Rabson 
399c19800e8SDoug Rabson     if(f.forwardable){
400c19800e8SDoug Rabson 	if(!tgt->flags.forwardable){
401c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
402c19800e8SDoug Rabson 		    "Bad request for forwardable ticket");
403c19800e8SDoug Rabson 	    return KRB5KDC_ERR_BADOPTION;
404c19800e8SDoug Rabson 	}
405c19800e8SDoug Rabson 	et->flags.forwardable = 1;
406c19800e8SDoug Rabson     }
407c19800e8SDoug Rabson     if(f.forwarded){
408c19800e8SDoug Rabson 	if(!tgt->flags.forwardable){
409c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
410c19800e8SDoug Rabson 		    "Request to forward non-forwardable ticket");
411c19800e8SDoug Rabson 	    return KRB5KDC_ERR_BADOPTION;
412c19800e8SDoug Rabson 	}
413c19800e8SDoug Rabson 	et->flags.forwarded = 1;
414c19800e8SDoug Rabson 	et->caddr = b->addresses;
415c19800e8SDoug Rabson     }
416c19800e8SDoug Rabson     if(tgt->flags.forwarded)
417c19800e8SDoug Rabson 	et->flags.forwarded = 1;
418c19800e8SDoug Rabson 
419c19800e8SDoug Rabson     if(f.proxiable){
420c19800e8SDoug Rabson 	if(!tgt->flags.proxiable){
421c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
422c19800e8SDoug Rabson 		    "Bad request for proxiable ticket");
423c19800e8SDoug Rabson 	    return KRB5KDC_ERR_BADOPTION;
424c19800e8SDoug Rabson 	}
425c19800e8SDoug Rabson 	et->flags.proxiable = 1;
426c19800e8SDoug Rabson     }
427c19800e8SDoug Rabson     if(f.proxy){
428c19800e8SDoug Rabson 	if(!tgt->flags.proxiable){
429c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
430c19800e8SDoug Rabson 		    "Request to proxy non-proxiable ticket");
431c19800e8SDoug Rabson 	    return KRB5KDC_ERR_BADOPTION;
432c19800e8SDoug Rabson 	}
433c19800e8SDoug Rabson 	et->flags.proxy = 1;
434c19800e8SDoug Rabson 	et->caddr = b->addresses;
435c19800e8SDoug Rabson     }
436c19800e8SDoug Rabson     if(tgt->flags.proxy)
437c19800e8SDoug Rabson 	et->flags.proxy = 1;
438c19800e8SDoug Rabson 
439c19800e8SDoug Rabson     if(f.allow_postdate){
440c19800e8SDoug Rabson 	if(!tgt->flags.may_postdate){
441c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
442c19800e8SDoug Rabson 		    "Bad request for post-datable ticket");
443c19800e8SDoug Rabson 	    return KRB5KDC_ERR_BADOPTION;
444c19800e8SDoug Rabson 	}
445c19800e8SDoug Rabson 	et->flags.may_postdate = 1;
446c19800e8SDoug Rabson     }
447c19800e8SDoug Rabson     if(f.postdated){
448c19800e8SDoug Rabson 	if(!tgt->flags.may_postdate){
449c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
450c19800e8SDoug Rabson 		    "Bad request for postdated ticket");
451c19800e8SDoug Rabson 	    return KRB5KDC_ERR_BADOPTION;
452c19800e8SDoug Rabson 	}
453c19800e8SDoug Rabson 	if(b->from)
454c19800e8SDoug Rabson 	    *et->starttime = *b->from;
455c19800e8SDoug Rabson 	et->flags.postdated = 1;
456c19800e8SDoug Rabson 	et->flags.invalid = 1;
457c19800e8SDoug Rabson     }else if(b->from && *b->from > kdc_time + context->max_skew){
458c19800e8SDoug Rabson 	kdc_log(context, config, 0, "Ticket cannot be postdated");
459c19800e8SDoug Rabson 	return KRB5KDC_ERR_CANNOT_POSTDATE;
460c19800e8SDoug Rabson     }
461c19800e8SDoug Rabson 
462c19800e8SDoug Rabson     if(f.renewable){
463ae771770SStanislav Sedov 	if(!tgt->flags.renewable || tgt->renew_till == NULL){
464c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
465c19800e8SDoug Rabson 		    "Bad request for renewable ticket");
466c19800e8SDoug Rabson 	    return KRB5KDC_ERR_BADOPTION;
467c19800e8SDoug Rabson 	}
468c19800e8SDoug Rabson 	et->flags.renewable = 1;
469c19800e8SDoug Rabson 	ALLOC(et->renew_till);
470c19800e8SDoug Rabson 	_kdc_fix_time(&b->rtime);
471c19800e8SDoug Rabson 	*et->renew_till = *b->rtime;
472c19800e8SDoug Rabson     }
473c19800e8SDoug Rabson     if(f.renew){
474c19800e8SDoug Rabson 	time_t old_life;
475c19800e8SDoug Rabson 	if(!tgt->flags.renewable || tgt->renew_till == NULL){
476c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
477c19800e8SDoug Rabson 		    "Request to renew non-renewable ticket");
478c19800e8SDoug Rabson 	    return KRB5KDC_ERR_BADOPTION;
479c19800e8SDoug Rabson 	}
480c19800e8SDoug Rabson 	old_life = tgt->endtime;
481c19800e8SDoug Rabson 	if(tgt->starttime)
482c19800e8SDoug Rabson 	    old_life -= *tgt->starttime;
483c19800e8SDoug Rabson 	else
484c19800e8SDoug Rabson 	    old_life -= tgt->authtime;
485c19800e8SDoug Rabson 	et->endtime = *et->starttime + old_life;
486c19800e8SDoug Rabson 	if (et->renew_till != NULL)
487c19800e8SDoug Rabson 	    et->endtime = min(*et->renew_till, et->endtime);
488c19800e8SDoug Rabson     }
489c19800e8SDoug Rabson 
490c19800e8SDoug Rabson #if 0
491c19800e8SDoug Rabson     /* checks for excess flags */
492c19800e8SDoug Rabson     if(f.request_anonymous && !config->allow_anonymous){
493c19800e8SDoug Rabson 	kdc_log(context, config, 0,
494c19800e8SDoug Rabson 		"Request for anonymous ticket");
495c19800e8SDoug Rabson 	return KRB5KDC_ERR_BADOPTION;
496c19800e8SDoug Rabson     }
497c19800e8SDoug Rabson #endif
498c19800e8SDoug Rabson     return 0;
499c19800e8SDoug Rabson }
500c19800e8SDoug Rabson 
501c19800e8SDoug Rabson /*
502ae771770SStanislav Sedov  * Determine if constrained delegation is allowed from this client to this server
503c19800e8SDoug Rabson  */
504c19800e8SDoug Rabson 
505c19800e8SDoug Rabson static krb5_error_code
check_constrained_delegation(krb5_context context,krb5_kdc_configuration * config,HDB * clientdb,hdb_entry_ex * client,hdb_entry_ex * server,krb5_const_principal target)506c19800e8SDoug Rabson check_constrained_delegation(krb5_context context,
507c19800e8SDoug Rabson 			     krb5_kdc_configuration *config,
508ae771770SStanislav Sedov 			     HDB *clientdb,
509c19800e8SDoug Rabson 			     hdb_entry_ex *client,
510ae771770SStanislav Sedov 			     hdb_entry_ex *server,
511ae771770SStanislav Sedov 			     krb5_const_principal target)
512c19800e8SDoug Rabson {
513c19800e8SDoug Rabson     const HDB_Ext_Constrained_delegation_acl *acl;
514c19800e8SDoug Rabson     krb5_error_code ret;
515ae771770SStanislav Sedov     size_t i;
516ae771770SStanislav Sedov 
517ae771770SStanislav Sedov     /*
518ae771770SStanislav Sedov      * constrained_delegation (S4U2Proxy) only works within
519ae771770SStanislav Sedov      * the same realm. We use the already canonicalized version
520ae771770SStanislav Sedov      * of the principals here, while "target" is the principal
521ae771770SStanislav Sedov      * provided by the client.
522ae771770SStanislav Sedov      */
523ae771770SStanislav Sedov     if(!krb5_realm_compare(context, client->entry.principal, server->entry.principal)) {
524ae771770SStanislav Sedov 	ret = KRB5KDC_ERR_BADOPTION;
525ae771770SStanislav Sedov 	kdc_log(context, config, 0,
526ae771770SStanislav Sedov 	    "Bad request for constrained delegation");
527ae771770SStanislav Sedov 	return ret;
528ae771770SStanislav Sedov     }
529ae771770SStanislav Sedov 
530ae771770SStanislav Sedov     if (clientdb->hdb_check_constrained_delegation) {
531ae771770SStanislav Sedov 	ret = clientdb->hdb_check_constrained_delegation(context, clientdb, client, target);
532ae771770SStanislav Sedov 	if (ret == 0)
533ae771770SStanislav Sedov 	    return 0;
534ae771770SStanislav Sedov     } else {
535ae771770SStanislav Sedov 	/* if client delegates to itself, that ok */
536ae771770SStanislav Sedov 	if (krb5_principal_compare(context, client->entry.principal, server->entry.principal) == TRUE)
537ae771770SStanislav Sedov 	    return 0;
538c19800e8SDoug Rabson 
539c19800e8SDoug Rabson 	ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
540c19800e8SDoug Rabson 	if (ret) {
541ae771770SStanislav Sedov 	    krb5_clear_error_message(context);
542c19800e8SDoug Rabson 	    return ret;
543c19800e8SDoug Rabson 	}
544c19800e8SDoug Rabson 
545c19800e8SDoug Rabson 	if (acl) {
546c19800e8SDoug Rabson 	    for (i = 0; i < acl->len; i++) {
547ae771770SStanislav Sedov 		if (krb5_principal_compare(context, target, &acl->val[i]) == TRUE)
548c19800e8SDoug Rabson 		    return 0;
549c19800e8SDoug Rabson 	    }
550c19800e8SDoug Rabson 	}
551ae771770SStanislav Sedov 	ret = KRB5KDC_ERR_BADOPTION;
552ae771770SStanislav Sedov     }
553c19800e8SDoug Rabson     kdc_log(context, config, 0,
554c19800e8SDoug Rabson 	    "Bad request for constrained delegation");
555ae771770SStanislav Sedov     return ret;
556ae771770SStanislav Sedov }
557ae771770SStanislav Sedov 
558ae771770SStanislav Sedov /*
559ae771770SStanislav Sedov  * Determine if s4u2self is allowed from this client to this server
560ae771770SStanislav Sedov  *
561ae771770SStanislav Sedov  * For example, regardless of the principal being impersonated, if the
562ae771770SStanislav Sedov  * 'client' and 'server' are the same, then it's safe.
563ae771770SStanislav Sedov  */
564ae771770SStanislav Sedov 
565ae771770SStanislav Sedov static krb5_error_code
check_s4u2self(krb5_context context,krb5_kdc_configuration * config,HDB * clientdb,hdb_entry_ex * client,krb5_const_principal server)566ae771770SStanislav Sedov check_s4u2self(krb5_context context,
567ae771770SStanislav Sedov 	       krb5_kdc_configuration *config,
568ae771770SStanislav Sedov 	       HDB *clientdb,
569ae771770SStanislav Sedov 	       hdb_entry_ex *client,
570ae771770SStanislav Sedov 	       krb5_const_principal server)
571ae771770SStanislav Sedov {
572ae771770SStanislav Sedov     krb5_error_code ret;
573ae771770SStanislav Sedov 
574ae771770SStanislav Sedov     /* if client does a s4u2self to itself, that ok */
575ae771770SStanislav Sedov     if (krb5_principal_compare(context, client->entry.principal, server) == TRUE)
576ae771770SStanislav Sedov 	return 0;
577ae771770SStanislav Sedov 
578ae771770SStanislav Sedov     if (clientdb->hdb_check_s4u2self) {
579ae771770SStanislav Sedov 	ret = clientdb->hdb_check_s4u2self(context, clientdb, client, server);
580ae771770SStanislav Sedov 	if (ret == 0)
581ae771770SStanislav Sedov 	    return 0;
582ae771770SStanislav Sedov     } else {
583ae771770SStanislav Sedov 	ret = KRB5KDC_ERR_BADOPTION;
584ae771770SStanislav Sedov     }
585ae771770SStanislav Sedov     return ret;
586c19800e8SDoug Rabson }
587c19800e8SDoug Rabson 
588c19800e8SDoug Rabson /*
589c19800e8SDoug Rabson  *
590c19800e8SDoug Rabson  */
591c19800e8SDoug Rabson 
592c19800e8SDoug Rabson static krb5_error_code
verify_flags(krb5_context context,krb5_kdc_configuration * config,const EncTicketPart * et,const char * pstr)593c19800e8SDoug Rabson verify_flags (krb5_context context,
594c19800e8SDoug Rabson 	      krb5_kdc_configuration *config,
595c19800e8SDoug Rabson 	      const EncTicketPart *et,
596c19800e8SDoug Rabson 	      const char *pstr)
597c19800e8SDoug Rabson {
598c19800e8SDoug Rabson     if(et->endtime < kdc_time){
599c19800e8SDoug Rabson 	kdc_log(context, config, 0, "Ticket expired (%s)", pstr);
600c19800e8SDoug Rabson 	return KRB5KRB_AP_ERR_TKT_EXPIRED;
601c19800e8SDoug Rabson     }
602c19800e8SDoug Rabson     if(et->flags.invalid){
603c19800e8SDoug Rabson 	kdc_log(context, config, 0, "Ticket not valid (%s)", pstr);
604c19800e8SDoug Rabson 	return KRB5KRB_AP_ERR_TKT_NYV;
605c19800e8SDoug Rabson     }
606c19800e8SDoug Rabson     return 0;
607c19800e8SDoug Rabson }
608c19800e8SDoug Rabson 
609c19800e8SDoug Rabson /*
610c19800e8SDoug Rabson  *
611c19800e8SDoug Rabson  */
612c19800e8SDoug Rabson 
613c19800e8SDoug Rabson static krb5_error_code
fix_transited_encoding(krb5_context context,krb5_kdc_configuration * config,krb5_boolean check_policy,const TransitedEncoding * tr,EncTicketPart * et,const char * client_realm,const char * server_realm,const char * tgt_realm)614c19800e8SDoug Rabson fix_transited_encoding(krb5_context context,
615c19800e8SDoug Rabson 		       krb5_kdc_configuration *config,
616c19800e8SDoug Rabson 		       krb5_boolean check_policy,
617c19800e8SDoug Rabson 		       const TransitedEncoding *tr,
618c19800e8SDoug Rabson 		       EncTicketPart *et,
619c19800e8SDoug Rabson 		       const char *client_realm,
620c19800e8SDoug Rabson 		       const char *server_realm,
621c19800e8SDoug Rabson 		       const char *tgt_realm)
622c19800e8SDoug Rabson {
623c19800e8SDoug Rabson     krb5_error_code ret = 0;
624c19800e8SDoug Rabson     char **realms, **tmp;
625ae771770SStanislav Sedov     unsigned int num_realms;
626ae771770SStanislav Sedov     size_t i;
627c19800e8SDoug Rabson 
628c19800e8SDoug Rabson     switch (tr->tr_type) {
629c19800e8SDoug Rabson     case DOMAIN_X500_COMPRESS:
630c19800e8SDoug Rabson 	break;
631c19800e8SDoug Rabson     case 0:
632c19800e8SDoug Rabson 	/*
633c19800e8SDoug Rabson 	 * Allow empty content of type 0 because that is was Microsoft
634c19800e8SDoug Rabson 	 * generates in their TGT.
635c19800e8SDoug Rabson 	 */
636c19800e8SDoug Rabson 	if (tr->contents.length == 0)
637c19800e8SDoug Rabson 	    break;
638c19800e8SDoug Rabson 	kdc_log(context, config, 0,
639c19800e8SDoug Rabson 		"Transited type 0 with non empty content");
640c19800e8SDoug Rabson 	return KRB5KDC_ERR_TRTYPE_NOSUPP;
641c19800e8SDoug Rabson     default:
642c19800e8SDoug Rabson 	kdc_log(context, config, 0,
643c19800e8SDoug Rabson 		"Unknown transited type: %u", tr->tr_type);
644c19800e8SDoug Rabson 	return KRB5KDC_ERR_TRTYPE_NOSUPP;
645c19800e8SDoug Rabson     }
646c19800e8SDoug Rabson 
647c19800e8SDoug Rabson     ret = krb5_domain_x500_decode(context,
648c19800e8SDoug Rabson 				  tr->contents,
649c19800e8SDoug Rabson 				  &realms,
650c19800e8SDoug Rabson 				  &num_realms,
651c19800e8SDoug Rabson 				  client_realm,
652c19800e8SDoug Rabson 				  server_realm);
653c19800e8SDoug Rabson     if(ret){
654c19800e8SDoug Rabson 	krb5_warn(context, ret,
655c19800e8SDoug Rabson 		  "Decoding transited encoding");
656c19800e8SDoug Rabson 	return ret;
657c19800e8SDoug Rabson     }
658f8041e36SCy Schubert 
659f8041e36SCy Schubert     /*
660f8041e36SCy Schubert      * If the realm of the presented tgt is neither the client nor the server
661f8041e36SCy Schubert      * realm, it is a transit realm and must be added to transited set.
662f8041e36SCy Schubert      */
663c19800e8SDoug Rabson     if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
664ae771770SStanislav Sedov 	if (num_realms + 1 > UINT_MAX/sizeof(*realms)) {
665c19800e8SDoug Rabson 	    ret = ERANGE;
666c19800e8SDoug Rabson 	    goto free_realms;
667c19800e8SDoug Rabson 	}
668c19800e8SDoug Rabson 	tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
669c19800e8SDoug Rabson 	if(tmp == NULL){
670c19800e8SDoug Rabson 	    ret = ENOMEM;
671c19800e8SDoug Rabson 	    goto free_realms;
672c19800e8SDoug Rabson 	}
673c19800e8SDoug Rabson 	realms = tmp;
674c19800e8SDoug Rabson 	realms[num_realms] = strdup(tgt_realm);
675c19800e8SDoug Rabson 	if(realms[num_realms] == NULL){
676c19800e8SDoug Rabson 	    ret = ENOMEM;
677c19800e8SDoug Rabson 	    goto free_realms;
678c19800e8SDoug Rabson 	}
679c19800e8SDoug Rabson 	num_realms++;
680c19800e8SDoug Rabson     }
681c19800e8SDoug Rabson     if(num_realms == 0) {
682c19800e8SDoug Rabson 	if(strcmp(client_realm, server_realm))
683c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
684c19800e8SDoug Rabson 		    "cross-realm %s -> %s", client_realm, server_realm);
685c19800e8SDoug Rabson     } else {
686c19800e8SDoug Rabson 	size_t l = 0;
687c19800e8SDoug Rabson 	char *rs;
688c19800e8SDoug Rabson 	for(i = 0; i < num_realms; i++)
689c19800e8SDoug Rabson 	    l += strlen(realms[i]) + 2;
690c19800e8SDoug Rabson 	rs = malloc(l);
691c19800e8SDoug Rabson 	if(rs != NULL) {
692c19800e8SDoug Rabson 	    *rs = '\0';
693c19800e8SDoug Rabson 	    for(i = 0; i < num_realms; i++) {
694c19800e8SDoug Rabson 		if(i > 0)
695c19800e8SDoug Rabson 		    strlcat(rs, ", ", l);
696c19800e8SDoug Rabson 		strlcat(rs, realms[i], l);
697c19800e8SDoug Rabson 	    }
698c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
699c19800e8SDoug Rabson 		    "cross-realm %s -> %s via [%s]",
700c19800e8SDoug Rabson 		    client_realm, server_realm, rs);
701c19800e8SDoug Rabson 	    free(rs);
702c19800e8SDoug Rabson 	}
703c19800e8SDoug Rabson     }
704c19800e8SDoug Rabson     if(check_policy) {
705c19800e8SDoug Rabson 	ret = krb5_check_transited(context, client_realm,
706c19800e8SDoug Rabson 				   server_realm,
707c19800e8SDoug Rabson 				   realms, num_realms, NULL);
708c19800e8SDoug Rabson 	if(ret) {
709c19800e8SDoug Rabson 	    krb5_warn(context, ret, "cross-realm %s -> %s",
710c19800e8SDoug Rabson 		      client_realm, server_realm);
711c19800e8SDoug Rabson 	    goto free_realms;
712c19800e8SDoug Rabson 	}
713c19800e8SDoug Rabson 	et->flags.transited_policy_checked = 1;
714c19800e8SDoug Rabson     }
715c19800e8SDoug Rabson     et->transited.tr_type = DOMAIN_X500_COMPRESS;
716c19800e8SDoug Rabson     ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
717c19800e8SDoug Rabson     if(ret)
718c19800e8SDoug Rabson 	krb5_warn(context, ret, "Encoding transited encoding");
719c19800e8SDoug Rabson   free_realms:
720c19800e8SDoug Rabson     for(i = 0; i < num_realms; i++)
721c19800e8SDoug Rabson 	free(realms[i]);
722c19800e8SDoug Rabson     free(realms);
723c19800e8SDoug Rabson     return ret;
724c19800e8SDoug Rabson }
725c19800e8SDoug Rabson 
726c19800e8SDoug Rabson 
727c19800e8SDoug Rabson static krb5_error_code
tgs_make_reply(krb5_context context,krb5_kdc_configuration * config,KDC_REQ_BODY * b,krb5_const_principal tgt_name,const EncTicketPart * tgt,const krb5_keyblock * replykey,int rk_is_subkey,const EncryptionKey * serverkey,const krb5_keyblock * sessionkey,krb5_kvno kvno,AuthorizationData * auth_data,hdb_entry_ex * server,krb5_principal server_principal,const char * server_name,hdb_entry_ex * client,krb5_principal client_principal,const char * tgt_realm,hdb_entry_ex * krbtgt,krb5_enctype krbtgt_etype,krb5_principals spp,const krb5_data * rspac,const METHOD_DATA * enc_pa_data,const char ** e_text,krb5_data * reply)728c19800e8SDoug Rabson tgs_make_reply(krb5_context context,
729c19800e8SDoug Rabson 	       krb5_kdc_configuration *config,
730c19800e8SDoug Rabson 	       KDC_REQ_BODY *b,
731c19800e8SDoug Rabson 	       krb5_const_principal tgt_name,
732c19800e8SDoug Rabson 	       const EncTicketPart *tgt,
733ae771770SStanislav Sedov 	       const krb5_keyblock *replykey,
734ae771770SStanislav Sedov 	       int rk_is_subkey,
735c19800e8SDoug Rabson 	       const EncryptionKey *serverkey,
736c19800e8SDoug Rabson 	       const krb5_keyblock *sessionkey,
737c19800e8SDoug Rabson 	       krb5_kvno kvno,
738c19800e8SDoug Rabson 	       AuthorizationData *auth_data,
739c19800e8SDoug Rabson 	       hdb_entry_ex *server,
740ae771770SStanislav Sedov 	       krb5_principal server_principal,
741c19800e8SDoug Rabson 	       const char *server_name,
742c19800e8SDoug Rabson 	       hdb_entry_ex *client,
743c19800e8SDoug Rabson 	       krb5_principal client_principal,
744f8041e36SCy Schubert 	       const char *tgt_realm,
745c19800e8SDoug Rabson 	       hdb_entry_ex *krbtgt,
746c19800e8SDoug Rabson 	       krb5_enctype krbtgt_etype,
747ae771770SStanislav Sedov 	       krb5_principals spp,
748c19800e8SDoug Rabson 	       const krb5_data *rspac,
749ae771770SStanislav Sedov 	       const METHOD_DATA *enc_pa_data,
750c19800e8SDoug Rabson 	       const char **e_text,
751c19800e8SDoug Rabson 	       krb5_data *reply)
752c19800e8SDoug Rabson {
753c19800e8SDoug Rabson     KDC_REP rep;
754c19800e8SDoug Rabson     EncKDCRepPart ek;
755c19800e8SDoug Rabson     EncTicketPart et;
756c19800e8SDoug Rabson     KDCOptions f = b->kdc_options;
757c19800e8SDoug Rabson     krb5_error_code ret;
758ae771770SStanislav Sedov     int is_weak = 0;
759c19800e8SDoug Rabson 
760c19800e8SDoug Rabson     memset(&rep, 0, sizeof(rep));
761c19800e8SDoug Rabson     memset(&et, 0, sizeof(et));
762c19800e8SDoug Rabson     memset(&ek, 0, sizeof(ek));
763c19800e8SDoug Rabson 
764c19800e8SDoug Rabson     rep.pvno = 5;
765c19800e8SDoug Rabson     rep.msg_type = krb_tgs_rep;
766c19800e8SDoug Rabson 
767c19800e8SDoug Rabson     et.authtime = tgt->authtime;
768c19800e8SDoug Rabson     _kdc_fix_time(&b->till);
769c19800e8SDoug Rabson     et.endtime = min(tgt->endtime, *b->till);
770c19800e8SDoug Rabson     ALLOC(et.starttime);
771c19800e8SDoug Rabson     *et.starttime = kdc_time;
772c19800e8SDoug Rabson 
773c19800e8SDoug Rabson     ret = check_tgs_flags(context, config, b, tgt, &et);
774c19800e8SDoug Rabson     if(ret)
775c19800e8SDoug Rabson 	goto out;
776c19800e8SDoug Rabson 
777c19800e8SDoug Rabson     /* We should check the transited encoding if:
778c19800e8SDoug Rabson        1) the request doesn't ask not to be checked
779c19800e8SDoug Rabson        2) globally enforcing a check
780c19800e8SDoug Rabson        3) principal requires checking
781c19800e8SDoug Rabson        4) we allow non-check per-principal, but principal isn't marked as allowing this
782c19800e8SDoug Rabson        5) we don't globally allow this
783c19800e8SDoug Rabson     */
784c19800e8SDoug Rabson 
785c19800e8SDoug Rabson #define GLOBAL_FORCE_TRANSITED_CHECK		\
786c19800e8SDoug Rabson     (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
787c19800e8SDoug Rabson #define GLOBAL_ALLOW_PER_PRINCIPAL			\
788c19800e8SDoug Rabson     (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
789c19800e8SDoug Rabson #define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK			\
790c19800e8SDoug Rabson     (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
791c19800e8SDoug Rabson 
792c19800e8SDoug Rabson /* these will consult the database in future release */
793c19800e8SDoug Rabson #define PRINCIPAL_FORCE_TRANSITED_CHECK(P)		0
794c19800e8SDoug Rabson #define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P)	0
795c19800e8SDoug Rabson 
796c19800e8SDoug Rabson     ret = fix_transited_encoding(context, config,
797c19800e8SDoug Rabson 				 !f.disable_transited_check ||
798c19800e8SDoug Rabson 				 GLOBAL_FORCE_TRANSITED_CHECK ||
799c19800e8SDoug Rabson 				 PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
800c19800e8SDoug Rabson 				 !((GLOBAL_ALLOW_PER_PRINCIPAL &&
801c19800e8SDoug Rabson 				    PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
802c19800e8SDoug Rabson 				   GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
803c19800e8SDoug Rabson 				 &tgt->transited, &et,
804ae771770SStanislav Sedov 				 krb5_principal_get_realm(context, client_principal),
805ae771770SStanislav Sedov 				 krb5_principal_get_realm(context, server->entry.principal),
806f8041e36SCy Schubert 				 tgt_realm);
807c19800e8SDoug Rabson     if(ret)
808c19800e8SDoug Rabson 	goto out;
809c19800e8SDoug Rabson 
810ae771770SStanislav Sedov     copy_Realm(&server_principal->realm, &rep.ticket.realm);
811ae771770SStanislav Sedov     _krb5_principal2principalname(&rep.ticket.sname, server_principal);
812c19800e8SDoug Rabson     copy_Realm(&tgt_name->realm, &rep.crealm);
813c19800e8SDoug Rabson /*
814c19800e8SDoug Rabson     if (f.request_anonymous)
815c19800e8SDoug Rabson 	_kdc_make_anonymous_principalname (&rep.cname);
816c19800e8SDoug Rabson     else */
817c19800e8SDoug Rabson 
818c19800e8SDoug Rabson     copy_PrincipalName(&tgt_name->name, &rep.cname);
819c19800e8SDoug Rabson     rep.ticket.tkt_vno = 5;
820c19800e8SDoug Rabson 
821c19800e8SDoug Rabson     ek.caddr = et.caddr;
822c19800e8SDoug Rabson     if(et.caddr == NULL)
823c19800e8SDoug Rabson 	et.caddr = tgt->caddr;
824c19800e8SDoug Rabson 
825c19800e8SDoug Rabson     {
826c19800e8SDoug Rabson 	time_t life;
827c19800e8SDoug Rabson 	life = et.endtime - *et.starttime;
828c19800e8SDoug Rabson 	if(client && client->entry.max_life)
829c19800e8SDoug Rabson 	    life = min(life, *client->entry.max_life);
830c19800e8SDoug Rabson 	if(server->entry.max_life)
831c19800e8SDoug Rabson 	    life = min(life, *server->entry.max_life);
832c19800e8SDoug Rabson 	et.endtime = *et.starttime + life;
833c19800e8SDoug Rabson     }
834c19800e8SDoug Rabson     if(f.renewable_ok && tgt->flags.renewable &&
835ae771770SStanislav Sedov        et.renew_till == NULL && et.endtime < *b->till &&
836ae771770SStanislav Sedov        tgt->renew_till != NULL)
837ae771770SStanislav Sedov     {
838c19800e8SDoug Rabson 	et.flags.renewable = 1;
839c19800e8SDoug Rabson 	ALLOC(et.renew_till);
840c19800e8SDoug Rabson 	*et.renew_till = *b->till;
841c19800e8SDoug Rabson     }
842c19800e8SDoug Rabson     if(et.renew_till){
843c19800e8SDoug Rabson 	time_t renew;
844c19800e8SDoug Rabson 	renew = *et.renew_till - et.authtime;
845c19800e8SDoug Rabson 	if(client && client->entry.max_renew)
846c19800e8SDoug Rabson 	    renew = min(renew, *client->entry.max_renew);
847c19800e8SDoug Rabson 	if(server->entry.max_renew)
848c19800e8SDoug Rabson 	    renew = min(renew, *server->entry.max_renew);
849c19800e8SDoug Rabson 	*et.renew_till = et.authtime + renew;
850c19800e8SDoug Rabson     }
851c19800e8SDoug Rabson 
852c19800e8SDoug Rabson     if(et.renew_till){
853c19800e8SDoug Rabson 	*et.renew_till = min(*et.renew_till, *tgt->renew_till);
854c19800e8SDoug Rabson 	*et.starttime = min(*et.starttime, *et.renew_till);
855c19800e8SDoug Rabson 	et.endtime = min(et.endtime, *et.renew_till);
856c19800e8SDoug Rabson     }
857c19800e8SDoug Rabson 
858c19800e8SDoug Rabson     *et.starttime = min(*et.starttime, et.endtime);
859c19800e8SDoug Rabson 
860c19800e8SDoug Rabson     if(*et.starttime == et.endtime){
861c19800e8SDoug Rabson 	ret = KRB5KDC_ERR_NEVER_VALID;
862c19800e8SDoug Rabson 	goto out;
863c19800e8SDoug Rabson     }
864c19800e8SDoug Rabson     if(et.renew_till && et.endtime == *et.renew_till){
865c19800e8SDoug Rabson 	free(et.renew_till);
866c19800e8SDoug Rabson 	et.renew_till = NULL;
867c19800e8SDoug Rabson 	et.flags.renewable = 0;
868c19800e8SDoug Rabson     }
869c19800e8SDoug Rabson 
870c19800e8SDoug Rabson     et.flags.pre_authent = tgt->flags.pre_authent;
871c19800e8SDoug Rabson     et.flags.hw_authent  = tgt->flags.hw_authent;
872c19800e8SDoug Rabson     et.flags.anonymous   = tgt->flags.anonymous;
873c19800e8SDoug Rabson     et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
874c19800e8SDoug Rabson 
875ae771770SStanislav Sedov     if(rspac->length) {
876ae771770SStanislav Sedov 	/*
877ae771770SStanislav Sedov 	 * No not need to filter out the any PAC from the
878ae771770SStanislav Sedov 	 * auth_data since it's signed by the KDC.
879ae771770SStanislav Sedov 	 */
880ae771770SStanislav Sedov 	ret = _kdc_tkt_add_if_relevant_ad(context, &et,
881ae771770SStanislav Sedov 					  KRB5_AUTHDATA_WIN2K_PAC, rspac);
882ae771770SStanislav Sedov 	if (ret)
883ae771770SStanislav Sedov 	    goto out;
884ae771770SStanislav Sedov     }
885ae771770SStanislav Sedov 
886c19800e8SDoug Rabson     if (auth_data) {
887ae771770SStanislav Sedov 	unsigned int i = 0;
888ae771770SStanislav Sedov 
889ae771770SStanislav Sedov 	/* XXX check authdata */
890ae771770SStanislav Sedov 
891ae771770SStanislav Sedov 	if (et.authorization_data == NULL) {
892c19800e8SDoug Rabson 	    et.authorization_data = calloc(1, sizeof(*et.authorization_data));
893c19800e8SDoug Rabson 	    if (et.authorization_data == NULL) {
894c19800e8SDoug Rabson 		ret = ENOMEM;
895ae771770SStanislav Sedov 		krb5_set_error_message(context, ret, "malloc: out of memory");
896c19800e8SDoug Rabson 		goto out;
897c19800e8SDoug Rabson 	    }
898ae771770SStanislav Sedov 	}
899ae771770SStanislav Sedov 	for(i = 0; i < auth_data->len ; i++) {
900ae771770SStanislav Sedov 	    ret = add_AuthorizationData(et.authorization_data, &auth_data->val[i]);
901ae771770SStanislav Sedov 	    if (ret) {
902ae771770SStanislav Sedov 		krb5_set_error_message(context, ret, "malloc: out of memory");
903c19800e8SDoug Rabson 		goto out;
904ae771770SStanislav Sedov 	    }
905ae771770SStanislav Sedov 	}
906c19800e8SDoug Rabson 
907c19800e8SDoug Rabson 	/* Filter out type KRB5SignedPath */
908c19800e8SDoug Rabson 	ret = find_KRB5SignedPath(context, et.authorization_data, NULL);
909c19800e8SDoug Rabson 	if (ret == 0) {
910c19800e8SDoug Rabson 	    if (et.authorization_data->len == 1) {
911c19800e8SDoug Rabson 		free_AuthorizationData(et.authorization_data);
912c19800e8SDoug Rabson 		free(et.authorization_data);
913c19800e8SDoug Rabson 		et.authorization_data = NULL;
914c19800e8SDoug Rabson 	    } else {
915c19800e8SDoug Rabson 		AuthorizationData *ad = et.authorization_data;
916c19800e8SDoug Rabson 		free_AuthorizationDataElement(&ad->val[ad->len - 1]);
917c19800e8SDoug Rabson 		ad->len--;
918c19800e8SDoug Rabson 	    }
919c19800e8SDoug Rabson 	}
920c19800e8SDoug Rabson     }
921c19800e8SDoug Rabson 
922c19800e8SDoug Rabson     ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key);
923c19800e8SDoug Rabson     if (ret)
924c19800e8SDoug Rabson 	goto out;
925ae771770SStanislav Sedov     et.crealm = tgt_name->realm;
926c19800e8SDoug Rabson     et.cname = tgt_name->name;
927c19800e8SDoug Rabson 
928c19800e8SDoug Rabson     ek.key = et.key;
929c19800e8SDoug Rabson     /* MIT must have at least one last_req */
930c19800e8SDoug Rabson     ek.last_req.len = 1;
931c19800e8SDoug Rabson     ek.last_req.val = calloc(1, sizeof(*ek.last_req.val));
932c19800e8SDoug Rabson     if (ek.last_req.val == NULL) {
933c19800e8SDoug Rabson 	ret = ENOMEM;
934c19800e8SDoug Rabson 	goto out;
935c19800e8SDoug Rabson     }
936c19800e8SDoug Rabson     ek.nonce = b->nonce;
937c19800e8SDoug Rabson     ek.flags = et.flags;
938c19800e8SDoug Rabson     ek.authtime = et.authtime;
939c19800e8SDoug Rabson     ek.starttime = et.starttime;
940c19800e8SDoug Rabson     ek.endtime = et.endtime;
941c19800e8SDoug Rabson     ek.renew_till = et.renew_till;
942c19800e8SDoug Rabson     ek.srealm = rep.ticket.realm;
943c19800e8SDoug Rabson     ek.sname = rep.ticket.sname;
944c19800e8SDoug Rabson 
945c19800e8SDoug Rabson     _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime,
946c19800e8SDoug Rabson 		       et.endtime, et.renew_till);
947c19800e8SDoug Rabson 
948c19800e8SDoug Rabson     /* Don't sign cross realm tickets, they can't be checked anyway */
949c19800e8SDoug Rabson     {
950c19800e8SDoug Rabson 	char *r = get_krbtgt_realm(&ek.sname);
951c19800e8SDoug Rabson 
952c19800e8SDoug Rabson 	if (r == NULL || strcmp(r, ek.srealm) == 0) {
953c19800e8SDoug Rabson 	    ret = _kdc_add_KRB5SignedPath(context,
954c19800e8SDoug Rabson 					  config,
955c19800e8SDoug Rabson 					  krbtgt,
956c19800e8SDoug Rabson 					  krbtgt_etype,
957ae771770SStanislav Sedov 					  client_principal,
958c19800e8SDoug Rabson 					  NULL,
959c19800e8SDoug Rabson 					  spp,
960c19800e8SDoug Rabson 					  &et);
961c19800e8SDoug Rabson 	    if (ret)
962c19800e8SDoug Rabson 		goto out;
963c19800e8SDoug Rabson 	}
964c19800e8SDoug Rabson     }
965c19800e8SDoug Rabson 
966ae771770SStanislav Sedov     if (enc_pa_data->len) {
967ae771770SStanislav Sedov 	rep.padata = calloc(1, sizeof(*rep.padata));
968ae771770SStanislav Sedov 	if (rep.padata == NULL) {
969ae771770SStanislav Sedov 	    ret = ENOMEM;
970ae771770SStanislav Sedov 	    goto out;
971ae771770SStanislav Sedov 	}
972ae771770SStanislav Sedov 	ret = copy_METHOD_DATA(enc_pa_data, rep.padata);
973ae771770SStanislav Sedov 	if (ret)
974ae771770SStanislav Sedov 	    goto out;
975ae771770SStanislav Sedov     }
976ae771770SStanislav Sedov 
977ae771770SStanislav Sedov     if (krb5_enctype_valid(context, et.key.keytype) != 0
978ae771770SStanislav Sedov 	&& _kdc_is_weak_exception(server->entry.principal, et.key.keytype))
979ae771770SStanislav Sedov     {
980ae771770SStanislav Sedov 	krb5_enctype_enable(context, et.key.keytype);
981ae771770SStanislav Sedov 	is_weak = 1;
982ae771770SStanislav Sedov     }
983ae771770SStanislav Sedov 
984ae771770SStanislav Sedov 
985c19800e8SDoug Rabson     /* It is somewhat unclear where the etype in the following
986c19800e8SDoug Rabson        encryption should come from. What we have is a session
987c19800e8SDoug Rabson        key in the passed tgt, and a list of preferred etypes
988c19800e8SDoug Rabson        *for the new ticket*. Should we pick the best possible
989c19800e8SDoug Rabson        etype, given the keytype in the tgt, or should we look
990c19800e8SDoug Rabson        at the etype list here as well?  What if the tgt
991c19800e8SDoug Rabson        session key is DES3 and we want a ticket with a (say)
992c19800e8SDoug Rabson        CAST session key. Should the DES3 etype be added to the
993c19800e8SDoug Rabson        etype list, even if we don't want a session key with
994c19800e8SDoug Rabson        DES3? */
995c19800e8SDoug Rabson     ret = _kdc_encode_reply(context, config,
996c19800e8SDoug Rabson 			    &rep, &et, &ek, et.key.keytype,
997c19800e8SDoug Rabson 			    kvno,
998ae771770SStanislav Sedov 			    serverkey, 0, replykey, rk_is_subkey,
999ae771770SStanislav Sedov 			    e_text, reply);
1000ae771770SStanislav Sedov     if (is_weak)
1001ae771770SStanislav Sedov 	krb5_enctype_disable(context, et.key.keytype);
1002ae771770SStanislav Sedov 
1003c19800e8SDoug Rabson out:
1004c19800e8SDoug Rabson     free_TGS_REP(&rep);
1005c19800e8SDoug Rabson     free_TransitedEncoding(&et.transited);
1006c19800e8SDoug Rabson     if(et.starttime)
1007c19800e8SDoug Rabson 	free(et.starttime);
1008c19800e8SDoug Rabson     if(et.renew_till)
1009c19800e8SDoug Rabson 	free(et.renew_till);
1010c19800e8SDoug Rabson     if(et.authorization_data) {
1011c19800e8SDoug Rabson 	free_AuthorizationData(et.authorization_data);
1012c19800e8SDoug Rabson 	free(et.authorization_data);
1013c19800e8SDoug Rabson     }
1014c19800e8SDoug Rabson     free_LastReq(&ek.last_req);
1015c19800e8SDoug Rabson     memset(et.key.keyvalue.data, 0, et.key.keyvalue.length);
1016c19800e8SDoug Rabson     free_EncryptionKey(&et.key);
1017c19800e8SDoug Rabson     return ret;
1018c19800e8SDoug Rabson }
1019c19800e8SDoug Rabson 
1020c19800e8SDoug Rabson static krb5_error_code
tgs_check_authenticator(krb5_context context,krb5_kdc_configuration * config,krb5_auth_context ac,KDC_REQ_BODY * b,const char ** e_text,krb5_keyblock * key)1021c19800e8SDoug Rabson tgs_check_authenticator(krb5_context context,
1022c19800e8SDoug Rabson 			krb5_kdc_configuration *config,
1023c19800e8SDoug Rabson 	                krb5_auth_context ac,
1024c19800e8SDoug Rabson 			KDC_REQ_BODY *b,
1025c19800e8SDoug Rabson 			const char **e_text,
1026c19800e8SDoug Rabson 			krb5_keyblock *key)
1027c19800e8SDoug Rabson {
1028c19800e8SDoug Rabson     krb5_authenticator auth;
1029ae771770SStanislav Sedov     size_t len = 0;
1030c19800e8SDoug Rabson     unsigned char *buf;
1031c19800e8SDoug Rabson     size_t buf_size;
1032c19800e8SDoug Rabson     krb5_error_code ret;
1033c19800e8SDoug Rabson     krb5_crypto crypto;
1034c19800e8SDoug Rabson 
1035c19800e8SDoug Rabson     krb5_auth_con_getauthenticator(context, ac, &auth);
1036c19800e8SDoug Rabson     if(auth->cksum == NULL){
1037c19800e8SDoug Rabson 	kdc_log(context, config, 0, "No authenticator in request");
1038c19800e8SDoug Rabson 	ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
1039c19800e8SDoug Rabson 	goto out;
1040c19800e8SDoug Rabson     }
1041c19800e8SDoug Rabson     /*
1042c19800e8SDoug Rabson      * according to RFC1510 it doesn't need to be keyed,
1043c19800e8SDoug Rabson      * but according to the latest draft it needs to.
1044c19800e8SDoug Rabson      */
1045c19800e8SDoug Rabson     if (
1046c19800e8SDoug Rabson #if 0
1047c19800e8SDoug Rabson !krb5_checksum_is_keyed(context, auth->cksum->cksumtype)
1048c19800e8SDoug Rabson 	||
1049c19800e8SDoug Rabson #endif
1050c19800e8SDoug Rabson  !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
1051c19800e8SDoug Rabson 	kdc_log(context, config, 0, "Bad checksum type in authenticator: %d",
1052c19800e8SDoug Rabson 		auth->cksum->cksumtype);
1053c19800e8SDoug Rabson 	ret =  KRB5KRB_AP_ERR_INAPP_CKSUM;
1054c19800e8SDoug Rabson 	goto out;
1055c19800e8SDoug Rabson     }
1056c19800e8SDoug Rabson 
1057c19800e8SDoug Rabson     /* XXX should not re-encode this */
1058c19800e8SDoug Rabson     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
1059c19800e8SDoug Rabson     if(ret){
1060ae771770SStanislav Sedov 	const char *msg = krb5_get_error_message(context, ret);
1061ae771770SStanislav Sedov 	kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s", msg);
1062ae771770SStanislav Sedov 	krb5_free_error_message(context, msg);
1063c19800e8SDoug Rabson 	goto out;
1064c19800e8SDoug Rabson     }
1065c19800e8SDoug Rabson     if(buf_size != len) {
1066c19800e8SDoug Rabson 	free(buf);
1067c19800e8SDoug Rabson 	kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
1068c19800e8SDoug Rabson 	*e_text = "KDC internal error";
1069c19800e8SDoug Rabson 	ret = KRB5KRB_ERR_GENERIC;
1070c19800e8SDoug Rabson 	goto out;
1071c19800e8SDoug Rabson     }
1072c19800e8SDoug Rabson     ret = krb5_crypto_init(context, key, 0, &crypto);
1073c19800e8SDoug Rabson     if (ret) {
1074ae771770SStanislav Sedov 	const char *msg = krb5_get_error_message(context, ret);
1075c19800e8SDoug Rabson 	free(buf);
1076ae771770SStanislav Sedov 	kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
1077ae771770SStanislav Sedov 	krb5_free_error_message(context, msg);
1078c19800e8SDoug Rabson 	goto out;
1079c19800e8SDoug Rabson     }
1080c19800e8SDoug Rabson     ret = krb5_verify_checksum(context,
1081c19800e8SDoug Rabson 			       crypto,
1082c19800e8SDoug Rabson 			       KRB5_KU_TGS_REQ_AUTH_CKSUM,
1083c19800e8SDoug Rabson 			       buf,
1084c19800e8SDoug Rabson 			       len,
1085c19800e8SDoug Rabson 			       auth->cksum);
1086c19800e8SDoug Rabson     free(buf);
1087c19800e8SDoug Rabson     krb5_crypto_destroy(context, crypto);
1088c19800e8SDoug Rabson     if(ret){
1089ae771770SStanislav Sedov 	const char *msg = krb5_get_error_message(context, ret);
1090c19800e8SDoug Rabson 	kdc_log(context, config, 0,
1091ae771770SStanislav Sedov 		"Failed to verify authenticator checksum: %s", msg);
1092ae771770SStanislav Sedov 	krb5_free_error_message(context, msg);
1093c19800e8SDoug Rabson     }
1094c19800e8SDoug Rabson out:
1095c19800e8SDoug Rabson     free_Authenticator(auth);
1096c19800e8SDoug Rabson     free(auth);
1097c19800e8SDoug Rabson     return ret;
1098c19800e8SDoug Rabson }
1099c19800e8SDoug Rabson 
1100c19800e8SDoug Rabson /*
1101c19800e8SDoug Rabson  *
1102c19800e8SDoug Rabson  */
1103c19800e8SDoug Rabson 
1104c19800e8SDoug Rabson static const char *
find_rpath(krb5_context context,Realm crealm,Realm srealm)1105c19800e8SDoug Rabson find_rpath(krb5_context context, Realm crealm, Realm srealm)
1106c19800e8SDoug Rabson {
1107c19800e8SDoug Rabson     const char *new_realm = krb5_config_get_string(context,
1108c19800e8SDoug Rabson 						   NULL,
1109c19800e8SDoug Rabson 						   "capaths",
1110c19800e8SDoug Rabson 						   crealm,
1111c19800e8SDoug Rabson 						   srealm,
1112c19800e8SDoug Rabson 						   NULL);
1113c19800e8SDoug Rabson     return new_realm;
1114c19800e8SDoug Rabson }
1115c19800e8SDoug Rabson 
1116c19800e8SDoug Rabson 
1117c19800e8SDoug Rabson static krb5_boolean
need_referral(krb5_context context,krb5_kdc_configuration * config,const KDCOptions * const options,krb5_principal server,krb5_realm ** realms)1118ae771770SStanislav Sedov need_referral(krb5_context context, krb5_kdc_configuration *config,
1119ae771770SStanislav Sedov 	      const KDCOptions * const options, krb5_principal server,
1120ae771770SStanislav Sedov 	      krb5_realm **realms)
1121c19800e8SDoug Rabson {
1122ae771770SStanislav Sedov     const char *name;
1123ae771770SStanislav Sedov 
1124ae771770SStanislav Sedov     if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST)
1125c19800e8SDoug Rabson 	return FALSE;
1126c19800e8SDoug Rabson 
1127ae771770SStanislav Sedov     if (server->name.name_string.len == 1)
1128ae771770SStanislav Sedov 	name = server->name.name_string.val[0];
1129ae771770SStanislav Sedov     else if (server->name.name_string.len > 1)
1130ae771770SStanislav Sedov 	name = server->name.name_string.val[1];
1131ae771770SStanislav Sedov     else
1132ae771770SStanislav Sedov 	return FALSE;
1133ae771770SStanislav Sedov 
1134ae771770SStanislav Sedov     kdc_log(context, config, 0, "Searching referral for %s", name);
1135ae771770SStanislav Sedov 
1136ae771770SStanislav Sedov     return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
1137c19800e8SDoug Rabson }
1138c19800e8SDoug Rabson 
1139c19800e8SDoug Rabson static krb5_error_code
tgs_parse_request(krb5_context context,krb5_kdc_configuration * config,KDC_REQ_BODY * b,const PA_DATA * tgs_req,hdb_entry_ex ** krbtgt,krb5_enctype * krbtgt_etype,krb5_ticket ** ticket,const char ** e_text,const char * from,const struct sockaddr * from_addr,time_t ** csec,int ** cusec,AuthorizationData ** auth_data,krb5_keyblock ** replykey,int * rk_is_subkey)1140c19800e8SDoug Rabson tgs_parse_request(krb5_context context,
1141c19800e8SDoug Rabson 		  krb5_kdc_configuration *config,
1142c19800e8SDoug Rabson 		  KDC_REQ_BODY *b,
1143c19800e8SDoug Rabson 		  const PA_DATA *tgs_req,
1144c19800e8SDoug Rabson 		  hdb_entry_ex **krbtgt,
1145c19800e8SDoug Rabson 		  krb5_enctype *krbtgt_etype,
1146c19800e8SDoug Rabson 		  krb5_ticket **ticket,
1147c19800e8SDoug Rabson 		  const char **e_text,
1148c19800e8SDoug Rabson 		  const char *from,
1149c19800e8SDoug Rabson 		  const struct sockaddr *from_addr,
1150c19800e8SDoug Rabson 		  time_t **csec,
1151c19800e8SDoug Rabson 		  int **cusec,
1152ae771770SStanislav Sedov 		  AuthorizationData **auth_data,
1153ae771770SStanislav Sedov 		  krb5_keyblock **replykey,
1154ae771770SStanislav Sedov 		  int *rk_is_subkey)
1155c19800e8SDoug Rabson {
1156ae771770SStanislav Sedov     static char failed[] = "<unparse_name failed>";
1157c19800e8SDoug Rabson     krb5_ap_req ap_req;
1158c19800e8SDoug Rabson     krb5_error_code ret;
1159c19800e8SDoug Rabson     krb5_principal princ;
1160c19800e8SDoug Rabson     krb5_auth_context ac = NULL;
1161c19800e8SDoug Rabson     krb5_flags ap_req_options;
1162c19800e8SDoug Rabson     krb5_flags verify_ap_req_flags;
1163c19800e8SDoug Rabson     krb5_crypto crypto;
1164c19800e8SDoug Rabson     Key *tkey;
1165ae771770SStanislav Sedov     krb5_keyblock *subkey = NULL;
1166ae771770SStanislav Sedov     unsigned usage;
1167c19800e8SDoug Rabson 
1168c19800e8SDoug Rabson     *auth_data = NULL;
1169c19800e8SDoug Rabson     *csec  = NULL;
1170c19800e8SDoug Rabson     *cusec = NULL;
1171ae771770SStanislav Sedov     *replykey = NULL;
1172c19800e8SDoug Rabson 
1173c19800e8SDoug Rabson     memset(&ap_req, 0, sizeof(ap_req));
1174c19800e8SDoug Rabson     ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
1175c19800e8SDoug Rabson     if(ret){
1176ae771770SStanislav Sedov 	const char *msg = krb5_get_error_message(context, ret);
1177ae771770SStanislav Sedov 	kdc_log(context, config, 0, "Failed to decode AP-REQ: %s", msg);
1178ae771770SStanislav Sedov 	krb5_free_error_message(context, msg);
1179c19800e8SDoug Rabson 	goto out;
1180c19800e8SDoug Rabson     }
1181c19800e8SDoug Rabson 
1182c19800e8SDoug Rabson     if(!get_krbtgt_realm(&ap_req.ticket.sname)){
1183c19800e8SDoug Rabson 	/* XXX check for ticket.sname == req.sname */
1184c19800e8SDoug Rabson 	kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
1185c19800e8SDoug Rabson 	ret = KRB5KDC_ERR_POLICY; /* ? */
1186c19800e8SDoug Rabson 	goto out;
1187c19800e8SDoug Rabson     }
1188c19800e8SDoug Rabson 
1189c19800e8SDoug Rabson     _krb5_principalname2krb5_principal(context,
1190c19800e8SDoug Rabson 				       &princ,
1191c19800e8SDoug Rabson 				       ap_req.ticket.sname,
1192c19800e8SDoug Rabson 				       ap_req.ticket.realm);
1193c19800e8SDoug Rabson 
1194ae771770SStanislav Sedov     ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, ap_req.ticket.enc_part.kvno, NULL, krbtgt);
1195c19800e8SDoug Rabson 
1196ae771770SStanislav Sedov     if(ret == HDB_ERR_NOT_FOUND_HERE) {
1197c19800e8SDoug Rabson 	char *p;
1198c19800e8SDoug Rabson 	ret = krb5_unparse_name(context, princ, &p);
1199c19800e8SDoug Rabson 	if (ret != 0)
1200ae771770SStanislav Sedov 	    p = failed;
1201ae771770SStanislav Sedov 	krb5_free_principal(context, princ);
1202ae771770SStanislav Sedov 	kdc_log(context, config, 5, "Ticket-granting ticket account %s does not have secrets at this KDC, need to proxy", p);
1203ae771770SStanislav Sedov 	if (ret == 0)
1204ae771770SStanislav Sedov 	    free(p);
1205ae771770SStanislav Sedov 	ret = HDB_ERR_NOT_FOUND_HERE;
1206ae771770SStanislav Sedov 	goto out;
1207ae771770SStanislav Sedov     } else if(ret){
1208ae771770SStanislav Sedov 	const char *msg = krb5_get_error_message(context, ret);
1209ae771770SStanislav Sedov 	char *p;
1210ae771770SStanislav Sedov 	ret = krb5_unparse_name(context, princ, &p);
1211ae771770SStanislav Sedov 	if (ret != 0)
1212ae771770SStanislav Sedov 	    p = failed;
1213c19800e8SDoug Rabson 	krb5_free_principal(context, princ);
1214c19800e8SDoug Rabson 	kdc_log(context, config, 0,
1215ae771770SStanislav Sedov 		"Ticket-granting ticket not found in database: %s", msg);
1216ae771770SStanislav Sedov 	krb5_free_error_message(context, msg);
1217c19800e8SDoug Rabson 	if (ret == 0)
1218c19800e8SDoug Rabson 	    free(p);
1219c19800e8SDoug Rabson 	ret = KRB5KRB_AP_ERR_NOT_US;
1220c19800e8SDoug Rabson 	goto out;
1221c19800e8SDoug Rabson     }
1222c19800e8SDoug Rabson 
1223c19800e8SDoug Rabson     if(ap_req.ticket.enc_part.kvno &&
1224c19800e8SDoug Rabson        *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){
1225c19800e8SDoug Rabson 	char *p;
1226c19800e8SDoug Rabson 
1227c19800e8SDoug Rabson 	ret = krb5_unparse_name (context, princ, &p);
1228c19800e8SDoug Rabson 	krb5_free_principal(context, princ);
1229c19800e8SDoug Rabson 	if (ret != 0)
1230ae771770SStanislav Sedov 	    p = failed;
1231c19800e8SDoug Rabson 	kdc_log(context, config, 0,
1232c19800e8SDoug Rabson 		"Ticket kvno = %d, DB kvno = %d (%s)",
1233c19800e8SDoug Rabson 		*ap_req.ticket.enc_part.kvno,
1234c19800e8SDoug Rabson 		(*krbtgt)->entry.kvno,
1235c19800e8SDoug Rabson 		p);
1236c19800e8SDoug Rabson 	if (ret == 0)
1237c19800e8SDoug Rabson 	    free (p);
1238c19800e8SDoug Rabson 	ret = KRB5KRB_AP_ERR_BADKEYVER;
1239c19800e8SDoug Rabson 	goto out;
1240c19800e8SDoug Rabson     }
1241c19800e8SDoug Rabson 
1242c19800e8SDoug Rabson     *krbtgt_etype = ap_req.ticket.enc_part.etype;
1243c19800e8SDoug Rabson 
1244c19800e8SDoug Rabson     ret = hdb_enctype2key(context, &(*krbtgt)->entry,
1245c19800e8SDoug Rabson 			  ap_req.ticket.enc_part.etype, &tkey);
1246c19800e8SDoug Rabson     if(ret){
1247c19800e8SDoug Rabson 	char *str = NULL, *p = NULL;
1248c19800e8SDoug Rabson 
1249c19800e8SDoug Rabson 	krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
1250c19800e8SDoug Rabson 	krb5_unparse_name(context, princ, &p);
1251c19800e8SDoug Rabson  	kdc_log(context, config, 0,
1252c19800e8SDoug Rabson 		"No server key with enctype %s found for %s",
1253c19800e8SDoug Rabson 		str ? str : "<unknown enctype>",
1254c19800e8SDoug Rabson 		p ? p : "<unparse_name failed>");
1255c19800e8SDoug Rabson 	free(str);
1256c19800e8SDoug Rabson 	free(p);
1257c19800e8SDoug Rabson 	ret = KRB5KRB_AP_ERR_BADKEYVER;
1258c19800e8SDoug Rabson 	goto out;
1259c19800e8SDoug Rabson     }
1260c19800e8SDoug Rabson 
1261c19800e8SDoug Rabson     if (b->kdc_options.validate)
1262c19800e8SDoug Rabson 	verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
1263c19800e8SDoug Rabson     else
1264c19800e8SDoug Rabson 	verify_ap_req_flags = 0;
1265c19800e8SDoug Rabson 
1266c19800e8SDoug Rabson     ret = krb5_verify_ap_req2(context,
1267c19800e8SDoug Rabson 			      &ac,
1268c19800e8SDoug Rabson 			      &ap_req,
1269c19800e8SDoug Rabson 			      princ,
1270c19800e8SDoug Rabson 			      &tkey->key,
1271c19800e8SDoug Rabson 			      verify_ap_req_flags,
1272c19800e8SDoug Rabson 			      &ap_req_options,
1273c19800e8SDoug Rabson 			      ticket,
1274c19800e8SDoug Rabson 			      KRB5_KU_TGS_REQ_AUTH);
1275c19800e8SDoug Rabson 
1276c19800e8SDoug Rabson     krb5_free_principal(context, princ);
1277c19800e8SDoug Rabson     if(ret) {
1278ae771770SStanislav Sedov 	const char *msg = krb5_get_error_message(context, ret);
1279ae771770SStanislav Sedov 	kdc_log(context, config, 0, "Failed to verify AP-REQ: %s", msg);
1280ae771770SStanislav Sedov 	krb5_free_error_message(context, msg);
1281c19800e8SDoug Rabson 	goto out;
1282c19800e8SDoug Rabson     }
1283c19800e8SDoug Rabson 
1284c19800e8SDoug Rabson     {
1285c19800e8SDoug Rabson 	krb5_authenticator auth;
1286c19800e8SDoug Rabson 
1287c19800e8SDoug Rabson 	ret = krb5_auth_con_getauthenticator(context, ac, &auth);
1288c19800e8SDoug Rabson 	if (ret == 0) {
1289c19800e8SDoug Rabson 	    *csec   = malloc(sizeof(**csec));
1290c19800e8SDoug Rabson 	    if (*csec == NULL) {
1291c19800e8SDoug Rabson 		krb5_free_authenticator(context, &auth);
1292c19800e8SDoug Rabson 		kdc_log(context, config, 0, "malloc failed");
1293c19800e8SDoug Rabson 		goto out;
1294c19800e8SDoug Rabson 	    }
1295c19800e8SDoug Rabson 	    **csec  = auth->ctime;
1296c19800e8SDoug Rabson 	    *cusec  = malloc(sizeof(**cusec));
1297c19800e8SDoug Rabson 	    if (*cusec == NULL) {
1298c19800e8SDoug Rabson 		krb5_free_authenticator(context, &auth);
1299c19800e8SDoug Rabson 		kdc_log(context, config, 0, "malloc failed");
1300c19800e8SDoug Rabson 		goto out;
1301c19800e8SDoug Rabson 	    }
1302c19800e8SDoug Rabson 	    **cusec  = auth->cusec;
1303c19800e8SDoug Rabson 	    krb5_free_authenticator(context, &auth);
1304c19800e8SDoug Rabson 	}
1305c19800e8SDoug Rabson     }
1306c19800e8SDoug Rabson 
1307c19800e8SDoug Rabson     ret = tgs_check_authenticator(context, config,
1308c19800e8SDoug Rabson 				  ac, b, e_text, &(*ticket)->ticket.key);
1309c19800e8SDoug Rabson     if (ret) {
1310c19800e8SDoug Rabson 	krb5_auth_con_free(context, ac);
1311c19800e8SDoug Rabson 	goto out;
1312c19800e8SDoug Rabson     }
1313c19800e8SDoug Rabson 
1314ae771770SStanislav Sedov     usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
1315ae771770SStanislav Sedov     *rk_is_subkey = 1;
1316c19800e8SDoug Rabson 
1317ae771770SStanislav Sedov     ret = krb5_auth_con_getremotesubkey(context, ac, &subkey);
1318c19800e8SDoug Rabson     if(ret){
1319ae771770SStanislav Sedov 	const char *msg = krb5_get_error_message(context, ret);
1320c19800e8SDoug Rabson 	krb5_auth_con_free(context, ac);
1321ae771770SStanislav Sedov 	kdc_log(context, config, 0, "Failed to get remote subkey: %s", msg);
1322ae771770SStanislav Sedov 	krb5_free_error_message(context, msg);
1323c19800e8SDoug Rabson 	goto out;
1324c19800e8SDoug Rabson     }
1325c19800e8SDoug Rabson     if(subkey == NULL){
1326c19800e8SDoug Rabson 	usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
1327ae771770SStanislav Sedov 	*rk_is_subkey = 0;
1328ae771770SStanislav Sedov 
1329c19800e8SDoug Rabson 	ret = krb5_auth_con_getkey(context, ac, &subkey);
1330c19800e8SDoug Rabson 	if(ret) {
1331ae771770SStanislav Sedov 	    const char *msg = krb5_get_error_message(context, ret);
1332c19800e8SDoug Rabson 	    krb5_auth_con_free(context, ac);
1333ae771770SStanislav Sedov 	    kdc_log(context, config, 0, "Failed to get session key: %s", msg);
1334ae771770SStanislav Sedov 	    krb5_free_error_message(context, msg);
1335c19800e8SDoug Rabson 	    goto out;
1336c19800e8SDoug Rabson 	}
1337c19800e8SDoug Rabson     }
1338c19800e8SDoug Rabson     if(subkey == NULL){
1339c19800e8SDoug Rabson 	krb5_auth_con_free(context, ac);
1340c19800e8SDoug Rabson 	kdc_log(context, config, 0,
1341c19800e8SDoug Rabson 		"Failed to get key for enc-authorization-data");
1342c19800e8SDoug Rabson 	ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1343c19800e8SDoug Rabson 	goto out;
1344c19800e8SDoug Rabson     }
1345ae771770SStanislav Sedov 
1346ae771770SStanislav Sedov     *replykey = subkey;
1347ae771770SStanislav Sedov 
1348ae771770SStanislav Sedov     if (b->enc_authorization_data) {
1349ae771770SStanislav Sedov 	krb5_data ad;
1350ae771770SStanislav Sedov 
1351c19800e8SDoug Rabson 	ret = krb5_crypto_init(context, subkey, 0, &crypto);
1352c19800e8SDoug Rabson 	if (ret) {
1353ae771770SStanislav Sedov 	    const char *msg = krb5_get_error_message(context, ret);
1354c19800e8SDoug Rabson 	    krb5_auth_con_free(context, ac);
1355ae771770SStanislav Sedov 	    kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
1356ae771770SStanislav Sedov 	    krb5_free_error_message(context, msg);
1357c19800e8SDoug Rabson 	    goto out;
1358c19800e8SDoug Rabson 	}
1359c19800e8SDoug Rabson 	ret = krb5_decrypt_EncryptedData (context,
1360c19800e8SDoug Rabson 					  crypto,
1361c19800e8SDoug Rabson 					  usage,
1362c19800e8SDoug Rabson 					  b->enc_authorization_data,
1363c19800e8SDoug Rabson 					  &ad);
1364c19800e8SDoug Rabson 	krb5_crypto_destroy(context, crypto);
1365c19800e8SDoug Rabson 	if(ret){
1366c19800e8SDoug Rabson 	    krb5_auth_con_free(context, ac);
1367c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
1368c19800e8SDoug Rabson 		    "Failed to decrypt enc-authorization-data");
1369c19800e8SDoug Rabson 	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1370c19800e8SDoug Rabson 	    goto out;
1371c19800e8SDoug Rabson 	}
1372c19800e8SDoug Rabson 	ALLOC(*auth_data);
1373c19800e8SDoug Rabson 	if (*auth_data == NULL) {
1374c19800e8SDoug Rabson 	    krb5_auth_con_free(context, ac);
1375c19800e8SDoug Rabson 	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1376c19800e8SDoug Rabson 	    goto out;
1377c19800e8SDoug Rabson 	}
1378c19800e8SDoug Rabson 	ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
1379c19800e8SDoug Rabson 	if(ret){
1380c19800e8SDoug Rabson 	    krb5_auth_con_free(context, ac);
1381c19800e8SDoug Rabson 	    free(*auth_data);
1382c19800e8SDoug Rabson 	    *auth_data = NULL;
1383c19800e8SDoug Rabson 	    kdc_log(context, config, 0, "Failed to decode authorization data");
1384c19800e8SDoug Rabson 	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1385c19800e8SDoug Rabson 	    goto out;
1386c19800e8SDoug Rabson 	}
1387c19800e8SDoug Rabson     }
1388c19800e8SDoug Rabson 
1389c19800e8SDoug Rabson     krb5_auth_con_free(context, ac);
1390c19800e8SDoug Rabson 
1391c19800e8SDoug Rabson out:
1392c19800e8SDoug Rabson     free_AP_REQ(&ap_req);
1393c19800e8SDoug Rabson 
1394c19800e8SDoug Rabson     return ret;
1395c19800e8SDoug Rabson }
1396c19800e8SDoug Rabson 
1397c19800e8SDoug Rabson static krb5_error_code
build_server_referral(krb5_context context,krb5_kdc_configuration * config,krb5_crypto session,krb5_const_realm referred_realm,const PrincipalName * true_principal_name,const PrincipalName * requested_principal,krb5_data * outdata)1398ae771770SStanislav Sedov build_server_referral(krb5_context context,
1399ae771770SStanislav Sedov 		      krb5_kdc_configuration *config,
1400ae771770SStanislav Sedov 		      krb5_crypto session,
1401ae771770SStanislav Sedov 		      krb5_const_realm referred_realm,
1402ae771770SStanislav Sedov 		      const PrincipalName *true_principal_name,
1403ae771770SStanislav Sedov 		      const PrincipalName *requested_principal,
1404ae771770SStanislav Sedov 		      krb5_data *outdata)
1405ae771770SStanislav Sedov {
1406ae771770SStanislav Sedov     PA_ServerReferralData ref;
1407ae771770SStanislav Sedov     krb5_error_code ret;
1408ae771770SStanislav Sedov     EncryptedData ed;
1409ae771770SStanislav Sedov     krb5_data data;
1410ae771770SStanislav Sedov     size_t size = 0;
1411ae771770SStanislav Sedov 
1412ae771770SStanislav Sedov     memset(&ref, 0, sizeof(ref));
1413ae771770SStanislav Sedov 
1414ae771770SStanislav Sedov     if (referred_realm) {
1415ae771770SStanislav Sedov 	ALLOC(ref.referred_realm);
1416ae771770SStanislav Sedov 	if (ref.referred_realm == NULL)
1417ae771770SStanislav Sedov 	    goto eout;
1418ae771770SStanislav Sedov 	*ref.referred_realm = strdup(referred_realm);
1419ae771770SStanislav Sedov 	if (*ref.referred_realm == NULL)
1420ae771770SStanislav Sedov 	    goto eout;
1421ae771770SStanislav Sedov     }
1422ae771770SStanislav Sedov     if (true_principal_name) {
1423ae771770SStanislav Sedov 	ALLOC(ref.true_principal_name);
1424ae771770SStanislav Sedov 	if (ref.true_principal_name == NULL)
1425ae771770SStanislav Sedov 	    goto eout;
1426ae771770SStanislav Sedov 	ret = copy_PrincipalName(true_principal_name, ref.true_principal_name);
1427ae771770SStanislav Sedov 	if (ret)
1428ae771770SStanislav Sedov 	    goto eout;
1429ae771770SStanislav Sedov     }
1430ae771770SStanislav Sedov     if (requested_principal) {
1431ae771770SStanislav Sedov 	ALLOC(ref.requested_principal_name);
1432ae771770SStanislav Sedov 	if (ref.requested_principal_name == NULL)
1433ae771770SStanislav Sedov 	    goto eout;
1434ae771770SStanislav Sedov 	ret = copy_PrincipalName(requested_principal,
1435ae771770SStanislav Sedov 				 ref.requested_principal_name);
1436ae771770SStanislav Sedov 	if (ret)
1437ae771770SStanislav Sedov 	    goto eout;
1438ae771770SStanislav Sedov     }
1439ae771770SStanislav Sedov 
1440ae771770SStanislav Sedov     ASN1_MALLOC_ENCODE(PA_ServerReferralData,
1441ae771770SStanislav Sedov 		       data.data, data.length,
1442ae771770SStanislav Sedov 		       &ref, &size, ret);
1443ae771770SStanislav Sedov     free_PA_ServerReferralData(&ref);
1444ae771770SStanislav Sedov     if (ret)
1445ae771770SStanislav Sedov 	return ret;
1446ae771770SStanislav Sedov     if (data.length != size)
1447ae771770SStanislav Sedov 	krb5_abortx(context, "internal asn.1 encoder error");
1448ae771770SStanislav Sedov 
1449ae771770SStanislav Sedov     ret = krb5_encrypt_EncryptedData(context, session,
1450ae771770SStanislav Sedov 				     KRB5_KU_PA_SERVER_REFERRAL,
1451ae771770SStanislav Sedov 				     data.data, data.length,
1452ae771770SStanislav Sedov 				     0 /* kvno */, &ed);
1453ae771770SStanislav Sedov     free(data.data);
1454ae771770SStanislav Sedov     if (ret)
1455ae771770SStanislav Sedov 	return ret;
1456ae771770SStanislav Sedov 
1457ae771770SStanislav Sedov     ASN1_MALLOC_ENCODE(EncryptedData,
1458ae771770SStanislav Sedov 		       outdata->data, outdata->length,
1459ae771770SStanislav Sedov 		       &ed, &size, ret);
1460ae771770SStanislav Sedov     free_EncryptedData(&ed);
1461ae771770SStanislav Sedov     if (ret)
1462ae771770SStanislav Sedov 	return ret;
1463ae771770SStanislav Sedov     if (outdata->length != size)
1464ae771770SStanislav Sedov 	krb5_abortx(context, "internal asn.1 encoder error");
1465ae771770SStanislav Sedov 
1466ae771770SStanislav Sedov     return 0;
1467ae771770SStanislav Sedov eout:
1468ae771770SStanislav Sedov     free_PA_ServerReferralData(&ref);
1469ae771770SStanislav Sedov     krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1470ae771770SStanislav Sedov     return ENOMEM;
1471ae771770SStanislav Sedov }
1472ae771770SStanislav Sedov 
1473ae771770SStanislav Sedov static krb5_error_code
tgs_build_reply(krb5_context context,krb5_kdc_configuration * config,KDC_REQ * req,KDC_REQ_BODY * b,hdb_entry_ex * krbtgt,krb5_enctype krbtgt_etype,const krb5_keyblock * replykey,int rk_is_subkey,krb5_ticket * ticket,krb5_data * reply,const char * from,const char ** e_text,AuthorizationData ** auth_data,const struct sockaddr * from_addr)1474c19800e8SDoug Rabson tgs_build_reply(krb5_context context,
1475c19800e8SDoug Rabson 		krb5_kdc_configuration *config,
1476c19800e8SDoug Rabson 		KDC_REQ *req,
1477c19800e8SDoug Rabson 		KDC_REQ_BODY *b,
1478c19800e8SDoug Rabson 		hdb_entry_ex *krbtgt,
1479c19800e8SDoug Rabson 		krb5_enctype krbtgt_etype,
1480ae771770SStanislav Sedov 		const krb5_keyblock *replykey,
1481ae771770SStanislav Sedov 		int rk_is_subkey,
1482c19800e8SDoug Rabson 		krb5_ticket *ticket,
1483c19800e8SDoug Rabson 		krb5_data *reply,
1484c19800e8SDoug Rabson 		const char *from,
1485c19800e8SDoug Rabson 		const char **e_text,
1486ae771770SStanislav Sedov 		AuthorizationData **auth_data,
1487ae771770SStanislav Sedov 		const struct sockaddr *from_addr)
1488c19800e8SDoug Rabson {
1489c19800e8SDoug Rabson     krb5_error_code ret;
1490ae771770SStanislav Sedov     krb5_principal cp = NULL, sp = NULL, rsp = NULL, tp = NULL, dp = NULL;
1491ae771770SStanislav Sedov     krb5_principal krbtgt_principal = NULL;
1492ae771770SStanislav Sedov     char *spn = NULL, *cpn = NULL, *tpn = NULL, *dpn = NULL;
1493ae771770SStanislav Sedov     hdb_entry_ex *server = NULL, *client = NULL, *s4u2self_impersonated_client = NULL;
1494ae771770SStanislav Sedov     HDB *clientdb, *s4u2self_impersonated_clientdb;
1495ae771770SStanislav Sedov     krb5_realm ref_realm = NULL;
1496c19800e8SDoug Rabson     EncTicketPart *tgt = &ticket->ticket;
1497ae771770SStanislav Sedov     krb5_principals spp = NULL;
1498c19800e8SDoug Rabson     const EncryptionKey *ekey;
1499c19800e8SDoug Rabson     krb5_keyblock sessionkey;
1500c19800e8SDoug Rabson     krb5_kvno kvno;
1501c19800e8SDoug Rabson     krb5_data rspac;
1502f8041e36SCy Schubert     const char *tgt_realm = /* Realm of TGT issuer */
1503f8041e36SCy Schubert         krb5_principal_get_realm(context, krbtgt->entry.principal);
1504ae771770SStanislav Sedov 
1505ae771770SStanislav Sedov     hdb_entry_ex *krbtgt_out = NULL;
1506ae771770SStanislav Sedov 
1507ae771770SStanislav Sedov     METHOD_DATA enc_pa_data;
1508c19800e8SDoug Rabson 
1509c19800e8SDoug Rabson     PrincipalName *s;
1510c19800e8SDoug Rabson     Realm r;
1511c19800e8SDoug Rabson     int nloop = 0;
1512c19800e8SDoug Rabson     EncTicketPart adtkt;
1513c19800e8SDoug Rabson     char opt_str[128];
1514ae771770SStanislav Sedov     int signedpath = 0;
1515ae771770SStanislav Sedov 
1516ae771770SStanislav Sedov     Key *tkey_check;
1517ae771770SStanislav Sedov     Key *tkey_sign;
1518ae771770SStanislav Sedov     int flags = HDB_F_FOR_TGS_REQ;
1519c19800e8SDoug Rabson 
1520c19800e8SDoug Rabson     memset(&sessionkey, 0, sizeof(sessionkey));
1521c19800e8SDoug Rabson     memset(&adtkt, 0, sizeof(adtkt));
1522c19800e8SDoug Rabson     krb5_data_zero(&rspac);
1523ae771770SStanislav Sedov     memset(&enc_pa_data, 0, sizeof(enc_pa_data));
1524c19800e8SDoug Rabson 
1525c19800e8SDoug Rabson     s = b->sname;
1526c19800e8SDoug Rabson     r = b->realm;
1527c19800e8SDoug Rabson 
1528ae771770SStanislav Sedov     /*
1529ae771770SStanislav Sedov      * Always to do CANON, see comment below about returned server principal (rsp).
1530ae771770SStanislav Sedov      */
1531ae771770SStanislav Sedov     flags |= HDB_F_CANON;
1532ae771770SStanislav Sedov 
1533c19800e8SDoug Rabson     if(b->kdc_options.enc_tkt_in_skey){
1534c19800e8SDoug Rabson 	Ticket *t;
1535c19800e8SDoug Rabson 	hdb_entry_ex *uu;
1536c19800e8SDoug Rabson 	krb5_principal p;
1537c19800e8SDoug Rabson 	Key *uukey;
1538c19800e8SDoug Rabson 
1539c19800e8SDoug Rabson 	if(b->additional_tickets == NULL ||
1540c19800e8SDoug Rabson 	   b->additional_tickets->len == 0){
1541c19800e8SDoug Rabson 	    ret = KRB5KDC_ERR_BADOPTION; /* ? */
1542c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
1543c19800e8SDoug Rabson 		    "No second ticket present in request");
1544c19800e8SDoug Rabson 	    goto out;
1545c19800e8SDoug Rabson 	}
1546c19800e8SDoug Rabson 	t = &b->additional_tickets->val[0];
1547c19800e8SDoug Rabson 	if(!get_krbtgt_realm(&t->sname)){
1548c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
1549c19800e8SDoug Rabson 		    "Additional ticket is not a ticket-granting ticket");
1550c19800e8SDoug Rabson 	    ret = KRB5KDC_ERR_POLICY;
1551c19800e8SDoug Rabson 	    goto out;
1552c19800e8SDoug Rabson 	}
1553c19800e8SDoug Rabson 	_krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
1554c19800e8SDoug Rabson 	ret = _kdc_db_fetch(context, config, p,
1555ae771770SStanislav Sedov 			    HDB_F_GET_KRBTGT, t->enc_part.kvno,
1556c19800e8SDoug Rabson 			    NULL, &uu);
1557c19800e8SDoug Rabson 	krb5_free_principal(context, p);
1558c19800e8SDoug Rabson 	if(ret){
1559c19800e8SDoug Rabson 	    if (ret == HDB_ERR_NOENTRY)
1560c19800e8SDoug Rabson 		ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1561c19800e8SDoug Rabson 	    goto out;
1562c19800e8SDoug Rabson 	}
1563c19800e8SDoug Rabson 	ret = hdb_enctype2key(context, &uu->entry,
1564c19800e8SDoug Rabson 			      t->enc_part.etype, &uukey);
1565c19800e8SDoug Rabson 	if(ret){
1566c19800e8SDoug Rabson 	    _kdc_free_ent(context, uu);
1567c19800e8SDoug Rabson 	    ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1568c19800e8SDoug Rabson 	    goto out;
1569c19800e8SDoug Rabson 	}
1570c19800e8SDoug Rabson 	ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
1571c19800e8SDoug Rabson 	_kdc_free_ent(context, uu);
1572c19800e8SDoug Rabson 	if(ret)
1573c19800e8SDoug Rabson 	    goto out;
1574c19800e8SDoug Rabson 
1575c19800e8SDoug Rabson 	ret = verify_flags(context, config, &adtkt, spn);
1576c19800e8SDoug Rabson 	if (ret)
1577c19800e8SDoug Rabson 	    goto out;
1578c19800e8SDoug Rabson 
1579c19800e8SDoug Rabson 	s = &adtkt.cname;
1580c19800e8SDoug Rabson 	r = adtkt.crealm;
1581c19800e8SDoug Rabson     }
1582c19800e8SDoug Rabson 
1583c19800e8SDoug Rabson     _krb5_principalname2krb5_principal(context, &sp, *s, r);
1584c19800e8SDoug Rabson     ret = krb5_unparse_name(context, sp, &spn);
1585c19800e8SDoug Rabson     if (ret)
1586c19800e8SDoug Rabson 	goto out;
1587c19800e8SDoug Rabson     _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
1588c19800e8SDoug Rabson     ret = krb5_unparse_name(context, cp, &cpn);
1589c19800e8SDoug Rabson     if (ret)
1590c19800e8SDoug Rabson 	goto out;
1591c19800e8SDoug Rabson     unparse_flags (KDCOptions2int(b->kdc_options),
1592c19800e8SDoug Rabson 		   asn1_KDCOptions_units(),
1593c19800e8SDoug Rabson 		   opt_str, sizeof(opt_str));
1594c19800e8SDoug Rabson     if(*opt_str)
1595c19800e8SDoug Rabson 	kdc_log(context, config, 0,
1596c19800e8SDoug Rabson 		"TGS-REQ %s from %s for %s [%s]",
1597c19800e8SDoug Rabson 		cpn, from, spn, opt_str);
1598c19800e8SDoug Rabson     else
1599c19800e8SDoug Rabson 	kdc_log(context, config, 0,
1600c19800e8SDoug Rabson 		"TGS-REQ %s from %s for %s", cpn, from, spn);
1601c19800e8SDoug Rabson 
1602c19800e8SDoug Rabson     /*
1603c19800e8SDoug Rabson      * Fetch server
1604c19800e8SDoug Rabson      */
1605c19800e8SDoug Rabson 
1606c19800e8SDoug Rabson server_lookup:
1607ae771770SStanislav Sedov     ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER | flags,
1608ae771770SStanislav Sedov 			NULL, NULL, &server);
1609c19800e8SDoug Rabson 
1610ae771770SStanislav Sedov     if(ret == HDB_ERR_NOT_FOUND_HERE) {
1611ae771770SStanislav Sedov 	kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", sp);
1612ae771770SStanislav Sedov 	goto out;
1613ae771770SStanislav Sedov     } else if(ret){
1614ae771770SStanislav Sedov 	const char *new_rlm, *msg;
1615c19800e8SDoug Rabson 	Realm req_rlm;
1616c19800e8SDoug Rabson 	krb5_realm *realms;
1617c19800e8SDoug Rabson 
1618c19800e8SDoug Rabson 	if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
1619c19800e8SDoug Rabson 	    if(nloop++ < 2) {
1620c19800e8SDoug Rabson 		new_rlm = find_rpath(context, tgt->crealm, req_rlm);
1621c19800e8SDoug Rabson 		if(new_rlm) {
1622c19800e8SDoug Rabson 		    kdc_log(context, config, 5, "krbtgt for realm %s "
1623c19800e8SDoug Rabson 			    "not found, trying %s",
1624c19800e8SDoug Rabson 			    req_rlm, new_rlm);
1625c19800e8SDoug Rabson 		    krb5_free_principal(context, sp);
1626c19800e8SDoug Rabson 		    free(spn);
1627c19800e8SDoug Rabson 		    krb5_make_principal(context, &sp, r,
1628c19800e8SDoug Rabson 					KRB5_TGS_NAME, new_rlm, NULL);
1629c19800e8SDoug Rabson 		    ret = krb5_unparse_name(context, sp, &spn);
1630c19800e8SDoug Rabson 		    if (ret)
1631c19800e8SDoug Rabson 			goto out;
1632ae771770SStanislav Sedov 
1633ae771770SStanislav Sedov 		    if (ref_realm)
1634ae771770SStanislav Sedov 			free(ref_realm);
1635ae771770SStanislav Sedov 		    ref_realm = strdup(new_rlm);
1636c19800e8SDoug Rabson 		    goto server_lookup;
1637c19800e8SDoug Rabson 		}
1638c19800e8SDoug Rabson 	    }
1639ae771770SStanislav Sedov 	} else if(need_referral(context, config, &b->kdc_options, sp, &realms)) {
1640c19800e8SDoug Rabson 	    if (strcmp(realms[0], sp->realm) != 0) {
1641c19800e8SDoug Rabson 		kdc_log(context, config, 5,
1642c19800e8SDoug Rabson 			"Returning a referral to realm %s for "
1643c19800e8SDoug Rabson 			"server %s that was not found",
1644c19800e8SDoug Rabson 			realms[0], spn);
1645c19800e8SDoug Rabson 		krb5_free_principal(context, sp);
1646c19800e8SDoug Rabson 		free(spn);
1647c19800e8SDoug Rabson 		krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1648c19800e8SDoug Rabson 				    realms[0], NULL);
1649c19800e8SDoug Rabson 		ret = krb5_unparse_name(context, sp, &spn);
1650c19800e8SDoug Rabson 		if (ret)
1651c19800e8SDoug Rabson 		    goto out;
1652ae771770SStanislav Sedov 
1653ae771770SStanislav Sedov 		if (ref_realm)
1654ae771770SStanislav Sedov 		    free(ref_realm);
1655ae771770SStanislav Sedov 		ref_realm = strdup(realms[0]);
1656ae771770SStanislav Sedov 
1657c19800e8SDoug Rabson 		krb5_free_host_realm(context, realms);
1658c19800e8SDoug Rabson 		goto server_lookup;
1659c19800e8SDoug Rabson 	    }
1660c19800e8SDoug Rabson 	    krb5_free_host_realm(context, realms);
1661c19800e8SDoug Rabson 	}
1662ae771770SStanislav Sedov 	msg = krb5_get_error_message(context, ret);
1663c19800e8SDoug Rabson 	kdc_log(context, config, 0,
1664ae771770SStanislav Sedov 		"Server not found in database: %s: %s", spn, msg);
1665ae771770SStanislav Sedov 	krb5_free_error_message(context, msg);
1666c19800e8SDoug Rabson 	if (ret == HDB_ERR_NOENTRY)
1667c19800e8SDoug Rabson 	    ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1668c19800e8SDoug Rabson 	goto out;
1669c19800e8SDoug Rabson     }
1670c19800e8SDoug Rabson 
1671ae771770SStanislav Sedov     /* the name returned to the client depend on what was asked for,
1672ae771770SStanislav Sedov      * return canonical name if kdc_options.canonicalize was set, the
1673ae771770SStanislav Sedov      * client wants the true name of the principal, if not it just
1674ae771770SStanislav Sedov      * wants the name its asked for.
1675ae771770SStanislav Sedov      */
1676ae771770SStanislav Sedov 
1677ae771770SStanislav Sedov     if (b->kdc_options.canonicalize)
1678ae771770SStanislav Sedov 	rsp = server->entry.principal;
1679ae771770SStanislav Sedov     else
1680ae771770SStanislav Sedov 	rsp = sp;
1681ae771770SStanislav Sedov 
1682ae771770SStanislav Sedov 
1683ae771770SStanislav Sedov     /*
1684ae771770SStanislav Sedov      * Select enctype, return key and kvno.
1685ae771770SStanislav Sedov      */
1686ae771770SStanislav Sedov 
1687ae771770SStanislav Sedov     {
1688ae771770SStanislav Sedov 	krb5_enctype etype;
1689ae771770SStanislav Sedov 
1690ae771770SStanislav Sedov 	if(b->kdc_options.enc_tkt_in_skey) {
1691ae771770SStanislav Sedov 	    size_t i;
1692ae771770SStanislav Sedov 	    ekey = &adtkt.key;
1693ae771770SStanislav Sedov 	    for(i = 0; i < b->etype.len; i++)
1694ae771770SStanislav Sedov 		if (b->etype.val[i] == adtkt.key.keytype)
1695ae771770SStanislav Sedov 		    break;
1696ae771770SStanislav Sedov 	    if(i == b->etype.len) {
1697ae771770SStanislav Sedov 		kdc_log(context, config, 0,
1698ae771770SStanislav Sedov 			"Addition ticket have not matching etypes");
1699ae771770SStanislav Sedov 		krb5_clear_error_message(context);
1700ae771770SStanislav Sedov 		ret = KRB5KDC_ERR_ETYPE_NOSUPP;
1701ae771770SStanislav Sedov 		goto out;
1702ae771770SStanislav Sedov 	    }
1703ae771770SStanislav Sedov 	    etype = b->etype.val[i];
1704ae771770SStanislav Sedov 	    kvno = 0;
1705ae771770SStanislav Sedov 	} else {
1706ae771770SStanislav Sedov 	    Key *skey;
1707ae771770SStanislav Sedov 
1708ae771770SStanislav Sedov 	    ret = _kdc_find_etype(context,
1709cf771f22SStanislav Sedov 				  krb5_principal_is_krbtgt(context, sp) ?
1710cf771f22SStanislav Sedov 				  config->tgt_use_strongest_session_key :
1711cf771f22SStanislav Sedov 				  config->svc_use_strongest_session_key, FALSE,
1712ae771770SStanislav Sedov 				  server, b->etype.val, b->etype.len, NULL,
1713ae771770SStanislav Sedov 				  &skey);
1714c19800e8SDoug Rabson 	    if(ret) {
1715ae771770SStanislav Sedov 		kdc_log(context, config, 0,
1716ae771770SStanislav Sedov 			"Server (%s) has no support for etypes", spn);
1717ae771770SStanislav Sedov 		goto out;
1718ae771770SStanislav Sedov 	    }
1719ae771770SStanislav Sedov 	    ekey = &skey->key;
1720ae771770SStanislav Sedov 	    etype = skey->key.keytype;
1721ae771770SStanislav Sedov 	    kvno = server->entry.kvno;
1722ae771770SStanislav Sedov 	}
1723ae771770SStanislav Sedov 
1724ae771770SStanislav Sedov 	ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
1725ae771770SStanislav Sedov 	if (ret)
1726ae771770SStanislav Sedov 	    goto out;
1727ae771770SStanislav Sedov     }
1728ae771770SStanislav Sedov 
1729ae771770SStanislav Sedov     /*
1730ae771770SStanislav Sedov      * Check that service is in the same realm as the krbtgt. If it's
1731ae771770SStanislav Sedov      * not the same, it's someone that is using a uni-directional trust
1732ae771770SStanislav Sedov      * backward.
1733ae771770SStanislav Sedov      */
1734ae771770SStanislav Sedov 
1735ae771770SStanislav Sedov     /*
1736ae771770SStanislav Sedov      * Validate authoriation data
1737ae771770SStanislav Sedov      */
1738ae771770SStanislav Sedov 
1739ae771770SStanislav Sedov     ret = hdb_enctype2key(context, &krbtgt->entry,
1740ae771770SStanislav Sedov 			  krbtgt_etype, &tkey_check);
1741ae771770SStanislav Sedov     if(ret) {
1742ae771770SStanislav Sedov 	kdc_log(context, config, 0,
1743ae771770SStanislav Sedov 		    "Failed to find key for krbtgt PAC check");
1744ae771770SStanislav Sedov 	goto out;
1745ae771770SStanislav Sedov     }
1746ae771770SStanislav Sedov 
1747ae771770SStanislav Sedov     /* Now refetch the primary krbtgt, and get the current kvno (the
1748ae771770SStanislav Sedov      * sign check may have been on an old kvno, and the server may
1749ae771770SStanislav Sedov      * have been an incoming trust) */
1750ae771770SStanislav Sedov     ret = krb5_make_principal(context, &krbtgt_principal,
1751ae771770SStanislav Sedov 			      krb5_principal_get_comp_string(context,
1752ae771770SStanislav Sedov 							     krbtgt->entry.principal,
1753ae771770SStanislav Sedov 							     1),
1754ae771770SStanislav Sedov 			      KRB5_TGS_NAME,
1755ae771770SStanislav Sedov 			      krb5_principal_get_comp_string(context,
1756ae771770SStanislav Sedov 							     krbtgt->entry.principal,
1757ae771770SStanislav Sedov 							     1), NULL);
1758ae771770SStanislav Sedov     if(ret) {
1759ae771770SStanislav Sedov 	kdc_log(context, config, 0,
1760ae771770SStanislav Sedov 		    "Failed to generate krbtgt principal");
1761ae771770SStanislav Sedov 	goto out;
1762ae771770SStanislav Sedov     }
1763ae771770SStanislav Sedov 
1764ae771770SStanislav Sedov     ret = _kdc_db_fetch(context, config, krbtgt_principal, HDB_F_GET_KRBTGT, NULL, NULL, &krbtgt_out);
1765ae771770SStanislav Sedov     krb5_free_principal(context, krbtgt_principal);
1766ae771770SStanislav Sedov     if (ret) {
1767ae771770SStanislav Sedov 	krb5_error_code ret2;
1768ae771770SStanislav Sedov 	char *ktpn, *ktpn2;
1769ae771770SStanislav Sedov 	ret = krb5_unparse_name(context, krbtgt->entry.principal, &ktpn);
1770ae771770SStanislav Sedov 	ret2 = krb5_unparse_name(context, krbtgt_principal, &ktpn2);
1771ae771770SStanislav Sedov 	kdc_log(context, config, 0,
1772ae771770SStanislav Sedov 		"Request with wrong krbtgt: %s, %s not found in our database",
1773ae771770SStanislav Sedov 		(ret == 0) ? ktpn : "<unknown>", (ret2 == 0) ? ktpn2 : "<unknown>");
1774ae771770SStanislav Sedov 	if(ret == 0)
1775ae771770SStanislav Sedov 	    free(ktpn);
1776ae771770SStanislav Sedov 	if(ret2 == 0)
1777ae771770SStanislav Sedov 	    free(ktpn2);
1778ae771770SStanislav Sedov 	ret = KRB5KRB_AP_ERR_NOT_US;
1779ae771770SStanislav Sedov 	goto out;
1780ae771770SStanislav Sedov     }
1781ae771770SStanislav Sedov 
1782ae771770SStanislav Sedov     /* The first realm is the realm of the service, the second is
1783ae771770SStanislav Sedov      * krbtgt/<this>/@REALM component of the krbtgt DN the request was
1784ae771770SStanislav Sedov      * encrypted to.  The redirection via the krbtgt_out entry allows
1785ae771770SStanislav Sedov      * the DB to possibly correct the case of the realm (Samba4 does
1786ae771770SStanislav Sedov      * this) before the strcmp() */
1787ae771770SStanislav Sedov     if (strcmp(krb5_principal_get_realm(context, server->entry.principal),
1788ae771770SStanislav Sedov 	       krb5_principal_get_realm(context, krbtgt_out->entry.principal)) != 0) {
1789ae771770SStanislav Sedov 	char *ktpn;
1790ae771770SStanislav Sedov 	ret = krb5_unparse_name(context, krbtgt_out->entry.principal, &ktpn);
1791ae771770SStanislav Sedov 	kdc_log(context, config, 0,
1792ae771770SStanislav Sedov 		"Request with wrong krbtgt: %s",
1793ae771770SStanislav Sedov 		(ret == 0) ? ktpn : "<unknown>");
1794ae771770SStanislav Sedov 	if(ret == 0)
1795ae771770SStanislav Sedov 	    free(ktpn);
1796ae771770SStanislav Sedov 	ret = KRB5KRB_AP_ERR_NOT_US;
1797ae771770SStanislav Sedov     }
1798ae771770SStanislav Sedov 
1799ae771770SStanislav Sedov     ret = hdb_enctype2key(context, &krbtgt_out->entry,
1800ae771770SStanislav Sedov 			  krbtgt_etype, &tkey_sign);
1801ae771770SStanislav Sedov     if(ret) {
1802ae771770SStanislav Sedov 	kdc_log(context, config, 0,
1803ae771770SStanislav Sedov 		    "Failed to find key for krbtgt PAC signature");
1804ae771770SStanislav Sedov 	goto out;
1805ae771770SStanislav Sedov     }
1806ae771770SStanislav Sedov 
1807ae771770SStanislav Sedov     ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | flags,
1808ae771770SStanislav Sedov 			NULL, &clientdb, &client);
1809ae771770SStanislav Sedov     if(ret == HDB_ERR_NOT_FOUND_HERE) {
1810ae771770SStanislav Sedov 	/* This is OK, we are just trying to find out if they have
1811ae771770SStanislav Sedov 	 * been disabled or deleted in the meantime, missing secrets
1812ae771770SStanislav Sedov 	 * is OK */
1813ae771770SStanislav Sedov     } else if(ret){
1814ae771770SStanislav Sedov 	const char *krbtgt_realm, *msg;
1815c19800e8SDoug Rabson 
1816c19800e8SDoug Rabson 	/*
1817c19800e8SDoug Rabson 	 * If the client belongs to the same realm as our krbtgt, it
1818c19800e8SDoug Rabson 	 * should exist in the local database.
1819c19800e8SDoug Rabson 	 *
1820c19800e8SDoug Rabson 	 */
1821c19800e8SDoug Rabson 
1822ae771770SStanislav Sedov 	krbtgt_realm = krb5_principal_get_realm(context, krbtgt_out->entry.principal);
1823c19800e8SDoug Rabson 
1824c19800e8SDoug Rabson 	if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
1825c19800e8SDoug Rabson 	    if (ret == HDB_ERR_NOENTRY)
1826c19800e8SDoug Rabson 		ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1827c19800e8SDoug Rabson 	    kdc_log(context, config, 1, "Client no longer in database: %s",
1828c19800e8SDoug Rabson 		    cpn);
1829c19800e8SDoug Rabson 	    goto out;
1830c19800e8SDoug Rabson 	}
1831c19800e8SDoug Rabson 
1832ae771770SStanislav Sedov 	msg = krb5_get_error_message(context, ret);
1833ae771770SStanislav Sedov 	kdc_log(context, config, 1, "Client not found in database: %s", msg);
1834ae771770SStanislav Sedov 	krb5_free_error_message(context, msg);
1835c19800e8SDoug Rabson     }
1836c19800e8SDoug Rabson 
1837ae771770SStanislav Sedov     ret = check_PAC(context, config, cp, NULL,
1838ae771770SStanislav Sedov 		    client, server, krbtgt,
1839ae771770SStanislav Sedov 		    &tkey_check->key, &tkey_check->key,
1840ae771770SStanislav Sedov 		    ekey, &tkey_sign->key,
1841ae771770SStanislav Sedov 		    tgt, &rspac, &signedpath);
1842ae771770SStanislav Sedov     if (ret) {
1843ae771770SStanislav Sedov 	const char *msg = krb5_get_error_message(context, ret);
1844c19800e8SDoug Rabson 	kdc_log(context, config, 0,
1845ae771770SStanislav Sedov 		"Verify PAC failed for %s (%s) from %s with %s",
1846ae771770SStanislav Sedov 		spn, cpn, from, msg);
1847ae771770SStanislav Sedov 	krb5_free_error_message(context, msg);
1848ae771770SStanislav Sedov 	goto out;
1849ae771770SStanislav Sedov     }
1850ae771770SStanislav Sedov 
1851ae771770SStanislav Sedov     /* also check the krbtgt for signature */
1852ae771770SStanislav Sedov     ret = check_KRB5SignedPath(context,
1853ae771770SStanislav Sedov 			       config,
1854ae771770SStanislav Sedov 			       krbtgt,
1855ae771770SStanislav Sedov 			       cp,
1856ae771770SStanislav Sedov 			       tgt,
1857ae771770SStanislav Sedov 			       &spp,
1858ae771770SStanislav Sedov 			       &signedpath);
1859ae771770SStanislav Sedov     if (ret) {
1860ae771770SStanislav Sedov 	const char *msg = krb5_get_error_message(context, ret);
1861ae771770SStanislav Sedov 	kdc_log(context, config, 0,
1862ae771770SStanislav Sedov 		"KRB5SignedPath check failed for %s (%s) from %s with %s",
1863ae771770SStanislav Sedov 		spn, cpn, from, msg);
1864ae771770SStanislav Sedov 	krb5_free_error_message(context, msg);
1865c19800e8SDoug Rabson 	goto out;
1866c19800e8SDoug Rabson     }
1867c19800e8SDoug Rabson 
1868c19800e8SDoug Rabson     /*
1869ae771770SStanislav Sedov      * Process request
1870c19800e8SDoug Rabson      */
1871c19800e8SDoug Rabson 
1872ae771770SStanislav Sedov     /* by default the tgt principal matches the client principal */
1873ae771770SStanislav Sedov     tp = cp;
1874ae771770SStanislav Sedov     tpn = cpn;
1875c19800e8SDoug Rabson 
1876c19800e8SDoug Rabson     if (client) {
1877c19800e8SDoug Rabson 	const PA_DATA *sdata;
1878c19800e8SDoug Rabson 	int i = 0;
1879c19800e8SDoug Rabson 
1880ae771770SStanislav Sedov 	sdata = _kdc_find_padata(req, &i, KRB5_PADATA_FOR_USER);
1881c19800e8SDoug Rabson 	if (sdata) {
1882c19800e8SDoug Rabson 	    krb5_crypto crypto;
1883c19800e8SDoug Rabson 	    krb5_data datack;
1884c19800e8SDoug Rabson 	    PA_S4U2Self self;
1885c19800e8SDoug Rabson 	    const char *str;
1886c19800e8SDoug Rabson 
1887c19800e8SDoug Rabson 	    ret = decode_PA_S4U2Self(sdata->padata_value.data,
1888c19800e8SDoug Rabson 				     sdata->padata_value.length,
1889c19800e8SDoug Rabson 				     &self, NULL);
1890c19800e8SDoug Rabson 	    if (ret) {
1891c19800e8SDoug Rabson 		kdc_log(context, config, 0, "Failed to decode PA-S4U2Self");
1892c19800e8SDoug Rabson 		goto out;
1893c19800e8SDoug Rabson 	    }
1894c19800e8SDoug Rabson 
189524339377SCy Schubert 	    if (!krb5_checksum_is_keyed(context, self.cksum.cksumtype)) {
189624339377SCy Schubert 		free_PA_S4U2Self(&self);
189724339377SCy Schubert 		kdc_log(context, config, 0, "Reject PA-S4U2Self with unkeyed checksum");
189824339377SCy Schubert 		ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
189924339377SCy Schubert 		goto out;
190024339377SCy Schubert 	    }
190124339377SCy Schubert 
1902c19800e8SDoug Rabson 	    ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
1903c19800e8SDoug Rabson 	    if (ret)
1904c19800e8SDoug Rabson 		goto out;
1905c19800e8SDoug Rabson 
1906c19800e8SDoug Rabson 	    ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
1907c19800e8SDoug Rabson 	    if (ret) {
1908ae771770SStanislav Sedov 		const char *msg = krb5_get_error_message(context, ret);
1909c19800e8SDoug Rabson 		free_PA_S4U2Self(&self);
1910c19800e8SDoug Rabson 		krb5_data_free(&datack);
1911ae771770SStanislav Sedov 		kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
1912ae771770SStanislav Sedov 		krb5_free_error_message(context, msg);
1913c19800e8SDoug Rabson 		goto out;
1914c19800e8SDoug Rabson 	    }
1915c19800e8SDoug Rabson 
1916c19800e8SDoug Rabson 	    ret = krb5_verify_checksum(context,
1917c19800e8SDoug Rabson 				       crypto,
1918c19800e8SDoug Rabson 				       KRB5_KU_OTHER_CKSUM,
1919c19800e8SDoug Rabson 				       datack.data,
1920c19800e8SDoug Rabson 				       datack.length,
1921c19800e8SDoug Rabson 				       &self.cksum);
1922c19800e8SDoug Rabson 	    krb5_data_free(&datack);
1923c19800e8SDoug Rabson 	    krb5_crypto_destroy(context, crypto);
1924c19800e8SDoug Rabson 	    if (ret) {
1925ae771770SStanislav Sedov 		const char *msg = krb5_get_error_message(context, ret);
1926c19800e8SDoug Rabson 		free_PA_S4U2Self(&self);
1927c19800e8SDoug Rabson 		kdc_log(context, config, 0,
1928ae771770SStanislav Sedov 			"krb5_verify_checksum failed for S4U2Self: %s", msg);
1929ae771770SStanislav Sedov 		krb5_free_error_message(context, msg);
1930c19800e8SDoug Rabson 		goto out;
1931c19800e8SDoug Rabson 	    }
1932c19800e8SDoug Rabson 
1933c19800e8SDoug Rabson 	    ret = _krb5_principalname2krb5_principal(context,
1934ae771770SStanislav Sedov 						     &tp,
1935c19800e8SDoug Rabson 						     self.name,
1936c19800e8SDoug Rabson 						     self.realm);
1937c19800e8SDoug Rabson 	    free_PA_S4U2Self(&self);
1938c19800e8SDoug Rabson 	    if (ret)
1939c19800e8SDoug Rabson 		goto out;
1940c19800e8SDoug Rabson 
1941ae771770SStanislav Sedov 	    ret = krb5_unparse_name(context, tp, &tpn);
1942c19800e8SDoug Rabson 	    if (ret)
1943c19800e8SDoug Rabson 		goto out;
1944c19800e8SDoug Rabson 
1945ae771770SStanislav Sedov 	    ret = _kdc_db_fetch(context, config, tp, HDB_F_GET_CLIENT | flags,
1946ed549cb0SCy Schubert 				NULL, &s4u2self_impersonated_clientdb,
1947ed549cb0SCy Schubert 				&s4u2self_impersonated_client);
1948ae771770SStanislav Sedov 	    if (ret) {
1949ae771770SStanislav Sedov 		const char *msg;
1950ae771770SStanislav Sedov 
1951ae771770SStanislav Sedov 		/*
1952ae771770SStanislav Sedov 		 * If the client belongs to the same realm as our krbtgt, it
1953ae771770SStanislav Sedov 		 * should exist in the local database.
1954ae771770SStanislav Sedov 		 *
1955ae771770SStanislav Sedov 		 */
1956ae771770SStanislav Sedov 
1957ae771770SStanislav Sedov 		if (ret == HDB_ERR_NOENTRY)
1958ae771770SStanislav Sedov 		    ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1959ae771770SStanislav Sedov 		msg = krb5_get_error_message(context, ret);
1960ed549cb0SCy Schubert 		kdc_log(context, config, 2,
1961ed549cb0SCy Schubert 			"S4U2Self principal to impersonate %s not found in database: %s",
1962ae771770SStanislav Sedov 			tpn, msg);
1963ae771770SStanislav Sedov 		krb5_free_error_message(context, msg);
1964ae771770SStanislav Sedov 		goto out;
1965ae771770SStanislav Sedov 	    }
1966ed549cb0SCy Schubert 
1967ed549cb0SCy Schubert 	    free(s4u2self_impersonated_client->entry.pw_end);
1968ed549cb0SCy Schubert 	    s4u2self_impersonated_client->entry.pw_end = NULL;
1969ed549cb0SCy Schubert 
1970ed549cb0SCy Schubert 	    ret = kdc_check_flags(context, config, s4u2self_impersonated_client, tpn,
1971ed549cb0SCy Schubert 				  NULL, NULL, FALSE);
1972ed549cb0SCy Schubert 	    if (ret)
1973ed549cb0SCy Schubert 		goto out;
1974ed549cb0SCy Schubert 
1975ed549cb0SCy Schubert 	    /* If we were about to put a PAC into the ticket, we better fix it to be the right PAC */
1976ed549cb0SCy Schubert 	    if(rspac.data) {
1977ed549cb0SCy Schubert 		krb5_pac p = NULL;
1978ed549cb0SCy Schubert 		krb5_data_free(&rspac);
1979ae771770SStanislav Sedov 		ret = _kdc_pac_generate(context, s4u2self_impersonated_client, &p);
1980ae771770SStanislav Sedov 		if (ret) {
1981ae771770SStanislav Sedov 		    kdc_log(context, config, 0, "PAC generation failed for -- %s",
1982ae771770SStanislav Sedov 			    tpn);
1983ae771770SStanislav Sedov 		    goto out;
1984ae771770SStanislav Sedov 		}
1985ae771770SStanislav Sedov 		if (p != NULL) {
1986ae771770SStanislav Sedov 		    ret = _krb5_pac_sign(context, p, ticket->ticket.authtime,
1987ae771770SStanislav Sedov 					 s4u2self_impersonated_client->entry.principal,
1988ae771770SStanislav Sedov 					 ekey, &tkey_sign->key,
1989ae771770SStanislav Sedov 					 &rspac);
1990ae771770SStanislav Sedov 		    krb5_pac_free(context, p);
1991ae771770SStanislav Sedov 		    if (ret) {
1992ae771770SStanislav Sedov 			kdc_log(context, config, 0, "PAC signing failed for -- %s",
1993ae771770SStanislav Sedov 				tpn);
1994ae771770SStanislav Sedov 			goto out;
1995ae771770SStanislav Sedov 		    }
1996ae771770SStanislav Sedov 		}
1997ae771770SStanislav Sedov 	    }
1998ae771770SStanislav Sedov 
1999c19800e8SDoug Rabson 	    /*
2000c19800e8SDoug Rabson 	     * Check that service doing the impersonating is
2001c19800e8SDoug Rabson 	     * requesting a ticket to it-self.
2002c19800e8SDoug Rabson 	     */
2003ae771770SStanislav Sedov 	    ret = check_s4u2self(context, config, clientdb, client, sp);
2004ae771770SStanislav Sedov 	    if (ret) {
2005c19800e8SDoug Rabson 		kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
2006ae771770SStanislav Sedov 			"to impersonate to service "
2007c19800e8SDoug Rabson 			"(tried for user %s to service %s)",
2008ae771770SStanislav Sedov 			cpn, tpn, spn);
2009c19800e8SDoug Rabson 		goto out;
2010c19800e8SDoug Rabson 	    }
2011c19800e8SDoug Rabson 
2012c19800e8SDoug Rabson 	    /*
2013c19800e8SDoug Rabson 	     * If the service isn't trusted for authentication to
2014ed549cb0SCy Schubert 	     * delegation or if the impersonate client is disallowed
2015ed549cb0SCy Schubert 	     * forwardable, remove the forwardable flag.
2016c19800e8SDoug Rabson 	     */
2017c19800e8SDoug Rabson 
2018ed549cb0SCy Schubert 	    if (client->entry.flags.trusted_for_delegation &&
2019ed549cb0SCy Schubert 		s4u2self_impersonated_client->entry.flags.forwardable) {
2020c19800e8SDoug Rabson 		str = "[forwardable]";
2021c19800e8SDoug Rabson 	    } else {
2022c19800e8SDoug Rabson 		b->kdc_options.forwardable = 0;
2023c19800e8SDoug Rabson 		str = "";
2024c19800e8SDoug Rabson 	    }
2025c19800e8SDoug Rabson 	    kdc_log(context, config, 0, "s4u2self %s impersonating %s to "
2026ae771770SStanislav Sedov 		    "service %s %s", cpn, tpn, spn, str);
2027c19800e8SDoug Rabson 	}
2028c19800e8SDoug Rabson     }
2029c19800e8SDoug Rabson 
2030c19800e8SDoug Rabson     /*
2031c19800e8SDoug Rabson      * Constrained delegation
2032c19800e8SDoug Rabson      */
2033c19800e8SDoug Rabson 
2034c19800e8SDoug Rabson     if (client != NULL
2035c19800e8SDoug Rabson 	&& b->additional_tickets != NULL
2036c19800e8SDoug Rabson 	&& b->additional_tickets->len != 0
2037c19800e8SDoug Rabson 	&& b->kdc_options.enc_tkt_in_skey == 0)
2038c19800e8SDoug Rabson     {
2039ae771770SStanislav Sedov 	int ad_signedpath = 0;
2040c19800e8SDoug Rabson 	Key *clientkey;
2041c19800e8SDoug Rabson 	Ticket *t;
2042ae771770SStanislav Sedov 
2043ae771770SStanislav Sedov 	/*
2044ae771770SStanislav Sedov 	 * Require that the KDC have issued the service's krbtgt (not
2045ae771770SStanislav Sedov 	 * self-issued ticket with kimpersonate(1).
2046ae771770SStanislav Sedov 	 */
2047ae771770SStanislav Sedov 	if (!signedpath) {
2048ae771770SStanislav Sedov 	    ret = KRB5KDC_ERR_BADOPTION;
2049ae771770SStanislav Sedov 	    kdc_log(context, config, 0,
2050ae771770SStanislav Sedov 		    "Constrained delegation done on service ticket %s/%s",
2051ae771770SStanislav Sedov 		    cpn, spn);
2052ae771770SStanislav Sedov 	    goto out;
2053ae771770SStanislav Sedov 	}
2054c19800e8SDoug Rabson 
2055c19800e8SDoug Rabson 	t = &b->additional_tickets->val[0];
2056c19800e8SDoug Rabson 
2057c19800e8SDoug Rabson 	ret = hdb_enctype2key(context, &client->entry,
2058c19800e8SDoug Rabson 			      t->enc_part.etype, &clientkey);
2059c19800e8SDoug Rabson 	if(ret){
2060c19800e8SDoug Rabson 	    ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
2061c19800e8SDoug Rabson 	    goto out;
2062c19800e8SDoug Rabson 	}
2063c19800e8SDoug Rabson 
2064c19800e8SDoug Rabson 	ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
2065c19800e8SDoug Rabson 	if (ret) {
2066c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
2067c19800e8SDoug Rabson 		    "failed to decrypt ticket for "
2068ae771770SStanislav Sedov 		    "constrained delegation from %s to %s ", cpn, spn);
2069c19800e8SDoug Rabson 	    goto out;
2070c19800e8SDoug Rabson 	}
2071c19800e8SDoug Rabson 
2072c19800e8SDoug Rabson 	ret = _krb5_principalname2krb5_principal(context,
2073ae771770SStanislav Sedov 						 &tp,
2074c19800e8SDoug Rabson 						 adtkt.cname,
2075c19800e8SDoug Rabson 						 adtkt.crealm);
2076c19800e8SDoug Rabson 	if (ret)
2077c19800e8SDoug Rabson 	    goto out;
2078c19800e8SDoug Rabson 
2079ae771770SStanislav Sedov 	ret = krb5_unparse_name(context, tp, &tpn);
2080c19800e8SDoug Rabson 	if (ret)
2081c19800e8SDoug Rabson 	    goto out;
2082c19800e8SDoug Rabson 
2083ae771770SStanislav Sedov 	ret = _krb5_principalname2krb5_principal(context,
2084ae771770SStanislav Sedov 						 &dp,
2085ae771770SStanislav Sedov 						 t->sname,
2086ae771770SStanislav Sedov 						 t->realm);
2087ae771770SStanislav Sedov 	if (ret)
2088ae771770SStanislav Sedov 	    goto out;
2089ae771770SStanislav Sedov 
2090ae771770SStanislav Sedov 	ret = krb5_unparse_name(context, dp, &dpn);
2091ae771770SStanislav Sedov 	if (ret)
2092ae771770SStanislav Sedov 	    goto out;
2093ae771770SStanislav Sedov 
2094ae771770SStanislav Sedov 	/* check that ticket is valid */
2095ae771770SStanislav Sedov 	if (adtkt.flags.forwardable == 0) {
2096ae771770SStanislav Sedov 	    kdc_log(context, config, 0,
2097ae771770SStanislav Sedov 		    "Missing forwardable flag on ticket for "
2098ae771770SStanislav Sedov 		    "constrained delegation from %s (%s) as %s to %s ",
2099ae771770SStanislav Sedov 		    cpn, dpn, tpn, spn);
2100ae771770SStanislav Sedov 	    ret = KRB5KDC_ERR_BADOPTION;
2101ae771770SStanislav Sedov 	    goto out;
2102ae771770SStanislav Sedov 	}
2103ae771770SStanislav Sedov 
2104ae771770SStanislav Sedov 	ret = check_constrained_delegation(context, config, clientdb,
2105ae771770SStanislav Sedov 					   client, server, sp);
2106c19800e8SDoug Rabson 	if (ret) {
2107ae771770SStanislav Sedov 	    kdc_log(context, config, 0,
2108ae771770SStanislav Sedov 		    "constrained delegation from %s (%s) as %s to %s not allowed",
2109ae771770SStanislav Sedov 		    cpn, dpn, tpn, spn);
2110ae771770SStanislav Sedov 	    goto out;
2111ae771770SStanislav Sedov 	}
2112ae771770SStanislav Sedov 
2113ae771770SStanislav Sedov 	ret = verify_flags(context, config, &adtkt, tpn);
2114ae771770SStanislav Sedov 	if (ret) {
2115ae771770SStanislav Sedov 	    goto out;
2116ae771770SStanislav Sedov 	}
2117ae771770SStanislav Sedov 
2118ae771770SStanislav Sedov 	krb5_data_free(&rspac);
2119ae771770SStanislav Sedov 
2120ae771770SStanislav Sedov 	/*
2121ae771770SStanislav Sedov 	 * generate the PAC for the user.
2122ae771770SStanislav Sedov 	 *
2123ae771770SStanislav Sedov 	 * TODO: pass in t->sname and t->realm and build
2124ae771770SStanislav Sedov 	 * a S4U_DELEGATION_INFO blob to the PAC.
2125ae771770SStanislav Sedov 	 */
2126ae771770SStanislav Sedov 	ret = check_PAC(context, config, tp, dp,
2127ae771770SStanislav Sedov 			client, server, krbtgt,
2128ae771770SStanislav Sedov 			&clientkey->key, &tkey_check->key,
2129ae771770SStanislav Sedov 			ekey, &tkey_sign->key,
2130ae771770SStanislav Sedov 			&adtkt, &rspac, &ad_signedpath);
2131ae771770SStanislav Sedov 	if (ret) {
2132ae771770SStanislav Sedov 	    const char *msg = krb5_get_error_message(context, ret);
2133ae771770SStanislav Sedov 	    kdc_log(context, config, 0,
2134ae771770SStanislav Sedov 		    "Verify delegated PAC failed to %s for client"
2135ae771770SStanislav Sedov 		    "%s (%s) as %s from %s with %s",
2136ae771770SStanislav Sedov 		    spn, cpn, dpn, tpn, from, msg);
2137ae771770SStanislav Sedov 	    krb5_free_error_message(context, msg);
2138c19800e8SDoug Rabson 	    goto out;
2139c19800e8SDoug Rabson 	}
2140c19800e8SDoug Rabson 
2141c19800e8SDoug Rabson 	/*
2142ae771770SStanislav Sedov 	 * Check that the KDC issued the user's ticket.
2143c19800e8SDoug Rabson 	 */
2144c19800e8SDoug Rabson 	ret = check_KRB5SignedPath(context,
2145c19800e8SDoug Rabson 				   config,
2146c19800e8SDoug Rabson 				   krbtgt,
2147ae771770SStanislav Sedov 				   cp,
2148c19800e8SDoug Rabson 				   &adtkt,
2149ae771770SStanislav Sedov 				   NULL,
2150ae771770SStanislav Sedov 				   &ad_signedpath);
2151c19800e8SDoug Rabson 	if (ret) {
2152ae771770SStanislav Sedov 	    const char *msg = krb5_get_error_message(context, ret);
2153c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
2154c19800e8SDoug Rabson 		    "KRB5SignedPath check from service %s failed "
2155ae771770SStanislav Sedov 		    "for delegation to %s for client %s (%s)"
2156c19800e8SDoug Rabson 		    "from %s failed with %s",
2157ae771770SStanislav Sedov 		    spn, tpn, dpn, cpn, from, msg);
2158ae771770SStanislav Sedov 	    krb5_free_error_message(context, msg);
2159ae771770SStanislav Sedov 	    goto out;
2160ae771770SStanislav Sedov 	}
2161ae771770SStanislav Sedov 
2162ae771770SStanislav Sedov 	if (!ad_signedpath) {
2163ae771770SStanislav Sedov 	    ret = KRB5KDC_ERR_BADOPTION;
2164ae771770SStanislav Sedov 	    kdc_log(context, config, 0,
2165ae771770SStanislav Sedov 		    "Ticket not signed with PAC nor SignedPath service %s failed "
2166ae771770SStanislav Sedov 		    "for delegation to %s for client %s (%s)"
2167ae771770SStanislav Sedov 		    "from %s",
2168ae771770SStanislav Sedov 		    spn, tpn, dpn, cpn, from);
2169c19800e8SDoug Rabson 	    goto out;
2170c19800e8SDoug Rabson 	}
2171c19800e8SDoug Rabson 
2172c19800e8SDoug Rabson 	kdc_log(context, config, 0, "constrained delegation for %s "
2173ae771770SStanislav Sedov 		"from %s (%s) to %s", tpn, cpn, dpn, spn);
2174c19800e8SDoug Rabson     }
2175c19800e8SDoug Rabson 
2176c19800e8SDoug Rabson     /*
2177c19800e8SDoug Rabson      * Check flags
2178c19800e8SDoug Rabson      */
2179c19800e8SDoug Rabson 
2180ae771770SStanislav Sedov     ret = kdc_check_flags(context, config,
2181c19800e8SDoug Rabson 			  client, cpn,
2182c19800e8SDoug Rabson 			  server, spn,
2183c19800e8SDoug Rabson 			  FALSE);
2184c19800e8SDoug Rabson     if(ret)
2185c19800e8SDoug Rabson 	goto out;
2186c19800e8SDoug Rabson 
2187c19800e8SDoug Rabson     if((b->kdc_options.validate || b->kdc_options.renew) &&
2188c19800e8SDoug Rabson        !krb5_principal_compare(context,
2189c19800e8SDoug Rabson 			       krbtgt->entry.principal,
2190c19800e8SDoug Rabson 			       server->entry.principal)){
2191c19800e8SDoug Rabson 	kdc_log(context, config, 0, "Inconsistent request.");
2192c19800e8SDoug Rabson 	ret = KRB5KDC_ERR_SERVER_NOMATCH;
2193c19800e8SDoug Rabson 	goto out;
2194c19800e8SDoug Rabson     }
2195c19800e8SDoug Rabson 
2196c19800e8SDoug Rabson     /* check for valid set of addresses */
2197c19800e8SDoug Rabson     if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) {
2198c19800e8SDoug Rabson 	ret = KRB5KRB_AP_ERR_BADADDR;
2199c19800e8SDoug Rabson 	kdc_log(context, config, 0, "Request from wrong address");
2200c19800e8SDoug Rabson 	goto out;
2201c19800e8SDoug Rabson     }
2202c19800e8SDoug Rabson 
2203c19800e8SDoug Rabson     /*
2204ae771770SStanislav Sedov      * If this is an referral, add server referral data to the
2205ae771770SStanislav Sedov      * auth_data reply .
2206c19800e8SDoug Rabson      */
2207ae771770SStanislav Sedov     if (ref_realm) {
2208ae771770SStanislav Sedov 	PA_DATA pa;
2209ae771770SStanislav Sedov 	krb5_crypto crypto;
2210c19800e8SDoug Rabson 
2211c19800e8SDoug Rabson 	kdc_log(context, config, 0,
2212ae771770SStanislav Sedov 		"Adding server referral to %s", ref_realm);
2213c19800e8SDoug Rabson 
2214ae771770SStanislav Sedov 	ret = krb5_crypto_init(context, &sessionkey, 0, &crypto);
2215c19800e8SDoug Rabson 	if (ret)
2216c19800e8SDoug Rabson 	    goto out;
2217c19800e8SDoug Rabson 
2218ae771770SStanislav Sedov 	ret = build_server_referral(context, config, crypto, ref_realm,
2219ae771770SStanislav Sedov 				    NULL, s, &pa.padata_value);
2220ae771770SStanislav Sedov 	krb5_crypto_destroy(context, crypto);
2221c19800e8SDoug Rabson 	if (ret) {
2222c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
2223ae771770SStanislav Sedov 		    "Failed building server referral");
2224c19800e8SDoug Rabson 	    goto out;
2225c19800e8SDoug Rabson 	}
2226ae771770SStanislav Sedov 	pa.padata_type = KRB5_PADATA_SERVER_REFERRAL;
2227c19800e8SDoug Rabson 
2228ae771770SStanislav Sedov 	ret = add_METHOD_DATA(&enc_pa_data, &pa);
2229ae771770SStanislav Sedov 	krb5_data_free(&pa.padata_value);
2230c19800e8SDoug Rabson 	if (ret) {
2231c19800e8SDoug Rabson 	    kdc_log(context, config, 0,
2232ae771770SStanislav Sedov 		    "Add server referral METHOD-DATA failed");
2233c19800e8SDoug Rabson 	    goto out;
2234c19800e8SDoug Rabson 	}
2235c19800e8SDoug Rabson     }
2236c19800e8SDoug Rabson 
2237c19800e8SDoug Rabson     /*
2238c19800e8SDoug Rabson      *
2239c19800e8SDoug Rabson      */
2240c19800e8SDoug Rabson 
2241c19800e8SDoug Rabson     ret = tgs_make_reply(context,
2242c19800e8SDoug Rabson 			 config,
2243c19800e8SDoug Rabson 			 b,
2244ae771770SStanislav Sedov 			 tp,
2245c19800e8SDoug Rabson 			 tgt,
2246ae771770SStanislav Sedov 			 replykey,
2247ae771770SStanislav Sedov 			 rk_is_subkey,
2248c19800e8SDoug Rabson 			 ekey,
2249c19800e8SDoug Rabson 			 &sessionkey,
2250c19800e8SDoug Rabson 			 kvno,
2251ae771770SStanislav Sedov 			 *auth_data,
2252c19800e8SDoug Rabson 			 server,
2253ae771770SStanislav Sedov 			 rsp,
2254c19800e8SDoug Rabson 			 spn,
2255c19800e8SDoug Rabson 			 client,
2256c19800e8SDoug Rabson 			 cp,
2257f8041e36SCy Schubert 			 tgt_realm,
2258ae771770SStanislav Sedov 			 krbtgt_out,
2259c19800e8SDoug Rabson 			 krbtgt_etype,
2260c19800e8SDoug Rabson 			 spp,
2261c19800e8SDoug Rabson 			 &rspac,
2262ae771770SStanislav Sedov 			 &enc_pa_data,
2263c19800e8SDoug Rabson 			 e_text,
2264c19800e8SDoug Rabson 			 reply);
2265c19800e8SDoug Rabson 
2266c19800e8SDoug Rabson out:
2267ae771770SStanislav Sedov     if (tpn != cpn)
2268ae771770SStanislav Sedov 	    free(tpn);
2269c19800e8SDoug Rabson     free(spn);
2270c19800e8SDoug Rabson     free(cpn);
2271ae771770SStanislav Sedov     if (dpn)
2272ae771770SStanislav Sedov 	free(dpn);
2273c19800e8SDoug Rabson 
2274c19800e8SDoug Rabson     krb5_data_free(&rspac);
2275c19800e8SDoug Rabson     krb5_free_keyblock_contents(context, &sessionkey);
2276ae771770SStanislav Sedov     if(krbtgt_out)
2277ae771770SStanislav Sedov 	_kdc_free_ent(context, krbtgt_out);
2278c19800e8SDoug Rabson     if(server)
2279c19800e8SDoug Rabson 	_kdc_free_ent(context, server);
2280c19800e8SDoug Rabson     if(client)
2281c19800e8SDoug Rabson 	_kdc_free_ent(context, client);
2282ae771770SStanislav Sedov     if(s4u2self_impersonated_client)
2283ae771770SStanislav Sedov 	_kdc_free_ent(context, s4u2self_impersonated_client);
2284c19800e8SDoug Rabson 
2285ae771770SStanislav Sedov     if (tp && tp != cp)
2286ae771770SStanislav Sedov 	krb5_free_principal(context, tp);
2287c19800e8SDoug Rabson     if (cp)
2288c19800e8SDoug Rabson 	krb5_free_principal(context, cp);
2289ae771770SStanislav Sedov     if (dp)
2290ae771770SStanislav Sedov 	krb5_free_principal(context, dp);
2291c19800e8SDoug Rabson     if (sp)
2292c19800e8SDoug Rabson 	krb5_free_principal(context, sp);
2293ae771770SStanislav Sedov     if (ref_realm)
2294ae771770SStanislav Sedov 	free(ref_realm);
2295ae771770SStanislav Sedov     free_METHOD_DATA(&enc_pa_data);
2296c19800e8SDoug Rabson 
2297c19800e8SDoug Rabson     free_EncTicketPart(&adtkt);
2298c19800e8SDoug Rabson 
2299c19800e8SDoug Rabson     return ret;
2300c19800e8SDoug Rabson }
2301c19800e8SDoug Rabson 
2302c19800e8SDoug Rabson /*
2303c19800e8SDoug Rabson  *
2304c19800e8SDoug Rabson  */
2305c19800e8SDoug Rabson 
2306c19800e8SDoug Rabson krb5_error_code
_kdc_tgs_rep(krb5_context context,krb5_kdc_configuration * config,KDC_REQ * req,krb5_data * data,const char * from,struct sockaddr * from_addr,int datagram_reply)2307c19800e8SDoug Rabson _kdc_tgs_rep(krb5_context context,
2308c19800e8SDoug Rabson 	     krb5_kdc_configuration *config,
2309c19800e8SDoug Rabson 	     KDC_REQ *req,
2310c19800e8SDoug Rabson 	     krb5_data *data,
2311c19800e8SDoug Rabson 	     const char *from,
2312c19800e8SDoug Rabson 	     struct sockaddr *from_addr,
2313c19800e8SDoug Rabson 	     int datagram_reply)
2314c19800e8SDoug Rabson {
2315c19800e8SDoug Rabson     AuthorizationData *auth_data = NULL;
2316c19800e8SDoug Rabson     krb5_error_code ret;
2317c19800e8SDoug Rabson     int i = 0;
2318c19800e8SDoug Rabson     const PA_DATA *tgs_req;
2319c19800e8SDoug Rabson 
2320c19800e8SDoug Rabson     hdb_entry_ex *krbtgt = NULL;
2321c19800e8SDoug Rabson     krb5_ticket *ticket = NULL;
2322c19800e8SDoug Rabson     const char *e_text = NULL;
2323c19800e8SDoug Rabson     krb5_enctype krbtgt_etype = ETYPE_NULL;
2324c19800e8SDoug Rabson 
2325ae771770SStanislav Sedov     krb5_keyblock *replykey = NULL;
2326ae771770SStanislav Sedov     int rk_is_subkey = 0;
2327c19800e8SDoug Rabson     time_t *csec = NULL;
2328c19800e8SDoug Rabson     int *cusec = NULL;
2329c19800e8SDoug Rabson 
2330c19800e8SDoug Rabson     if(req->padata == NULL){
2331c19800e8SDoug Rabson 	ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
2332c19800e8SDoug Rabson 	kdc_log(context, config, 0,
2333c19800e8SDoug Rabson 		"TGS-REQ from %s without PA-DATA", from);
2334c19800e8SDoug Rabson 	goto out;
2335c19800e8SDoug Rabson     }
2336c19800e8SDoug Rabson 
2337c19800e8SDoug Rabson     tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
2338c19800e8SDoug Rabson 
2339c19800e8SDoug Rabson     if(tgs_req == NULL){
2340c19800e8SDoug Rabson 	ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2341c19800e8SDoug Rabson 
2342c19800e8SDoug Rabson 	kdc_log(context, config, 0,
2343c19800e8SDoug Rabson 		"TGS-REQ from %s without PA-TGS-REQ", from);
2344c19800e8SDoug Rabson 	goto out;
2345c19800e8SDoug Rabson     }
2346c19800e8SDoug Rabson     ret = tgs_parse_request(context, config,
2347c19800e8SDoug Rabson 			    &req->req_body, tgs_req,
2348c19800e8SDoug Rabson 			    &krbtgt,
2349c19800e8SDoug Rabson 			    &krbtgt_etype,
2350c19800e8SDoug Rabson 			    &ticket,
2351c19800e8SDoug Rabson 			    &e_text,
2352c19800e8SDoug Rabson 			    from, from_addr,
2353c19800e8SDoug Rabson 			    &csec, &cusec,
2354ae771770SStanislav Sedov 			    &auth_data,
2355ae771770SStanislav Sedov 			    &replykey,
2356ae771770SStanislav Sedov 			    &rk_is_subkey);
2357ae771770SStanislav Sedov     if (ret == HDB_ERR_NOT_FOUND_HERE) {
2358ae771770SStanislav Sedov 	/* kdc_log() is called in tgs_parse_request() */
2359ae771770SStanislav Sedov 	goto out;
2360ae771770SStanislav Sedov     }
2361c19800e8SDoug Rabson     if (ret) {
2362c19800e8SDoug Rabson 	kdc_log(context, config, 0,
2363c19800e8SDoug Rabson 		"Failed parsing TGS-REQ from %s", from);
2364c19800e8SDoug Rabson 	goto out;
2365c19800e8SDoug Rabson     }
2366c19800e8SDoug Rabson 
2367c19800e8SDoug Rabson     ret = tgs_build_reply(context,
2368c19800e8SDoug Rabson 			  config,
2369c19800e8SDoug Rabson 			  req,
2370c19800e8SDoug Rabson 			  &req->req_body,
2371c19800e8SDoug Rabson 			  krbtgt,
2372c19800e8SDoug Rabson 			  krbtgt_etype,
2373ae771770SStanislav Sedov 			  replykey,
2374ae771770SStanislav Sedov 			  rk_is_subkey,
2375c19800e8SDoug Rabson 			  ticket,
2376c19800e8SDoug Rabson 			  data,
2377c19800e8SDoug Rabson 			  from,
2378c19800e8SDoug Rabson 			  &e_text,
2379ae771770SStanislav Sedov 			  &auth_data,
2380ae771770SStanislav Sedov 			  from_addr);
2381c19800e8SDoug Rabson     if (ret) {
2382c19800e8SDoug Rabson 	kdc_log(context, config, 0,
2383c19800e8SDoug Rabson 		"Failed building TGS-REP to %s", from);
2384c19800e8SDoug Rabson 	goto out;
2385c19800e8SDoug Rabson     }
2386c19800e8SDoug Rabson 
2387c19800e8SDoug Rabson     /* */
2388c19800e8SDoug Rabson     if (datagram_reply && data->length > config->max_datagram_reply_length) {
2389c19800e8SDoug Rabson 	krb5_data_free(data);
2390c19800e8SDoug Rabson 	ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
2391c19800e8SDoug Rabson 	e_text = "Reply packet too large";
2392c19800e8SDoug Rabson     }
2393c19800e8SDoug Rabson 
2394c19800e8SDoug Rabson out:
2395ae771770SStanislav Sedov     if (replykey)
2396ae771770SStanislav Sedov 	krb5_free_keyblock(context, replykey);
2397ae771770SStanislav Sedov     if(ret && ret != HDB_ERR_NOT_FOUND_HERE && data->data == NULL){
2398c19800e8SDoug Rabson 	krb5_mk_error(context,
2399c19800e8SDoug Rabson 		      ret,
2400c19800e8SDoug Rabson 		      NULL,
2401c19800e8SDoug Rabson 		      NULL,
2402c19800e8SDoug Rabson 		      NULL,
2403c19800e8SDoug Rabson 		      NULL,
2404c19800e8SDoug Rabson 		      csec,
2405c19800e8SDoug Rabson 		      cusec,
2406c19800e8SDoug Rabson 		      data);
2407ae771770SStanislav Sedov 	ret = 0;
2408c19800e8SDoug Rabson     }
2409c19800e8SDoug Rabson     free(csec);
2410c19800e8SDoug Rabson     free(cusec);
2411c19800e8SDoug Rabson     if (ticket)
2412c19800e8SDoug Rabson 	krb5_free_ticket(context, ticket);
2413c19800e8SDoug Rabson     if(krbtgt)
2414c19800e8SDoug Rabson 	_kdc_free_ent(context, krbtgt);
2415c19800e8SDoug Rabson 
2416c19800e8SDoug Rabson     if (auth_data) {
2417c19800e8SDoug Rabson 	free_AuthorizationData(auth_data);
2418c19800e8SDoug Rabson 	free(auth_data);
2419c19800e8SDoug Rabson     }
2420c19800e8SDoug Rabson 
2421ae771770SStanislav Sedov     return ret;
2422c19800e8SDoug Rabson }
2423