1178825Sdfr/*
2233294Sstas * Copyright (c) 1997-2008 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
5178825Sdfr *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
9178825Sdfr *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
20178825Sdfr *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include "kdc_locl.h"
35178825Sdfr
36178825Sdfr/*
37178825Sdfr * return the realm of a krbtgt-ticket or NULL
38178825Sdfr */
39178825Sdfr
40233294Sstasstatic Realm
41178825Sdfrget_krbtgt_realm(const PrincipalName *p)
42178825Sdfr{
43178825Sdfr    if(p->name_string.len == 2
44178825Sdfr       && strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0)
45178825Sdfr	return p->name_string.val[1];
46178825Sdfr    else
47178825Sdfr	return NULL;
48178825Sdfr}
49178825Sdfr
50178825Sdfr/*
51178825Sdfr * The KDC might add a signed path to the ticket authorization data
52178825Sdfr * field. This is to avoid server impersonating clients and the
53178825Sdfr * request constrained delegation.
54178825Sdfr *
55178825Sdfr * This is done by storing a KRB5_AUTHDATA_IF_RELEVANT with a single
56178825Sdfr * entry of type KRB5SignedPath.
57178825Sdfr */
58178825Sdfr
59178825Sdfrstatic krb5_error_code
60178825Sdfrfind_KRB5SignedPath(krb5_context context,
61178825Sdfr		    const AuthorizationData *ad,
62178825Sdfr		    krb5_data *data)
63178825Sdfr{
64178825Sdfr    AuthorizationData child;
65178825Sdfr    krb5_error_code ret;
66178825Sdfr    int pos;
67233294Sstas
68178825Sdfr    if (ad == NULL || ad->len == 0)
69178825Sdfr	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
70178825Sdfr
71178825Sdfr    pos = ad->len - 1;
72178825Sdfr
73178825Sdfr    if (ad->val[pos].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
74178825Sdfr	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
75178825Sdfr
76178825Sdfr    ret = decode_AuthorizationData(ad->val[pos].ad_data.data,
77178825Sdfr				   ad->val[pos].ad_data.length,
78178825Sdfr				   &child,
79178825Sdfr				   NULL);
80178825Sdfr    if (ret) {
81233294Sstas	krb5_set_error_message(context, ret, "Failed to decode "
82233294Sstas			       "IF_RELEVANT with %d", ret);
83178825Sdfr	return ret;
84178825Sdfr    }
85178825Sdfr
86178825Sdfr    if (child.len != 1) {
87178825Sdfr	free_AuthorizationData(&child);
88178825Sdfr	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
89178825Sdfr    }
90178825Sdfr
91178825Sdfr    if (child.val[0].ad_type != KRB5_AUTHDATA_SIGNTICKET) {
92178825Sdfr	free_AuthorizationData(&child);
93178825Sdfr	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
94178825Sdfr    }
95178825Sdfr
96178825Sdfr    if (data)
97178825Sdfr	ret = der_copy_octet_string(&child.val[0].ad_data, data);
98178825Sdfr    free_AuthorizationData(&child);
99178825Sdfr    return ret;
100178825Sdfr}
101178825Sdfr
102178825Sdfrkrb5_error_code
103178825Sdfr_kdc_add_KRB5SignedPath(krb5_context context,
104178825Sdfr			krb5_kdc_configuration *config,
105178825Sdfr			hdb_entry_ex *krbtgt,
106178825Sdfr			krb5_enctype enctype,
107233294Sstas			krb5_principal client,
108178825Sdfr			krb5_const_principal server,
109233294Sstas			krb5_principals principals,
110178825Sdfr			EncTicketPart *tkt)
111178825Sdfr{
112178825Sdfr    krb5_error_code ret;
113178825Sdfr    KRB5SignedPath sp;
114178825Sdfr    krb5_data data;
115178825Sdfr    krb5_crypto crypto = NULL;
116233294Sstas    size_t size = 0;
117178825Sdfr
118178825Sdfr    if (server && principals) {
119233294Sstas	ret = add_Principals(principals, server);
120178825Sdfr	if (ret)
121178825Sdfr	    return ret;
122178825Sdfr    }
123178825Sdfr
124178825Sdfr    {
125178825Sdfr	KRB5SignedPathData spd;
126233294Sstas
127233294Sstas	spd.client = client;
128233294Sstas	spd.authtime = tkt->authtime;
129178825Sdfr	spd.delegated = principals;
130233294Sstas	spd.method_data = NULL;
131233294Sstas
132178825Sdfr	ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
133178825Sdfr			   &spd, &size, ret);
134178825Sdfr	if (ret)
135178825Sdfr	    return ret;
136178825Sdfr	if (data.length != size)
137178825Sdfr	    krb5_abortx(context, "internal asn.1 encoder error");
138178825Sdfr    }
139178825Sdfr
140178825Sdfr    {
141178825Sdfr	Key *key;
142178825Sdfr	ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key);
143178825Sdfr	if (ret == 0)
144178825Sdfr	    ret = krb5_crypto_init(context, &key->key, 0, &crypto);
145178825Sdfr	if (ret) {
146178825Sdfr	    free(data.data);
147178825Sdfr	    return ret;
148178825Sdfr	}
149178825Sdfr    }
150178825Sdfr
151178825Sdfr    /*
152178825Sdfr     * Fill in KRB5SignedPath
153178825Sdfr     */
154178825Sdfr
155178825Sdfr    sp.etype = enctype;
156178825Sdfr    sp.delegated = principals;
157233294Sstas    sp.method_data = NULL;
158178825Sdfr
159178825Sdfr    ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0,
160178825Sdfr			       data.data, data.length, &sp.cksum);
161178825Sdfr    krb5_crypto_destroy(context, crypto);
162178825Sdfr    free(data.data);
163178825Sdfr    if (ret)
164178825Sdfr	return ret;
165178825Sdfr
166178825Sdfr    ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret);
167178825Sdfr    free_Checksum(&sp.cksum);
168178825Sdfr    if (ret)
169178825Sdfr	return ret;
170178825Sdfr    if (data.length != size)
171178825Sdfr	krb5_abortx(context, "internal asn.1 encoder error");
172178825Sdfr
173233294Sstas
174178825Sdfr    /*
175178825Sdfr     * Add IF-RELEVANT(KRB5SignedPath) to the last slot in
176178825Sdfr     * authorization data field.
177178825Sdfr     */
178178825Sdfr
179178825Sdfr    ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
180178825Sdfr				      KRB5_AUTHDATA_SIGNTICKET, &data);
181178825Sdfr    krb5_data_free(&data);
182178825Sdfr
183178825Sdfr    return ret;
184178825Sdfr}
185178825Sdfr
186178825Sdfrstatic krb5_error_code
187178825Sdfrcheck_KRB5SignedPath(krb5_context context,
188178825Sdfr		     krb5_kdc_configuration *config,
189178825Sdfr		     hdb_entry_ex *krbtgt,
190233294Sstas		     krb5_principal cp,
191178825Sdfr		     EncTicketPart *tkt,
192233294Sstas		     krb5_principals *delegated,
193233294Sstas		     int *signedpath)
194178825Sdfr{
195178825Sdfr    krb5_error_code ret;
196178825Sdfr    krb5_data data;
197178825Sdfr    krb5_crypto crypto = NULL;
198178825Sdfr
199233294Sstas    if (delegated)
200233294Sstas	*delegated = NULL;
201178825Sdfr
202178825Sdfr    ret = find_KRB5SignedPath(context, tkt->authorization_data, &data);
203178825Sdfr    if (ret == 0) {
204178825Sdfr	KRB5SignedPathData spd;
205178825Sdfr	KRB5SignedPath sp;
206233294Sstas	size_t size = 0;
207178825Sdfr
208178825Sdfr	ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL);
209178825Sdfr	krb5_data_free(&data);
210178825Sdfr	if (ret)
211178825Sdfr	    return ret;
212178825Sdfr
213233294Sstas	spd.client = cp;
214233294Sstas	spd.authtime = tkt->authtime;
215178825Sdfr	spd.delegated = sp.delegated;
216233294Sstas	spd.method_data = sp.method_data;
217178825Sdfr
218178825Sdfr	ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
219178825Sdfr			   &spd, &size, ret);
220178825Sdfr	if (ret) {
221178825Sdfr	    free_KRB5SignedPath(&sp);
222178825Sdfr	    return ret;
223178825Sdfr	}
224178825Sdfr	if (data.length != size)
225178825Sdfr	    krb5_abortx(context, "internal asn.1 encoder error");
226178825Sdfr
227178825Sdfr	{
228178825Sdfr	    Key *key;
229178825Sdfr	    ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key);
230178825Sdfr	    if (ret == 0)
231178825Sdfr		ret = krb5_crypto_init(context, &key->key, 0, &crypto);
232178825Sdfr	    if (ret) {
233178825Sdfr		free(data.data);
234178825Sdfr		free_KRB5SignedPath(&sp);
235178825Sdfr		return ret;
236178825Sdfr	    }
237178825Sdfr	}
238233294Sstas	ret = krb5_verify_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH,
239233294Sstas				   data.data, data.length,
240178825Sdfr				   &sp.cksum);
241178825Sdfr	krb5_crypto_destroy(context, crypto);
242178825Sdfr	free(data.data);
243178825Sdfr	if (ret) {
244178825Sdfr	    free_KRB5SignedPath(&sp);
245233294Sstas	    kdc_log(context, config, 5,
246233294Sstas		    "KRB5SignedPath not signed correctly, not marking as signed");
247233294Sstas	    return 0;
248178825Sdfr	}
249178825Sdfr
250233294Sstas	if (delegated && sp.delegated) {
251178825Sdfr
252178825Sdfr	    *delegated = malloc(sizeof(*sp.delegated));
253178825Sdfr	    if (*delegated == NULL) {
254178825Sdfr		free_KRB5SignedPath(&sp);
255178825Sdfr		return ENOMEM;
256178825Sdfr	    }
257178825Sdfr
258233294Sstas	    ret = copy_Principals(*delegated, sp.delegated);
259178825Sdfr	    if (ret) {
260178825Sdfr		free_KRB5SignedPath(&sp);
261178825Sdfr		free(*delegated);
262178825Sdfr		*delegated = NULL;
263178825Sdfr		return ret;
264178825Sdfr	    }
265178825Sdfr	}
266178825Sdfr	free_KRB5SignedPath(&sp);
267233294Sstas
268233294Sstas	*signedpath = 1;
269178825Sdfr    }
270178825Sdfr
271178825Sdfr    return 0;
272178825Sdfr}
273178825Sdfr
274178825Sdfr/*
275178825Sdfr *
276178825Sdfr */
277178825Sdfr
278178825Sdfrstatic krb5_error_code
279178825Sdfrcheck_PAC(krb5_context context,
280178825Sdfr	  krb5_kdc_configuration *config,
281178825Sdfr	  const krb5_principal client_principal,
282233294Sstas	  const krb5_principal delegated_proxy_principal,
283178825Sdfr	  hdb_entry_ex *client,
284178825Sdfr	  hdb_entry_ex *server,
285233294Sstas	  hdb_entry_ex *krbtgt,
286233294Sstas	  const EncryptionKey *server_check_key,
287233294Sstas	  const EncryptionKey *krbtgt_check_key,
288233294Sstas	  const EncryptionKey *server_sign_key,
289233294Sstas	  const EncryptionKey *krbtgt_sign_key,
290178825Sdfr	  EncTicketPart *tkt,
291178825Sdfr	  krb5_data *rspac,
292233294Sstas	  int *signedpath)
293178825Sdfr{
294178825Sdfr    AuthorizationData *ad = tkt->authorization_data;
295178825Sdfr    unsigned i, j;
296178825Sdfr    krb5_error_code ret;
297178825Sdfr
298178825Sdfr    if (ad == NULL || ad->len == 0)
299178825Sdfr	return 0;
300178825Sdfr
301178825Sdfr    for (i = 0; i < ad->len; i++) {
302178825Sdfr	AuthorizationData child;
303178825Sdfr
304178825Sdfr	if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
305178825Sdfr	    continue;
306178825Sdfr
307178825Sdfr	ret = decode_AuthorizationData(ad->val[i].ad_data.data,
308178825Sdfr				       ad->val[i].ad_data.length,
309178825Sdfr				       &child,
310178825Sdfr				       NULL);
311178825Sdfr	if (ret) {
312233294Sstas	    krb5_set_error_message(context, ret, "Failed to decode "
313233294Sstas				   "IF_RELEVANT with %d", ret);
314178825Sdfr	    return ret;
315178825Sdfr	}
316178825Sdfr	for (j = 0; j < child.len; j++) {
317178825Sdfr
318178825Sdfr	    if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
319233294Sstas		int signed_pac = 0;
320178825Sdfr		krb5_pac pac;
321178825Sdfr
322178825Sdfr		/* Found PAC */
323178825Sdfr		ret = krb5_pac_parse(context,
324178825Sdfr				     child.val[j].ad_data.data,
325178825Sdfr				     child.val[j].ad_data.length,
326178825Sdfr				     &pac);
327178825Sdfr		free_AuthorizationData(&child);
328178825Sdfr		if (ret)
329178825Sdfr		    return ret;
330178825Sdfr
331233294Sstas		ret = krb5_pac_verify(context, pac, tkt->authtime,
332178825Sdfr				      client_principal,
333233294Sstas				      server_check_key, krbtgt_check_key);
334178825Sdfr		if (ret) {
335178825Sdfr		    krb5_pac_free(context, pac);
336178825Sdfr		    return ret;
337178825Sdfr		}
338178825Sdfr
339233294Sstas		ret = _kdc_pac_verify(context, client_principal,
340233294Sstas				      delegated_proxy_principal,
341233294Sstas				      client, server, krbtgt, &pac, &signed_pac);
342178825Sdfr		if (ret) {
343178825Sdfr		    krb5_pac_free(context, pac);
344178825Sdfr		    return ret;
345178825Sdfr		}
346178825Sdfr
347233294Sstas		/*
348233294Sstas		 * Only re-sign PAC if we could verify it with the PAC
349233294Sstas		 * function. The no-verify case happens when we get in
350233294Sstas		 * a PAC from cross realm from a Windows domain and
351233294Sstas		 * that there is no PAC verification function.
352233294Sstas		 */
353233294Sstas		if (signed_pac) {
354233294Sstas		    *signedpath = 1;
355233294Sstas		    ret = _krb5_pac_sign(context, pac, tkt->authtime,
356233294Sstas					 client_principal,
357233294Sstas					 server_sign_key, krbtgt_sign_key, rspac);
358233294Sstas		}
359178825Sdfr		krb5_pac_free(context, pac);
360178825Sdfr
361178825Sdfr		return ret;
362178825Sdfr	    }
363178825Sdfr	}
364178825Sdfr	free_AuthorizationData(&child);
365178825Sdfr    }
366178825Sdfr    return 0;
367178825Sdfr}
368178825Sdfr
369178825Sdfr/*
370178825Sdfr *
371178825Sdfr */
372178825Sdfr
373178825Sdfrstatic krb5_error_code
374233294Sstascheck_tgs_flags(krb5_context context,
375178825Sdfr		krb5_kdc_configuration *config,
376178825Sdfr		KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et)
377178825Sdfr{
378178825Sdfr    KDCOptions f = b->kdc_options;
379233294Sstas
380178825Sdfr    if(f.validate){
381178825Sdfr	if(!tgt->flags.invalid || tgt->starttime == NULL){
382178825Sdfr	    kdc_log(context, config, 0,
383178825Sdfr		    "Bad request to validate ticket");
384178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
385178825Sdfr	}
386178825Sdfr	if(*tgt->starttime > kdc_time){
387178825Sdfr	    kdc_log(context, config, 0,
388178825Sdfr		    "Early request to validate ticket");
389178825Sdfr	    return KRB5KRB_AP_ERR_TKT_NYV;
390178825Sdfr	}
391178825Sdfr	/* XXX  tkt = tgt */
392178825Sdfr	et->flags.invalid = 0;
393178825Sdfr    }else if(tgt->flags.invalid){
394233294Sstas	kdc_log(context, config, 0,
395178825Sdfr		"Ticket-granting ticket has INVALID flag set");
396178825Sdfr	return KRB5KRB_AP_ERR_TKT_INVALID;
397178825Sdfr    }
398178825Sdfr
399178825Sdfr    if(f.forwardable){
400178825Sdfr	if(!tgt->flags.forwardable){
401178825Sdfr	    kdc_log(context, config, 0,
402178825Sdfr		    "Bad request for forwardable ticket");
403178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
404178825Sdfr	}
405178825Sdfr	et->flags.forwardable = 1;
406178825Sdfr    }
407178825Sdfr    if(f.forwarded){
408178825Sdfr	if(!tgt->flags.forwardable){
409178825Sdfr	    kdc_log(context, config, 0,
410178825Sdfr		    "Request to forward non-forwardable ticket");
411178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
412178825Sdfr	}
413178825Sdfr	et->flags.forwarded = 1;
414178825Sdfr	et->caddr = b->addresses;
415178825Sdfr    }
416178825Sdfr    if(tgt->flags.forwarded)
417178825Sdfr	et->flags.forwarded = 1;
418233294Sstas
419178825Sdfr    if(f.proxiable){
420178825Sdfr	if(!tgt->flags.proxiable){
421178825Sdfr	    kdc_log(context, config, 0,
422178825Sdfr		    "Bad request for proxiable ticket");
423178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
424178825Sdfr	}
425178825Sdfr	et->flags.proxiable = 1;
426178825Sdfr    }
427178825Sdfr    if(f.proxy){
428178825Sdfr	if(!tgt->flags.proxiable){
429178825Sdfr	    kdc_log(context, config, 0,
430178825Sdfr		    "Request to proxy non-proxiable ticket");
431178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
432178825Sdfr	}
433178825Sdfr	et->flags.proxy = 1;
434178825Sdfr	et->caddr = b->addresses;
435178825Sdfr    }
436178825Sdfr    if(tgt->flags.proxy)
437178825Sdfr	et->flags.proxy = 1;
438178825Sdfr
439178825Sdfr    if(f.allow_postdate){
440178825Sdfr	if(!tgt->flags.may_postdate){
441178825Sdfr	    kdc_log(context, config, 0,
442178825Sdfr		    "Bad request for post-datable ticket");
443178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
444178825Sdfr	}
445178825Sdfr	et->flags.may_postdate = 1;
446178825Sdfr    }
447178825Sdfr    if(f.postdated){
448178825Sdfr	if(!tgt->flags.may_postdate){
449178825Sdfr	    kdc_log(context, config, 0,
450178825Sdfr		    "Bad request for postdated ticket");
451178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
452178825Sdfr	}
453178825Sdfr	if(b->from)
454178825Sdfr	    *et->starttime = *b->from;
455178825Sdfr	et->flags.postdated = 1;
456178825Sdfr	et->flags.invalid = 1;
457178825Sdfr    }else if(b->from && *b->from > kdc_time + context->max_skew){
458178825Sdfr	kdc_log(context, config, 0, "Ticket cannot be postdated");
459178825Sdfr	return KRB5KDC_ERR_CANNOT_POSTDATE;
460178825Sdfr    }
461178825Sdfr
462178825Sdfr    if(f.renewable){
463233294Sstas	if(!tgt->flags.renewable || tgt->renew_till == NULL){
464178825Sdfr	    kdc_log(context, config, 0,
465178825Sdfr		    "Bad request for renewable ticket");
466178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
467178825Sdfr	}
468178825Sdfr	et->flags.renewable = 1;
469178825Sdfr	ALLOC(et->renew_till);
470178825Sdfr	_kdc_fix_time(&b->rtime);
471178825Sdfr	*et->renew_till = *b->rtime;
472178825Sdfr    }
473178825Sdfr    if(f.renew){
474178825Sdfr	time_t old_life;
475178825Sdfr	if(!tgt->flags.renewable || tgt->renew_till == NULL){
476178825Sdfr	    kdc_log(context, config, 0,
477178825Sdfr		    "Request to renew non-renewable ticket");
478178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
479178825Sdfr	}
480178825Sdfr	old_life = tgt->endtime;
481178825Sdfr	if(tgt->starttime)
482178825Sdfr	    old_life -= *tgt->starttime;
483178825Sdfr	else
484178825Sdfr	    old_life -= tgt->authtime;
485178825Sdfr	et->endtime = *et->starttime + old_life;
486178825Sdfr	if (et->renew_till != NULL)
487178825Sdfr	    et->endtime = min(*et->renew_till, et->endtime);
488233294Sstas    }
489233294Sstas
490178825Sdfr#if 0
491178825Sdfr    /* checks for excess flags */
492178825Sdfr    if(f.request_anonymous && !config->allow_anonymous){
493178825Sdfr	kdc_log(context, config, 0,
494178825Sdfr		"Request for anonymous ticket");
495178825Sdfr	return KRB5KDC_ERR_BADOPTION;
496178825Sdfr    }
497178825Sdfr#endif
498178825Sdfr    return 0;
499178825Sdfr}
500178825Sdfr
501178825Sdfr/*
502233294Sstas * Determine if constrained delegation is allowed from this client to this server
503178825Sdfr */
504178825Sdfr
505178825Sdfrstatic krb5_error_code
506233294Sstascheck_constrained_delegation(krb5_context context,
507178825Sdfr			     krb5_kdc_configuration *config,
508233294Sstas			     HDB *clientdb,
509178825Sdfr			     hdb_entry_ex *client,
510233294Sstas			     hdb_entry_ex *server,
511233294Sstas			     krb5_const_principal target)
512178825Sdfr{
513178825Sdfr    const HDB_Ext_Constrained_delegation_acl *acl;
514178825Sdfr    krb5_error_code ret;
515233294Sstas    size_t i;
516178825Sdfr
517233294Sstas    /*
518233294Sstas     * constrained_delegation (S4U2Proxy) only works within
519233294Sstas     * the same realm. We use the already canonicalized version
520233294Sstas     * of the principals here, while "target" is the principal
521233294Sstas     * provided by the client.
522233294Sstas     */
523233294Sstas    if(!krb5_realm_compare(context, client->entry.principal, server->entry.principal)) {
524233294Sstas	ret = KRB5KDC_ERR_BADOPTION;
525233294Sstas	kdc_log(context, config, 0,
526233294Sstas	    "Bad request for constrained delegation");
527178825Sdfr	return ret;
528178825Sdfr    }
529178825Sdfr
530233294Sstas    if (clientdb->hdb_check_constrained_delegation) {
531233294Sstas	ret = clientdb->hdb_check_constrained_delegation(context, clientdb, client, target);
532233294Sstas	if (ret == 0)
533233294Sstas	    return 0;
534233294Sstas    } else {
535233294Sstas	/* if client delegates to itself, that ok */
536233294Sstas	if (krb5_principal_compare(context, client->entry.principal, server->entry.principal) == TRUE)
537233294Sstas	    return 0;
538233294Sstas
539233294Sstas	ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
540233294Sstas	if (ret) {
541233294Sstas	    krb5_clear_error_message(context);
542233294Sstas	    return ret;
543178825Sdfr	}
544233294Sstas
545233294Sstas	if (acl) {
546233294Sstas	    for (i = 0; i < acl->len; i++) {
547233294Sstas		if (krb5_principal_compare(context, target, &acl->val[i]) == TRUE)
548233294Sstas		    return 0;
549233294Sstas	    }
550233294Sstas	}
551233294Sstas	ret = KRB5KDC_ERR_BADOPTION;
552178825Sdfr    }
553178825Sdfr    kdc_log(context, config, 0,
554178825Sdfr	    "Bad request for constrained delegation");
555233294Sstas    return ret;
556178825Sdfr}
557178825Sdfr
558178825Sdfr/*
559233294Sstas * Determine if s4u2self is allowed from this client to this server
560178825Sdfr *
561233294Sstas * For example, regardless of the principal being impersonated, if the
562233294Sstas * 'client' and 'server' are the same, then it's safe.
563178825Sdfr */
564178825Sdfr
565178825Sdfrstatic krb5_error_code
566233294Sstascheck_s4u2self(krb5_context context,
567233294Sstas	       krb5_kdc_configuration *config,
568233294Sstas	       HDB *clientdb,
569233294Sstas	       hdb_entry_ex *client,
570233294Sstas	       krb5_const_principal server)
571233294Sstas{
572233294Sstas    krb5_error_code ret;
573233294Sstas
574233294Sstas    /* if client does a s4u2self to itself, that ok */
575233294Sstas    if (krb5_principal_compare(context, client->entry.principal, server) == TRUE)
576233294Sstas	return 0;
577233294Sstas
578233294Sstas    if (clientdb->hdb_check_s4u2self) {
579233294Sstas	ret = clientdb->hdb_check_s4u2self(context, clientdb, client, server);
580233294Sstas	if (ret == 0)
581233294Sstas	    return 0;
582233294Sstas    } else {
583233294Sstas	ret = KRB5KDC_ERR_BADOPTION;
584233294Sstas    }
585233294Sstas    return ret;
586233294Sstas}
587233294Sstas
588233294Sstas/*
589233294Sstas *
590233294Sstas */
591233294Sstas
592233294Sstasstatic krb5_error_code
593233294Sstasverify_flags (krb5_context context,
594178825Sdfr	      krb5_kdc_configuration *config,
595178825Sdfr	      const EncTicketPart *et,
596178825Sdfr	      const char *pstr)
597178825Sdfr{
598178825Sdfr    if(et->endtime < kdc_time){
599178825Sdfr	kdc_log(context, config, 0, "Ticket expired (%s)", pstr);
600178825Sdfr	return KRB5KRB_AP_ERR_TKT_EXPIRED;
601178825Sdfr    }
602178825Sdfr    if(et->flags.invalid){
603178825Sdfr	kdc_log(context, config, 0, "Ticket not valid (%s)", pstr);
604178825Sdfr	return KRB5KRB_AP_ERR_TKT_NYV;
605178825Sdfr    }
606178825Sdfr    return 0;
607178825Sdfr}
608178825Sdfr
609178825Sdfr/*
610178825Sdfr *
611178825Sdfr */
612178825Sdfr
613178825Sdfrstatic krb5_error_code
614233294Sstasfix_transited_encoding(krb5_context context,
615178825Sdfr		       krb5_kdc_configuration *config,
616178825Sdfr		       krb5_boolean check_policy,
617233294Sstas		       const TransitedEncoding *tr,
618233294Sstas		       EncTicketPart *et,
619233294Sstas		       const char *client_realm,
620233294Sstas		       const char *server_realm,
621178825Sdfr		       const char *tgt_realm)
622178825Sdfr{
623178825Sdfr    krb5_error_code ret = 0;
624178825Sdfr    char **realms, **tmp;
625233294Sstas    unsigned int num_realms;
626233294Sstas    size_t i;
627178825Sdfr
628178825Sdfr    switch (tr->tr_type) {
629178825Sdfr    case DOMAIN_X500_COMPRESS:
630178825Sdfr	break;
631178825Sdfr    case 0:
632178825Sdfr	/*
633178825Sdfr	 * Allow empty content of type 0 because that is was Microsoft
634178825Sdfr	 * generates in their TGT.
635178825Sdfr	 */
636178825Sdfr	if (tr->contents.length == 0)
637178825Sdfr	    break;
638178825Sdfr	kdc_log(context, config, 0,
639178825Sdfr		"Transited type 0 with non empty content");
640178825Sdfr	return KRB5KDC_ERR_TRTYPE_NOSUPP;
641178825Sdfr    default:
642178825Sdfr	kdc_log(context, config, 0,
643178825Sdfr		"Unknown transited type: %u", tr->tr_type);
644178825Sdfr	return KRB5KDC_ERR_TRTYPE_NOSUPP;
645178825Sdfr    }
646178825Sdfr
647233294Sstas    ret = krb5_domain_x500_decode(context,
648178825Sdfr				  tr->contents,
649233294Sstas				  &realms,
650178825Sdfr				  &num_realms,
651178825Sdfr				  client_realm,
652178825Sdfr				  server_realm);
653178825Sdfr    if(ret){
654178825Sdfr	krb5_warn(context, ret,
655178825Sdfr		  "Decoding transited encoding");
656178825Sdfr	return ret;
657178825Sdfr    }
658178825Sdfr    if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
659178825Sdfr	/* not us, so add the previous realm to transited set */
660233294Sstas	if (num_realms + 1 > UINT_MAX/sizeof(*realms)) {
661178825Sdfr	    ret = ERANGE;
662178825Sdfr	    goto free_realms;
663178825Sdfr	}
664178825Sdfr	tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
665178825Sdfr	if(tmp == NULL){
666178825Sdfr	    ret = ENOMEM;
667178825Sdfr	    goto free_realms;
668178825Sdfr	}
669178825Sdfr	realms = tmp;
670178825Sdfr	realms[num_realms] = strdup(tgt_realm);
671178825Sdfr	if(realms[num_realms] == NULL){
672178825Sdfr	    ret = ENOMEM;
673178825Sdfr	    goto free_realms;
674178825Sdfr	}
675178825Sdfr	num_realms++;
676178825Sdfr    }
677178825Sdfr    if(num_realms == 0) {
678233294Sstas	if(strcmp(client_realm, server_realm))
679178825Sdfr	    kdc_log(context, config, 0,
680178825Sdfr		    "cross-realm %s -> %s", client_realm, server_realm);
681178825Sdfr    } else {
682178825Sdfr	size_t l = 0;
683178825Sdfr	char *rs;
684178825Sdfr	for(i = 0; i < num_realms; i++)
685178825Sdfr	    l += strlen(realms[i]) + 2;
686178825Sdfr	rs = malloc(l);
687178825Sdfr	if(rs != NULL) {
688178825Sdfr	    *rs = '\0';
689178825Sdfr	    for(i = 0; i < num_realms; i++) {
690178825Sdfr		if(i > 0)
691178825Sdfr		    strlcat(rs, ", ", l);
692178825Sdfr		strlcat(rs, realms[i], l);
693178825Sdfr	    }
694178825Sdfr	    kdc_log(context, config, 0,
695178825Sdfr		    "cross-realm %s -> %s via [%s]",
696178825Sdfr		    client_realm, server_realm, rs);
697178825Sdfr	    free(rs);
698178825Sdfr	}
699178825Sdfr    }
700178825Sdfr    if(check_policy) {
701233294Sstas	ret = krb5_check_transited(context, client_realm,
702233294Sstas				   server_realm,
703178825Sdfr				   realms, num_realms, NULL);
704178825Sdfr	if(ret) {
705233294Sstas	    krb5_warn(context, ret, "cross-realm %s -> %s",
706178825Sdfr		      client_realm, server_realm);
707178825Sdfr	    goto free_realms;
708178825Sdfr	}
709178825Sdfr	et->flags.transited_policy_checked = 1;
710178825Sdfr    }
711178825Sdfr    et->transited.tr_type = DOMAIN_X500_COMPRESS;
712178825Sdfr    ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
713178825Sdfr    if(ret)
714178825Sdfr	krb5_warn(context, ret, "Encoding transited encoding");
715178825Sdfr  free_realms:
716178825Sdfr    for(i = 0; i < num_realms; i++)
717178825Sdfr	free(realms[i]);
718178825Sdfr    free(realms);
719178825Sdfr    return ret;
720178825Sdfr}
721178825Sdfr
722178825Sdfr
723178825Sdfrstatic krb5_error_code
724233294Sstastgs_make_reply(krb5_context context,
725178825Sdfr	       krb5_kdc_configuration *config,
726233294Sstas	       KDC_REQ_BODY *b,
727178825Sdfr	       krb5_const_principal tgt_name,
728233294Sstas	       const EncTicketPart *tgt,
729233294Sstas	       const krb5_keyblock *replykey,
730233294Sstas	       int rk_is_subkey,
731178825Sdfr	       const EncryptionKey *serverkey,
732178825Sdfr	       const krb5_keyblock *sessionkey,
733178825Sdfr	       krb5_kvno kvno,
734178825Sdfr	       AuthorizationData *auth_data,
735233294Sstas	       hdb_entry_ex *server,
736233294Sstas	       krb5_principal server_principal,
737233294Sstas	       const char *server_name,
738233294Sstas	       hdb_entry_ex *client,
739233294Sstas	       krb5_principal client_principal,
740178825Sdfr	       hdb_entry_ex *krbtgt,
741178825Sdfr	       krb5_enctype krbtgt_etype,
742233294Sstas	       krb5_principals spp,
743178825Sdfr	       const krb5_data *rspac,
744233294Sstas	       const METHOD_DATA *enc_pa_data,
745178825Sdfr	       const char **e_text,
746178825Sdfr	       krb5_data *reply)
747178825Sdfr{
748178825Sdfr    KDC_REP rep;
749178825Sdfr    EncKDCRepPart ek;
750178825Sdfr    EncTicketPart et;
751178825Sdfr    KDCOptions f = b->kdc_options;
752178825Sdfr    krb5_error_code ret;
753233294Sstas    int is_weak = 0;
754233294Sstas
755178825Sdfr    memset(&rep, 0, sizeof(rep));
756178825Sdfr    memset(&et, 0, sizeof(et));
757178825Sdfr    memset(&ek, 0, sizeof(ek));
758233294Sstas
759178825Sdfr    rep.pvno = 5;
760178825Sdfr    rep.msg_type = krb_tgs_rep;
761178825Sdfr
762178825Sdfr    et.authtime = tgt->authtime;
763178825Sdfr    _kdc_fix_time(&b->till);
764178825Sdfr    et.endtime = min(tgt->endtime, *b->till);
765178825Sdfr    ALLOC(et.starttime);
766178825Sdfr    *et.starttime = kdc_time;
767233294Sstas
768178825Sdfr    ret = check_tgs_flags(context, config, b, tgt, &et);
769178825Sdfr    if(ret)
770178825Sdfr	goto out;
771178825Sdfr
772178825Sdfr    /* We should check the transited encoding if:
773178825Sdfr       1) the request doesn't ask not to be checked
774178825Sdfr       2) globally enforcing a check
775178825Sdfr       3) principal requires checking
776178825Sdfr       4) we allow non-check per-principal, but principal isn't marked as allowing this
777178825Sdfr       5) we don't globally allow this
778178825Sdfr    */
779178825Sdfr
780178825Sdfr#define GLOBAL_FORCE_TRANSITED_CHECK		\
781178825Sdfr    (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
782178825Sdfr#define GLOBAL_ALLOW_PER_PRINCIPAL			\
783178825Sdfr    (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
784178825Sdfr#define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK			\
785178825Sdfr    (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
786178825Sdfr
787178825Sdfr/* these will consult the database in future release */
788178825Sdfr#define PRINCIPAL_FORCE_TRANSITED_CHECK(P)		0
789178825Sdfr#define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P)	0
790178825Sdfr
791233294Sstas    ret = fix_transited_encoding(context, config,
792178825Sdfr				 !f.disable_transited_check ||
793178825Sdfr				 GLOBAL_FORCE_TRANSITED_CHECK ||
794178825Sdfr				 PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
795233294Sstas				 !((GLOBAL_ALLOW_PER_PRINCIPAL &&
796178825Sdfr				    PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
797178825Sdfr				   GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
798178825Sdfr				 &tgt->transited, &et,
799233294Sstas				 krb5_principal_get_realm(context, client_principal),
800233294Sstas				 krb5_principal_get_realm(context, server->entry.principal),
801233294Sstas				 krb5_principal_get_realm(context, krbtgt->entry.principal));
802178825Sdfr    if(ret)
803178825Sdfr	goto out;
804178825Sdfr
805233294Sstas    copy_Realm(&server_principal->realm, &rep.ticket.realm);
806233294Sstas    _krb5_principal2principalname(&rep.ticket.sname, server_principal);
807178825Sdfr    copy_Realm(&tgt_name->realm, &rep.crealm);
808178825Sdfr/*
809178825Sdfr    if (f.request_anonymous)
810178825Sdfr	_kdc_make_anonymous_principalname (&rep.cname);
811178825Sdfr    else */
812178825Sdfr
813178825Sdfr    copy_PrincipalName(&tgt_name->name, &rep.cname);
814178825Sdfr    rep.ticket.tkt_vno = 5;
815178825Sdfr
816178825Sdfr    ek.caddr = et.caddr;
817178825Sdfr    if(et.caddr == NULL)
818178825Sdfr	et.caddr = tgt->caddr;
819178825Sdfr
820178825Sdfr    {
821178825Sdfr	time_t life;
822178825Sdfr	life = et.endtime - *et.starttime;
823178825Sdfr	if(client && client->entry.max_life)
824178825Sdfr	    life = min(life, *client->entry.max_life);
825178825Sdfr	if(server->entry.max_life)
826178825Sdfr	    life = min(life, *server->entry.max_life);
827178825Sdfr	et.endtime = *et.starttime + life;
828178825Sdfr    }
829233294Sstas    if(f.renewable_ok && tgt->flags.renewable &&
830233294Sstas       et.renew_till == NULL && et.endtime < *b->till &&
831233294Sstas       tgt->renew_till != NULL)
832233294Sstas    {
833178825Sdfr	et.flags.renewable = 1;
834178825Sdfr	ALLOC(et.renew_till);
835178825Sdfr	*et.renew_till = *b->till;
836178825Sdfr    }
837178825Sdfr    if(et.renew_till){
838178825Sdfr	time_t renew;
839178825Sdfr	renew = *et.renew_till - et.authtime;
840178825Sdfr	if(client && client->entry.max_renew)
841178825Sdfr	    renew = min(renew, *client->entry.max_renew);
842178825Sdfr	if(server->entry.max_renew)
843178825Sdfr	    renew = min(renew, *server->entry.max_renew);
844178825Sdfr	*et.renew_till = et.authtime + renew;
845178825Sdfr    }
846233294Sstas
847178825Sdfr    if(et.renew_till){
848178825Sdfr	*et.renew_till = min(*et.renew_till, *tgt->renew_till);
849178825Sdfr	*et.starttime = min(*et.starttime, *et.renew_till);
850178825Sdfr	et.endtime = min(et.endtime, *et.renew_till);
851178825Sdfr    }
852233294Sstas
853178825Sdfr    *et.starttime = min(*et.starttime, et.endtime);
854178825Sdfr
855178825Sdfr    if(*et.starttime == et.endtime){
856178825Sdfr	ret = KRB5KDC_ERR_NEVER_VALID;
857178825Sdfr	goto out;
858178825Sdfr    }
859178825Sdfr    if(et.renew_till && et.endtime == *et.renew_till){
860178825Sdfr	free(et.renew_till);
861178825Sdfr	et.renew_till = NULL;
862178825Sdfr	et.flags.renewable = 0;
863178825Sdfr    }
864233294Sstas
865178825Sdfr    et.flags.pre_authent = tgt->flags.pre_authent;
866178825Sdfr    et.flags.hw_authent  = tgt->flags.hw_authent;
867178825Sdfr    et.flags.anonymous   = tgt->flags.anonymous;
868178825Sdfr    et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
869233294Sstas
870233294Sstas    if(rspac->length) {
871233294Sstas	/*
872233294Sstas	 * No not need to filter out the any PAC from the
873233294Sstas	 * auth_data since it's signed by the KDC.
874233294Sstas	 */
875233294Sstas	ret = _kdc_tkt_add_if_relevant_ad(context, &et,
876233294Sstas					  KRB5_AUTHDATA_WIN2K_PAC, rspac);
877233294Sstas	if (ret)
878233294Sstas	    goto out;
879233294Sstas    }
880233294Sstas
881178825Sdfr    if (auth_data) {
882233294Sstas	unsigned int i = 0;
883233294Sstas
884233294Sstas	/* XXX check authdata */
885233294Sstas
886178825Sdfr	if (et.authorization_data == NULL) {
887233294Sstas	    et.authorization_data = calloc(1, sizeof(*et.authorization_data));
888233294Sstas	    if (et.authorization_data == NULL) {
889233294Sstas		ret = ENOMEM;
890233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
891233294Sstas		goto out;
892233294Sstas	    }
893178825Sdfr	}
894233294Sstas	for(i = 0; i < auth_data->len ; i++) {
895233294Sstas	    ret = add_AuthorizationData(et.authorization_data, &auth_data->val[i]);
896233294Sstas	    if (ret) {
897233294Sstas		krb5_set_error_message(context, ret, "malloc: out of memory");
898233294Sstas		goto out;
899233294Sstas	    }
900233294Sstas	}
901178825Sdfr
902178825Sdfr	/* Filter out type KRB5SignedPath */
903178825Sdfr	ret = find_KRB5SignedPath(context, et.authorization_data, NULL);
904178825Sdfr	if (ret == 0) {
905178825Sdfr	    if (et.authorization_data->len == 1) {
906178825Sdfr		free_AuthorizationData(et.authorization_data);
907178825Sdfr		free(et.authorization_data);
908178825Sdfr		et.authorization_data = NULL;
909178825Sdfr	    } else {
910178825Sdfr		AuthorizationData *ad = et.authorization_data;
911178825Sdfr		free_AuthorizationDataElement(&ad->val[ad->len - 1]);
912178825Sdfr		ad->len--;
913178825Sdfr	    }
914178825Sdfr	}
915178825Sdfr    }
916178825Sdfr
917178825Sdfr    ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key);
918178825Sdfr    if (ret)
919178825Sdfr	goto out;
920233294Sstas    et.crealm = tgt_name->realm;
921178825Sdfr    et.cname = tgt_name->name;
922233294Sstas
923178825Sdfr    ek.key = et.key;
924178825Sdfr    /* MIT must have at least one last_req */
925178825Sdfr    ek.last_req.len = 1;
926178825Sdfr    ek.last_req.val = calloc(1, sizeof(*ek.last_req.val));
927178825Sdfr    if (ek.last_req.val == NULL) {
928178825Sdfr	ret = ENOMEM;
929178825Sdfr	goto out;
930178825Sdfr    }
931178825Sdfr    ek.nonce = b->nonce;
932178825Sdfr    ek.flags = et.flags;
933178825Sdfr    ek.authtime = et.authtime;
934178825Sdfr    ek.starttime = et.starttime;
935178825Sdfr    ek.endtime = et.endtime;
936178825Sdfr    ek.renew_till = et.renew_till;
937178825Sdfr    ek.srealm = rep.ticket.realm;
938178825Sdfr    ek.sname = rep.ticket.sname;
939233294Sstas
940233294Sstas    _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime,
941178825Sdfr		       et.endtime, et.renew_till);
942178825Sdfr
943178825Sdfr    /* Don't sign cross realm tickets, they can't be checked anyway */
944178825Sdfr    {
945178825Sdfr	char *r = get_krbtgt_realm(&ek.sname);
946178825Sdfr
947178825Sdfr	if (r == NULL || strcmp(r, ek.srealm) == 0) {
948178825Sdfr	    ret = _kdc_add_KRB5SignedPath(context,
949178825Sdfr					  config,
950178825Sdfr					  krbtgt,
951178825Sdfr					  krbtgt_etype,
952233294Sstas					  client_principal,
953178825Sdfr					  NULL,
954178825Sdfr					  spp,
955178825Sdfr					  &et);
956178825Sdfr	    if (ret)
957178825Sdfr		goto out;
958178825Sdfr	}
959178825Sdfr    }
960178825Sdfr
961233294Sstas    if (enc_pa_data->len) {
962233294Sstas	rep.padata = calloc(1, sizeof(*rep.padata));
963233294Sstas	if (rep.padata == NULL) {
964233294Sstas	    ret = ENOMEM;
965233294Sstas	    goto out;
966233294Sstas	}
967233294Sstas	ret = copy_METHOD_DATA(enc_pa_data, rep.padata);
968233294Sstas	if (ret)
969233294Sstas	    goto out;
970233294Sstas    }
971233294Sstas
972233294Sstas    if (krb5_enctype_valid(context, et.key.keytype) != 0
973233294Sstas	&& _kdc_is_weak_exception(server->entry.principal, et.key.keytype))
974233294Sstas    {
975233294Sstas	krb5_enctype_enable(context, et.key.keytype);
976233294Sstas	is_weak = 1;
977233294Sstas    }
978233294Sstas
979233294Sstas
980178825Sdfr    /* It is somewhat unclear where the etype in the following
981178825Sdfr       encryption should come from. What we have is a session
982178825Sdfr       key in the passed tgt, and a list of preferred etypes
983178825Sdfr       *for the new ticket*. Should we pick the best possible
984178825Sdfr       etype, given the keytype in the tgt, or should we look
985178825Sdfr       at the etype list here as well?  What if the tgt
986178825Sdfr       session key is DES3 and we want a ticket with a (say)
987178825Sdfr       CAST session key. Should the DES3 etype be added to the
988178825Sdfr       etype list, even if we don't want a session key with
989178825Sdfr       DES3? */
990233294Sstas    ret = _kdc_encode_reply(context, config,
991178825Sdfr			    &rep, &et, &ek, et.key.keytype,
992233294Sstas			    kvno,
993233294Sstas			    serverkey, 0, replykey, rk_is_subkey,
994233294Sstas			    e_text, reply);
995233294Sstas    if (is_weak)
996233294Sstas	krb5_enctype_disable(context, et.key.keytype);
997233294Sstas
998178825Sdfrout:
999178825Sdfr    free_TGS_REP(&rep);
1000178825Sdfr    free_TransitedEncoding(&et.transited);
1001178825Sdfr    if(et.starttime)
1002178825Sdfr	free(et.starttime);
1003178825Sdfr    if(et.renew_till)
1004178825Sdfr	free(et.renew_till);
1005178825Sdfr    if(et.authorization_data) {
1006178825Sdfr	free_AuthorizationData(et.authorization_data);
1007178825Sdfr	free(et.authorization_data);
1008178825Sdfr    }
1009178825Sdfr    free_LastReq(&ek.last_req);
1010178825Sdfr    memset(et.key.keyvalue.data, 0, et.key.keyvalue.length);
1011178825Sdfr    free_EncryptionKey(&et.key);
1012178825Sdfr    return ret;
1013178825Sdfr}
1014178825Sdfr
1015178825Sdfrstatic krb5_error_code
1016233294Sstastgs_check_authenticator(krb5_context context,
1017178825Sdfr			krb5_kdc_configuration *config,
1018178825Sdfr	                krb5_auth_context ac,
1019233294Sstas			KDC_REQ_BODY *b,
1020178825Sdfr			const char **e_text,
1021178825Sdfr			krb5_keyblock *key)
1022178825Sdfr{
1023178825Sdfr    krb5_authenticator auth;
1024233294Sstas    size_t len = 0;
1025178825Sdfr    unsigned char *buf;
1026178825Sdfr    size_t buf_size;
1027178825Sdfr    krb5_error_code ret;
1028178825Sdfr    krb5_crypto crypto;
1029233294Sstas
1030178825Sdfr    krb5_auth_con_getauthenticator(context, ac, &auth);
1031178825Sdfr    if(auth->cksum == NULL){
1032178825Sdfr	kdc_log(context, config, 0, "No authenticator in request");
1033178825Sdfr	ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
1034178825Sdfr	goto out;
1035178825Sdfr    }
1036178825Sdfr    /*
1037178825Sdfr     * according to RFC1510 it doesn't need to be keyed,
1038178825Sdfr     * but according to the latest draft it needs to.
1039178825Sdfr     */
1040178825Sdfr    if (
1041178825Sdfr#if 0
1042178825Sdfr!krb5_checksum_is_keyed(context, auth->cksum->cksumtype)
1043178825Sdfr	||
1044178825Sdfr#endif
1045178825Sdfr !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
1046233294Sstas	kdc_log(context, config, 0, "Bad checksum type in authenticator: %d",
1047178825Sdfr		auth->cksum->cksumtype);
1048178825Sdfr	ret =  KRB5KRB_AP_ERR_INAPP_CKSUM;
1049178825Sdfr	goto out;
1050178825Sdfr    }
1051233294Sstas
1052178825Sdfr    /* XXX should not re-encode this */
1053178825Sdfr    ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
1054178825Sdfr    if(ret){
1055233294Sstas	const char *msg = krb5_get_error_message(context, ret);
1056233294Sstas	kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s", msg);
1057233294Sstas	krb5_free_error_message(context, msg);
1058178825Sdfr	goto out;
1059178825Sdfr    }
1060178825Sdfr    if(buf_size != len) {
1061178825Sdfr	free(buf);
1062178825Sdfr	kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
1063178825Sdfr	*e_text = "KDC internal error";
1064178825Sdfr	ret = KRB5KRB_ERR_GENERIC;
1065178825Sdfr	goto out;
1066178825Sdfr    }
1067178825Sdfr    ret = krb5_crypto_init(context, key, 0, &crypto);
1068178825Sdfr    if (ret) {
1069233294Sstas	const char *msg = krb5_get_error_message(context, ret);
1070178825Sdfr	free(buf);
1071233294Sstas	kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
1072233294Sstas	krb5_free_error_message(context, msg);
1073178825Sdfr	goto out;
1074178825Sdfr    }
1075178825Sdfr    ret = krb5_verify_checksum(context,
1076178825Sdfr			       crypto,
1077178825Sdfr			       KRB5_KU_TGS_REQ_AUTH_CKSUM,
1078233294Sstas			       buf,
1079178825Sdfr			       len,
1080178825Sdfr			       auth->cksum);
1081178825Sdfr    free(buf);
1082178825Sdfr    krb5_crypto_destroy(context, crypto);
1083178825Sdfr    if(ret){
1084233294Sstas	const char *msg = krb5_get_error_message(context, ret);
1085178825Sdfr	kdc_log(context, config, 0,
1086233294Sstas		"Failed to verify authenticator checksum: %s", msg);
1087233294Sstas	krb5_free_error_message(context, msg);
1088178825Sdfr    }
1089178825Sdfrout:
1090178825Sdfr    free_Authenticator(auth);
1091178825Sdfr    free(auth);
1092178825Sdfr    return ret;
1093178825Sdfr}
1094178825Sdfr
1095178825Sdfr/*
1096178825Sdfr *
1097178825Sdfr */
1098178825Sdfr
1099178825Sdfrstatic const char *
1100178825Sdfrfind_rpath(krb5_context context, Realm crealm, Realm srealm)
1101178825Sdfr{
1102178825Sdfr    const char *new_realm = krb5_config_get_string(context,
1103178825Sdfr						   NULL,
1104233294Sstas						   "capaths",
1105178825Sdfr						   crealm,
1106178825Sdfr						   srealm,
1107178825Sdfr						   NULL);
1108178825Sdfr    return new_realm;
1109178825Sdfr}
1110178825Sdfr
1111233294Sstas
1112178825Sdfrstatic krb5_boolean
1113233294Sstasneed_referral(krb5_context context, krb5_kdc_configuration *config,
1114233294Sstas	      const KDCOptions * const options, krb5_principal server,
1115233294Sstas	      krb5_realm **realms)
1116178825Sdfr{
1117233294Sstas    const char *name;
1118233294Sstas
1119233294Sstas    if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST)
1120178825Sdfr	return FALSE;
1121233294Sstas
1122233294Sstas    if (server->name.name_string.len == 1)
1123233294Sstas	name = server->name.name_string.val[0];
1124233294Sstas    else if (server->name.name_string.len > 1)
1125233294Sstas	name = server->name.name_string.val[1];
1126233294Sstas    else
1127233294Sstas	return FALSE;
1128233294Sstas
1129233294Sstas    kdc_log(context, config, 0, "Searching referral for %s", name);
1130233294Sstas
1131233294Sstas    return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
1132178825Sdfr}
1133178825Sdfr
1134178825Sdfrstatic krb5_error_code
1135233294Sstastgs_parse_request(krb5_context context,
1136178825Sdfr		  krb5_kdc_configuration *config,
1137178825Sdfr		  KDC_REQ_BODY *b,
1138178825Sdfr		  const PA_DATA *tgs_req,
1139178825Sdfr		  hdb_entry_ex **krbtgt,
1140178825Sdfr		  krb5_enctype *krbtgt_etype,
1141178825Sdfr		  krb5_ticket **ticket,
1142178825Sdfr		  const char **e_text,
1143178825Sdfr		  const char *from,
1144178825Sdfr		  const struct sockaddr *from_addr,
1145178825Sdfr		  time_t **csec,
1146178825Sdfr		  int **cusec,
1147233294Sstas		  AuthorizationData **auth_data,
1148233294Sstas		  krb5_keyblock **replykey,
1149233294Sstas		  int *rk_is_subkey)
1150178825Sdfr{
1151233294Sstas    static char failed[] = "<unparse_name failed>";
1152178825Sdfr    krb5_ap_req ap_req;
1153178825Sdfr    krb5_error_code ret;
1154178825Sdfr    krb5_principal princ;
1155178825Sdfr    krb5_auth_context ac = NULL;
1156178825Sdfr    krb5_flags ap_req_options;
1157178825Sdfr    krb5_flags verify_ap_req_flags;
1158178825Sdfr    krb5_crypto crypto;
1159178825Sdfr    Key *tkey;
1160233294Sstas    krb5_keyblock *subkey = NULL;
1161233294Sstas    unsigned usage;
1162178825Sdfr
1163178825Sdfr    *auth_data = NULL;
1164178825Sdfr    *csec  = NULL;
1165178825Sdfr    *cusec = NULL;
1166233294Sstas    *replykey = NULL;
1167178825Sdfr
1168178825Sdfr    memset(&ap_req, 0, sizeof(ap_req));
1169178825Sdfr    ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
1170178825Sdfr    if(ret){
1171233294Sstas	const char *msg = krb5_get_error_message(context, ret);
1172233294Sstas	kdc_log(context, config, 0, "Failed to decode AP-REQ: %s", msg);
1173233294Sstas	krb5_free_error_message(context, msg);
1174178825Sdfr	goto out;
1175178825Sdfr    }
1176178825Sdfr
1177178825Sdfr    if(!get_krbtgt_realm(&ap_req.ticket.sname)){
1178178825Sdfr	/* XXX check for ticket.sname == req.sname */
1179178825Sdfr	kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
1180178825Sdfr	ret = KRB5KDC_ERR_POLICY; /* ? */
1181178825Sdfr	goto out;
1182178825Sdfr    }
1183233294Sstas
1184178825Sdfr    _krb5_principalname2krb5_principal(context,
1185178825Sdfr				       &princ,
1186178825Sdfr				       ap_req.ticket.sname,
1187178825Sdfr				       ap_req.ticket.realm);
1188178825Sdfr
1189233294Sstas    ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, ap_req.ticket.enc_part.kvno, NULL, krbtgt);
1190233294Sstas
1191233294Sstas    if(ret == HDB_ERR_NOT_FOUND_HERE) {
1192178825Sdfr	char *p;
1193178825Sdfr	ret = krb5_unparse_name(context, princ, &p);
1194178825Sdfr	if (ret != 0)
1195233294Sstas	    p = failed;
1196178825Sdfr	krb5_free_principal(context, princ);
1197233294Sstas	kdc_log(context, config, 5, "Ticket-granting ticket account %s does not have secrets at this KDC, need to proxy", p);
1198233294Sstas	if (ret == 0)
1199233294Sstas	    free(p);
1200233294Sstas	ret = HDB_ERR_NOT_FOUND_HERE;
1201233294Sstas	goto out;
1202233294Sstas    } else if(ret){
1203233294Sstas	const char *msg = krb5_get_error_message(context, ret);
1204233294Sstas	char *p;
1205233294Sstas	ret = krb5_unparse_name(context, princ, &p);
1206233294Sstas	if (ret != 0)
1207233294Sstas	    p = failed;
1208233294Sstas	krb5_free_principal(context, princ);
1209178825Sdfr	kdc_log(context, config, 0,
1210233294Sstas		"Ticket-granting ticket not found in database: %s", msg);
1211233294Sstas	krb5_free_error_message(context, msg);
1212178825Sdfr	if (ret == 0)
1213178825Sdfr	    free(p);
1214178825Sdfr	ret = KRB5KRB_AP_ERR_NOT_US;
1215178825Sdfr	goto out;
1216178825Sdfr    }
1217233294Sstas
1218233294Sstas    if(ap_req.ticket.enc_part.kvno &&
1219178825Sdfr       *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){
1220178825Sdfr	char *p;
1221178825Sdfr
1222178825Sdfr	ret = krb5_unparse_name (context, princ, &p);
1223178825Sdfr	krb5_free_principal(context, princ);
1224178825Sdfr	if (ret != 0)
1225233294Sstas	    p = failed;
1226178825Sdfr	kdc_log(context, config, 0,
1227233294Sstas		"Ticket kvno = %d, DB kvno = %d (%s)",
1228178825Sdfr		*ap_req.ticket.enc_part.kvno,
1229178825Sdfr		(*krbtgt)->entry.kvno,
1230178825Sdfr		p);
1231178825Sdfr	if (ret == 0)
1232178825Sdfr	    free (p);
1233178825Sdfr	ret = KRB5KRB_AP_ERR_BADKEYVER;
1234178825Sdfr	goto out;
1235178825Sdfr    }
1236178825Sdfr
1237178825Sdfr    *krbtgt_etype = ap_req.ticket.enc_part.etype;
1238178825Sdfr
1239233294Sstas    ret = hdb_enctype2key(context, &(*krbtgt)->entry,
1240178825Sdfr			  ap_req.ticket.enc_part.etype, &tkey);
1241178825Sdfr    if(ret){
1242178825Sdfr	char *str = NULL, *p = NULL;
1243178825Sdfr
1244178825Sdfr	krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
1245178825Sdfr	krb5_unparse_name(context, princ, &p);
1246178825Sdfr 	kdc_log(context, config, 0,
1247178825Sdfr		"No server key with enctype %s found for %s",
1248178825Sdfr		str ? str : "<unknown enctype>",
1249178825Sdfr		p ? p : "<unparse_name failed>");
1250178825Sdfr	free(str);
1251178825Sdfr	free(p);
1252178825Sdfr	ret = KRB5KRB_AP_ERR_BADKEYVER;
1253178825Sdfr	goto out;
1254178825Sdfr    }
1255233294Sstas
1256178825Sdfr    if (b->kdc_options.validate)
1257178825Sdfr	verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
1258178825Sdfr    else
1259178825Sdfr	verify_ap_req_flags = 0;
1260178825Sdfr
1261178825Sdfr    ret = krb5_verify_ap_req2(context,
1262178825Sdfr			      &ac,
1263178825Sdfr			      &ap_req,
1264178825Sdfr			      princ,
1265178825Sdfr			      &tkey->key,
1266178825Sdfr			      verify_ap_req_flags,
1267178825Sdfr			      &ap_req_options,
1268178825Sdfr			      ticket,
1269178825Sdfr			      KRB5_KU_TGS_REQ_AUTH);
1270233294Sstas
1271178825Sdfr    krb5_free_principal(context, princ);
1272178825Sdfr    if(ret) {
1273233294Sstas	const char *msg = krb5_get_error_message(context, ret);
1274233294Sstas	kdc_log(context, config, 0, "Failed to verify AP-REQ: %s", msg);
1275233294Sstas	krb5_free_error_message(context, msg);
1276178825Sdfr	goto out;
1277178825Sdfr    }
1278178825Sdfr
1279178825Sdfr    {
1280178825Sdfr	krb5_authenticator auth;
1281178825Sdfr
1282178825Sdfr	ret = krb5_auth_con_getauthenticator(context, ac, &auth);
1283178825Sdfr	if (ret == 0) {
1284178825Sdfr	    *csec   = malloc(sizeof(**csec));
1285178825Sdfr	    if (*csec == NULL) {
1286178825Sdfr		krb5_free_authenticator(context, &auth);
1287178825Sdfr		kdc_log(context, config, 0, "malloc failed");
1288178825Sdfr		goto out;
1289178825Sdfr	    }
1290178825Sdfr	    **csec  = auth->ctime;
1291178825Sdfr	    *cusec  = malloc(sizeof(**cusec));
1292178825Sdfr	    if (*cusec == NULL) {
1293178825Sdfr		krb5_free_authenticator(context, &auth);
1294178825Sdfr		kdc_log(context, config, 0, "malloc failed");
1295178825Sdfr		goto out;
1296178825Sdfr	    }
1297178825Sdfr	    **cusec  = auth->cusec;
1298178825Sdfr	    krb5_free_authenticator(context, &auth);
1299178825Sdfr	}
1300178825Sdfr    }
1301178825Sdfr
1302233294Sstas    ret = tgs_check_authenticator(context, config,
1303178825Sdfr				  ac, b, e_text, &(*ticket)->ticket.key);
1304178825Sdfr    if (ret) {
1305178825Sdfr	krb5_auth_con_free(context, ac);
1306178825Sdfr	goto out;
1307178825Sdfr    }
1308178825Sdfr
1309233294Sstas    usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
1310233294Sstas    *rk_is_subkey = 1;
1311178825Sdfr
1312233294Sstas    ret = krb5_auth_con_getremotesubkey(context, ac, &subkey);
1313233294Sstas    if(ret){
1314233294Sstas	const char *msg = krb5_get_error_message(context, ret);
1315233294Sstas	krb5_auth_con_free(context, ac);
1316233294Sstas	kdc_log(context, config, 0, "Failed to get remote subkey: %s", msg);
1317233294Sstas	krb5_free_error_message(context, msg);
1318233294Sstas	goto out;
1319233294Sstas    }
1320233294Sstas    if(subkey == NULL){
1321233294Sstas	usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
1322233294Sstas	*rk_is_subkey = 0;
1323233294Sstas
1324233294Sstas	ret = krb5_auth_con_getkey(context, ac, &subkey);
1325233294Sstas	if(ret) {
1326233294Sstas	    const char *msg = krb5_get_error_message(context, ret);
1327178825Sdfr	    krb5_auth_con_free(context, ac);
1328233294Sstas	    kdc_log(context, config, 0, "Failed to get session key: %s", msg);
1329233294Sstas	    krb5_free_error_message(context, msg);
1330178825Sdfr	    goto out;
1331178825Sdfr	}
1332233294Sstas    }
1333233294Sstas    if(subkey == NULL){
1334233294Sstas	krb5_auth_con_free(context, ac);
1335233294Sstas	kdc_log(context, config, 0,
1336233294Sstas		"Failed to get key for enc-authorization-data");
1337233294Sstas	ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1338233294Sstas	goto out;
1339233294Sstas    }
1340233294Sstas
1341233294Sstas    *replykey = subkey;
1342233294Sstas
1343233294Sstas    if (b->enc_authorization_data) {
1344233294Sstas	krb5_data ad;
1345233294Sstas
1346178825Sdfr	ret = krb5_crypto_init(context, subkey, 0, &crypto);
1347178825Sdfr	if (ret) {
1348233294Sstas	    const char *msg = krb5_get_error_message(context, ret);
1349178825Sdfr	    krb5_auth_con_free(context, ac);
1350233294Sstas	    kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
1351233294Sstas	    krb5_free_error_message(context, msg);
1352178825Sdfr	    goto out;
1353178825Sdfr	}
1354178825Sdfr	ret = krb5_decrypt_EncryptedData (context,
1355178825Sdfr					  crypto,
1356178825Sdfr					  usage,
1357178825Sdfr					  b->enc_authorization_data,
1358178825Sdfr					  &ad);
1359178825Sdfr	krb5_crypto_destroy(context, crypto);
1360178825Sdfr	if(ret){
1361178825Sdfr	    krb5_auth_con_free(context, ac);
1362233294Sstas	    kdc_log(context, config, 0,
1363178825Sdfr		    "Failed to decrypt enc-authorization-data");
1364178825Sdfr	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1365178825Sdfr	    goto out;
1366178825Sdfr	}
1367178825Sdfr	ALLOC(*auth_data);
1368178825Sdfr	if (*auth_data == NULL) {
1369178825Sdfr	    krb5_auth_con_free(context, ac);
1370178825Sdfr	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1371178825Sdfr	    goto out;
1372178825Sdfr	}
1373178825Sdfr	ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
1374178825Sdfr	if(ret){
1375178825Sdfr	    krb5_auth_con_free(context, ac);
1376178825Sdfr	    free(*auth_data);
1377178825Sdfr	    *auth_data = NULL;
1378178825Sdfr	    kdc_log(context, config, 0, "Failed to decode authorization data");
1379178825Sdfr	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1380178825Sdfr	    goto out;
1381178825Sdfr	}
1382178825Sdfr    }
1383178825Sdfr
1384178825Sdfr    krb5_auth_con_free(context, ac);
1385233294Sstas
1386178825Sdfrout:
1387178825Sdfr    free_AP_REQ(&ap_req);
1388233294Sstas
1389178825Sdfr    return ret;
1390178825Sdfr}
1391178825Sdfr
1392178825Sdfrstatic krb5_error_code
1393233294Sstasbuild_server_referral(krb5_context context,
1394233294Sstas		      krb5_kdc_configuration *config,
1395233294Sstas		      krb5_crypto session,
1396233294Sstas		      krb5_const_realm referred_realm,
1397233294Sstas		      const PrincipalName *true_principal_name,
1398233294Sstas		      const PrincipalName *requested_principal,
1399233294Sstas		      krb5_data *outdata)
1400233294Sstas{
1401233294Sstas    PA_ServerReferralData ref;
1402233294Sstas    krb5_error_code ret;
1403233294Sstas    EncryptedData ed;
1404233294Sstas    krb5_data data;
1405233294Sstas    size_t size = 0;
1406233294Sstas
1407233294Sstas    memset(&ref, 0, sizeof(ref));
1408233294Sstas
1409233294Sstas    if (referred_realm) {
1410233294Sstas	ALLOC(ref.referred_realm);
1411233294Sstas	if (ref.referred_realm == NULL)
1412233294Sstas	    goto eout;
1413233294Sstas	*ref.referred_realm = strdup(referred_realm);
1414233294Sstas	if (*ref.referred_realm == NULL)
1415233294Sstas	    goto eout;
1416233294Sstas    }
1417233294Sstas    if (true_principal_name) {
1418233294Sstas	ALLOC(ref.true_principal_name);
1419233294Sstas	if (ref.true_principal_name == NULL)
1420233294Sstas	    goto eout;
1421233294Sstas	ret = copy_PrincipalName(true_principal_name, ref.true_principal_name);
1422233294Sstas	if (ret)
1423233294Sstas	    goto eout;
1424233294Sstas    }
1425233294Sstas    if (requested_principal) {
1426233294Sstas	ALLOC(ref.requested_principal_name);
1427233294Sstas	if (ref.requested_principal_name == NULL)
1428233294Sstas	    goto eout;
1429233294Sstas	ret = copy_PrincipalName(requested_principal,
1430233294Sstas				 ref.requested_principal_name);
1431233294Sstas	if (ret)
1432233294Sstas	    goto eout;
1433233294Sstas    }
1434233294Sstas
1435233294Sstas    ASN1_MALLOC_ENCODE(PA_ServerReferralData,
1436233294Sstas		       data.data, data.length,
1437233294Sstas		       &ref, &size, ret);
1438233294Sstas    free_PA_ServerReferralData(&ref);
1439233294Sstas    if (ret)
1440233294Sstas	return ret;
1441233294Sstas    if (data.length != size)
1442233294Sstas	krb5_abortx(context, "internal asn.1 encoder error");
1443233294Sstas
1444233294Sstas    ret = krb5_encrypt_EncryptedData(context, session,
1445233294Sstas				     KRB5_KU_PA_SERVER_REFERRAL,
1446233294Sstas				     data.data, data.length,
1447233294Sstas				     0 /* kvno */, &ed);
1448233294Sstas    free(data.data);
1449233294Sstas    if (ret)
1450233294Sstas	return ret;
1451233294Sstas
1452233294Sstas    ASN1_MALLOC_ENCODE(EncryptedData,
1453233294Sstas		       outdata->data, outdata->length,
1454233294Sstas		       &ed, &size, ret);
1455233294Sstas    free_EncryptedData(&ed);
1456233294Sstas    if (ret)
1457233294Sstas	return ret;
1458233294Sstas    if (outdata->length != size)
1459233294Sstas	krb5_abortx(context, "internal asn.1 encoder error");
1460233294Sstas
1461233294Sstas    return 0;
1462233294Sstaseout:
1463233294Sstas    free_PA_ServerReferralData(&ref);
1464233294Sstas    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1465233294Sstas    return ENOMEM;
1466233294Sstas}
1467233294Sstas
1468233294Sstasstatic krb5_error_code
1469233294Sstastgs_build_reply(krb5_context context,
1470178825Sdfr		krb5_kdc_configuration *config,
1471233294Sstas		KDC_REQ *req,
1472178825Sdfr		KDC_REQ_BODY *b,
1473178825Sdfr		hdb_entry_ex *krbtgt,
1474178825Sdfr		krb5_enctype krbtgt_etype,
1475233294Sstas		const krb5_keyblock *replykey,
1476233294Sstas		int rk_is_subkey,
1477178825Sdfr		krb5_ticket *ticket,
1478178825Sdfr		krb5_data *reply,
1479178825Sdfr		const char *from,
1480178825Sdfr		const char **e_text,
1481233294Sstas		AuthorizationData **auth_data,
1482233294Sstas		const struct sockaddr *from_addr)
1483178825Sdfr{
1484178825Sdfr    krb5_error_code ret;
1485233294Sstas    krb5_principal cp = NULL, sp = NULL, rsp = NULL, tp = NULL, dp = NULL;
1486233294Sstas    krb5_principal krbtgt_principal = NULL;
1487233294Sstas    char *spn = NULL, *cpn = NULL, *tpn = NULL, *dpn = NULL;
1488233294Sstas    hdb_entry_ex *server = NULL, *client = NULL, *s4u2self_impersonated_client = NULL;
1489233294Sstas    HDB *clientdb, *s4u2self_impersonated_clientdb;
1490233294Sstas    krb5_realm ref_realm = NULL;
1491178825Sdfr    EncTicketPart *tgt = &ticket->ticket;
1492233294Sstas    krb5_principals spp = NULL;
1493178825Sdfr    const EncryptionKey *ekey;
1494178825Sdfr    krb5_keyblock sessionkey;
1495178825Sdfr    krb5_kvno kvno;
1496178825Sdfr    krb5_data rspac;
1497178825Sdfr
1498233294Sstas    hdb_entry_ex *krbtgt_out = NULL;
1499233294Sstas
1500233294Sstas    METHOD_DATA enc_pa_data;
1501233294Sstas
1502178825Sdfr    PrincipalName *s;
1503178825Sdfr    Realm r;
1504178825Sdfr    int nloop = 0;
1505178825Sdfr    EncTicketPart adtkt;
1506178825Sdfr    char opt_str[128];
1507233294Sstas    int signedpath = 0;
1508178825Sdfr
1509233294Sstas    Key *tkey_check;
1510233294Sstas    Key *tkey_sign;
1511233294Sstas    int flags = HDB_F_FOR_TGS_REQ;
1512233294Sstas
1513178825Sdfr    memset(&sessionkey, 0, sizeof(sessionkey));
1514178825Sdfr    memset(&adtkt, 0, sizeof(adtkt));
1515178825Sdfr    krb5_data_zero(&rspac);
1516233294Sstas    memset(&enc_pa_data, 0, sizeof(enc_pa_data));
1517178825Sdfr
1518178825Sdfr    s = b->sname;
1519178825Sdfr    r = b->realm;
1520178825Sdfr
1521233294Sstas    /*
1522233294Sstas     * Always to do CANON, see comment below about returned server principal (rsp).
1523233294Sstas     */
1524233294Sstas    flags |= HDB_F_CANON;
1525233294Sstas
1526178825Sdfr    if(b->kdc_options.enc_tkt_in_skey){
1527178825Sdfr	Ticket *t;
1528178825Sdfr	hdb_entry_ex *uu;
1529178825Sdfr	krb5_principal p;
1530178825Sdfr	Key *uukey;
1531233294Sstas
1532233294Sstas	if(b->additional_tickets == NULL ||
1533178825Sdfr	   b->additional_tickets->len == 0){
1534178825Sdfr	    ret = KRB5KDC_ERR_BADOPTION; /* ? */
1535178825Sdfr	    kdc_log(context, config, 0,
1536178825Sdfr		    "No second ticket present in request");
1537178825Sdfr	    goto out;
1538178825Sdfr	}
1539178825Sdfr	t = &b->additional_tickets->val[0];
1540178825Sdfr	if(!get_krbtgt_realm(&t->sname)){
1541178825Sdfr	    kdc_log(context, config, 0,
1542178825Sdfr		    "Additional ticket is not a ticket-granting ticket");
1543178825Sdfr	    ret = KRB5KDC_ERR_POLICY;
1544178825Sdfr	    goto out;
1545178825Sdfr	}
1546178825Sdfr	_krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
1547233294Sstas	ret = _kdc_db_fetch(context, config, p,
1548233294Sstas			    HDB_F_GET_KRBTGT, t->enc_part.kvno,
1549178825Sdfr			    NULL, &uu);
1550178825Sdfr	krb5_free_principal(context, p);
1551178825Sdfr	if(ret){
1552178825Sdfr	    if (ret == HDB_ERR_NOENTRY)
1553178825Sdfr		ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1554178825Sdfr	    goto out;
1555178825Sdfr	}
1556233294Sstas	ret = hdb_enctype2key(context, &uu->entry,
1557178825Sdfr			      t->enc_part.etype, &uukey);
1558178825Sdfr	if(ret){
1559178825Sdfr	    _kdc_free_ent(context, uu);
1560178825Sdfr	    ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1561178825Sdfr	    goto out;
1562178825Sdfr	}
1563178825Sdfr	ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
1564178825Sdfr	_kdc_free_ent(context, uu);
1565178825Sdfr	if(ret)
1566178825Sdfr	    goto out;
1567178825Sdfr
1568178825Sdfr	ret = verify_flags(context, config, &adtkt, spn);
1569178825Sdfr	if (ret)
1570178825Sdfr	    goto out;
1571178825Sdfr
1572178825Sdfr	s = &adtkt.cname;
1573178825Sdfr	r = adtkt.crealm;
1574178825Sdfr    }
1575178825Sdfr
1576178825Sdfr    _krb5_principalname2krb5_principal(context, &sp, *s, r);
1577233294Sstas    ret = krb5_unparse_name(context, sp, &spn);
1578178825Sdfr    if (ret)
1579178825Sdfr	goto out;
1580178825Sdfr    _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
1581178825Sdfr    ret = krb5_unparse_name(context, cp, &cpn);
1582178825Sdfr    if (ret)
1583178825Sdfr	goto out;
1584178825Sdfr    unparse_flags (KDCOptions2int(b->kdc_options),
1585178825Sdfr		   asn1_KDCOptions_units(),
1586178825Sdfr		   opt_str, sizeof(opt_str));
1587178825Sdfr    if(*opt_str)
1588178825Sdfr	kdc_log(context, config, 0,
1589233294Sstas		"TGS-REQ %s from %s for %s [%s]",
1590178825Sdfr		cpn, from, spn, opt_str);
1591178825Sdfr    else
1592178825Sdfr	kdc_log(context, config, 0,
1593178825Sdfr		"TGS-REQ %s from %s for %s", cpn, from, spn);
1594178825Sdfr
1595178825Sdfr    /*
1596178825Sdfr     * Fetch server
1597178825Sdfr     */
1598178825Sdfr
1599178825Sdfrserver_lookup:
1600233294Sstas    ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER | flags,
1601233294Sstas			NULL, NULL, &server);
1602178825Sdfr
1603233294Sstas    if(ret == HDB_ERR_NOT_FOUND_HERE) {
1604233294Sstas	kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", sp);
1605233294Sstas	goto out;
1606233294Sstas    } else if(ret){
1607233294Sstas	const char *new_rlm, *msg;
1608178825Sdfr	Realm req_rlm;
1609178825Sdfr	krb5_realm *realms;
1610178825Sdfr
1611178825Sdfr	if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
1612178825Sdfr	    if(nloop++ < 2) {
1613178825Sdfr		new_rlm = find_rpath(context, tgt->crealm, req_rlm);
1614178825Sdfr		if(new_rlm) {
1615178825Sdfr		    kdc_log(context, config, 5, "krbtgt for realm %s "
1616233294Sstas			    "not found, trying %s",
1617178825Sdfr			    req_rlm, new_rlm);
1618178825Sdfr		    krb5_free_principal(context, sp);
1619178825Sdfr		    free(spn);
1620233294Sstas		    krb5_make_principal(context, &sp, r,
1621178825Sdfr					KRB5_TGS_NAME, new_rlm, NULL);
1622233294Sstas		    ret = krb5_unparse_name(context, sp, &spn);
1623178825Sdfr		    if (ret)
1624178825Sdfr			goto out;
1625233294Sstas
1626233294Sstas		    if (ref_realm)
1627233294Sstas			free(ref_realm);
1628233294Sstas		    ref_realm = strdup(new_rlm);
1629178825Sdfr		    goto server_lookup;
1630178825Sdfr		}
1631178825Sdfr	    }
1632233294Sstas	} else if(need_referral(context, config, &b->kdc_options, sp, &realms)) {
1633178825Sdfr	    if (strcmp(realms[0], sp->realm) != 0) {
1634178825Sdfr		kdc_log(context, config, 5,
1635178825Sdfr			"Returning a referral to realm %s for "
1636178825Sdfr			"server %s that was not found",
1637178825Sdfr			realms[0], spn);
1638178825Sdfr		krb5_free_principal(context, sp);
1639178825Sdfr		free(spn);
1640178825Sdfr		krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1641178825Sdfr				    realms[0], NULL);
1642178825Sdfr		ret = krb5_unparse_name(context, sp, &spn);
1643178825Sdfr		if (ret)
1644178825Sdfr		    goto out;
1645233294Sstas
1646233294Sstas		if (ref_realm)
1647233294Sstas		    free(ref_realm);
1648233294Sstas		ref_realm = strdup(realms[0]);
1649233294Sstas
1650178825Sdfr		krb5_free_host_realm(context, realms);
1651178825Sdfr		goto server_lookup;
1652178825Sdfr	    }
1653178825Sdfr	    krb5_free_host_realm(context, realms);
1654178825Sdfr	}
1655233294Sstas	msg = krb5_get_error_message(context, ret);
1656178825Sdfr	kdc_log(context, config, 0,
1657233294Sstas		"Server not found in database: %s: %s", spn, msg);
1658233294Sstas	krb5_free_error_message(context, msg);
1659178825Sdfr	if (ret == HDB_ERR_NOENTRY)
1660178825Sdfr	    ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1661178825Sdfr	goto out;
1662178825Sdfr    }
1663178825Sdfr
1664233294Sstas    /* the name returned to the client depend on what was asked for,
1665233294Sstas     * return canonical name if kdc_options.canonicalize was set, the
1666233294Sstas     * client wants the true name of the principal, if not it just
1667233294Sstas     * wants the name its asked for.
1668233294Sstas     */
1669233294Sstas
1670233294Sstas    if (b->kdc_options.canonicalize)
1671233294Sstas	rsp = server->entry.principal;
1672233294Sstas    else
1673233294Sstas	rsp = sp;
1674233294Sstas
1675233294Sstas
1676233294Sstas    /*
1677233294Sstas     * Select enctype, return key and kvno.
1678233294Sstas     */
1679233294Sstas
1680233294Sstas    {
1681233294Sstas	krb5_enctype etype;
1682233294Sstas
1683233294Sstas	if(b->kdc_options.enc_tkt_in_skey) {
1684233294Sstas	    size_t i;
1685233294Sstas	    ekey = &adtkt.key;
1686233294Sstas	    for(i = 0; i < b->etype.len; i++)
1687233294Sstas		if (b->etype.val[i] == adtkt.key.keytype)
1688233294Sstas		    break;
1689233294Sstas	    if(i == b->etype.len) {
1690233294Sstas		kdc_log(context, config, 0,
1691233294Sstas			"Addition ticket have not matching etypes");
1692233294Sstas		krb5_clear_error_message(context);
1693233294Sstas		ret = KRB5KDC_ERR_ETYPE_NOSUPP;
1694233294Sstas		goto out;
1695233294Sstas	    }
1696233294Sstas	    etype = b->etype.val[i];
1697233294Sstas	    kvno = 0;
1698233294Sstas	} else {
1699233294Sstas	    Key *skey;
1700233294Sstas
1701233294Sstas	    ret = _kdc_find_etype(context,
1702234027Sstas				  krb5_principal_is_krbtgt(context, sp) ?
1703234027Sstas				  config->tgt_use_strongest_session_key :
1704234027Sstas				  config->svc_use_strongest_session_key, FALSE,
1705233294Sstas				  server, b->etype.val, b->etype.len, NULL,
1706233294Sstas				  &skey);
1707233294Sstas	    if(ret) {
1708233294Sstas		kdc_log(context, config, 0,
1709233294Sstas			"Server (%s) has no support for etypes", spn);
1710233294Sstas		goto out;
1711233294Sstas	    }
1712233294Sstas	    ekey = &skey->key;
1713233294Sstas	    etype = skey->key.keytype;
1714233294Sstas	    kvno = server->entry.kvno;
1715233294Sstas	}
1716233294Sstas
1717233294Sstas	ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
1718233294Sstas	if (ret)
1719233294Sstas	    goto out;
1720233294Sstas    }
1721233294Sstas
1722233294Sstas    /*
1723233294Sstas     * Check that service is in the same realm as the krbtgt. If it's
1724233294Sstas     * not the same, it's someone that is using a uni-directional trust
1725233294Sstas     * backward.
1726233294Sstas     */
1727233294Sstas
1728233294Sstas    /*
1729233294Sstas     * Validate authoriation data
1730233294Sstas     */
1731233294Sstas
1732233294Sstas    ret = hdb_enctype2key(context, &krbtgt->entry,
1733233294Sstas			  krbtgt_etype, &tkey_check);
1734178825Sdfr    if(ret) {
1735233294Sstas	kdc_log(context, config, 0,
1736233294Sstas		    "Failed to find key for krbtgt PAC check");
1737233294Sstas	goto out;
1738233294Sstas    }
1739178825Sdfr
1740233294Sstas    /* Now refetch the primary krbtgt, and get the current kvno (the
1741233294Sstas     * sign check may have been on an old kvno, and the server may
1742233294Sstas     * have been an incoming trust) */
1743233294Sstas    ret = krb5_make_principal(context, &krbtgt_principal,
1744233294Sstas			      krb5_principal_get_comp_string(context,
1745233294Sstas							     krbtgt->entry.principal,
1746233294Sstas							     1),
1747233294Sstas			      KRB5_TGS_NAME,
1748233294Sstas			      krb5_principal_get_comp_string(context,
1749233294Sstas							     krbtgt->entry.principal,
1750233294Sstas							     1), NULL);
1751233294Sstas    if(ret) {
1752233294Sstas	kdc_log(context, config, 0,
1753233294Sstas		    "Failed to generate krbtgt principal");
1754233294Sstas	goto out;
1755233294Sstas    }
1756233294Sstas
1757233294Sstas    ret = _kdc_db_fetch(context, config, krbtgt_principal, HDB_F_GET_KRBTGT, NULL, NULL, &krbtgt_out);
1758233294Sstas    krb5_free_principal(context, krbtgt_principal);
1759233294Sstas    if (ret) {
1760233294Sstas	krb5_error_code ret2;
1761233294Sstas	char *ktpn, *ktpn2;
1762233294Sstas	ret = krb5_unparse_name(context, krbtgt->entry.principal, &ktpn);
1763233294Sstas	ret2 = krb5_unparse_name(context, krbtgt_principal, &ktpn2);
1764233294Sstas	kdc_log(context, config, 0,
1765233294Sstas		"Request with wrong krbtgt: %s, %s not found in our database",
1766233294Sstas		(ret == 0) ? ktpn : "<unknown>", (ret2 == 0) ? ktpn2 : "<unknown>");
1767233294Sstas	if(ret == 0)
1768233294Sstas	    free(ktpn);
1769233294Sstas	if(ret2 == 0)
1770233294Sstas	    free(ktpn2);
1771233294Sstas	ret = KRB5KRB_AP_ERR_NOT_US;
1772233294Sstas	goto out;
1773233294Sstas    }
1774233294Sstas
1775233294Sstas    /* The first realm is the realm of the service, the second is
1776233294Sstas     * krbtgt/<this>/@REALM component of the krbtgt DN the request was
1777233294Sstas     * encrypted to.  The redirection via the krbtgt_out entry allows
1778233294Sstas     * the DB to possibly correct the case of the realm (Samba4 does
1779233294Sstas     * this) before the strcmp() */
1780233294Sstas    if (strcmp(krb5_principal_get_realm(context, server->entry.principal),
1781233294Sstas	       krb5_principal_get_realm(context, krbtgt_out->entry.principal)) != 0) {
1782233294Sstas	char *ktpn;
1783233294Sstas	ret = krb5_unparse_name(context, krbtgt_out->entry.principal, &ktpn);
1784233294Sstas	kdc_log(context, config, 0,
1785233294Sstas		"Request with wrong krbtgt: %s",
1786233294Sstas		(ret == 0) ? ktpn : "<unknown>");
1787233294Sstas	if(ret == 0)
1788233294Sstas	    free(ktpn);
1789233294Sstas	ret = KRB5KRB_AP_ERR_NOT_US;
1790233294Sstas    }
1791233294Sstas
1792233294Sstas    ret = hdb_enctype2key(context, &krbtgt_out->entry,
1793233294Sstas			  krbtgt_etype, &tkey_sign);
1794233294Sstas    if(ret) {
1795233294Sstas	kdc_log(context, config, 0,
1796233294Sstas		    "Failed to find key for krbtgt PAC signature");
1797233294Sstas	goto out;
1798233294Sstas    }
1799233294Sstas
1800233294Sstas    ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | flags,
1801233294Sstas			NULL, &clientdb, &client);
1802233294Sstas    if(ret == HDB_ERR_NOT_FOUND_HERE) {
1803233294Sstas	/* This is OK, we are just trying to find out if they have
1804233294Sstas	 * been disabled or deleted in the meantime, missing secrets
1805233294Sstas	 * is OK */
1806233294Sstas    } else if(ret){
1807233294Sstas	const char *krbtgt_realm, *msg;
1808233294Sstas
1809178825Sdfr	/*
1810178825Sdfr	 * If the client belongs to the same realm as our krbtgt, it
1811178825Sdfr	 * should exist in the local database.
1812178825Sdfr	 *
1813178825Sdfr	 */
1814178825Sdfr
1815233294Sstas	krbtgt_realm = krb5_principal_get_realm(context, krbtgt_out->entry.principal);
1816178825Sdfr
1817178825Sdfr	if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
1818178825Sdfr	    if (ret == HDB_ERR_NOENTRY)
1819178825Sdfr		ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1820178825Sdfr	    kdc_log(context, config, 1, "Client no longer in database: %s",
1821178825Sdfr		    cpn);
1822178825Sdfr	    goto out;
1823178825Sdfr	}
1824178825Sdfr
1825233294Sstas	msg = krb5_get_error_message(context, ret);
1826233294Sstas	kdc_log(context, config, 1, "Client not found in database: %s", msg);
1827233294Sstas	krb5_free_error_message(context, msg);
1828178825Sdfr    }
1829233294Sstas
1830233294Sstas    ret = check_PAC(context, config, cp, NULL,
1831233294Sstas		    client, server, krbtgt,
1832233294Sstas		    &tkey_check->key, &tkey_check->key,
1833233294Sstas		    ekey, &tkey_sign->key,
1834233294Sstas		    tgt, &rspac, &signedpath);
1835233294Sstas    if (ret) {
1836233294Sstas	const char *msg = krb5_get_error_message(context, ret);
1837178825Sdfr	kdc_log(context, config, 0,
1838233294Sstas		"Verify PAC failed for %s (%s) from %s with %s",
1839233294Sstas		spn, cpn, from, msg);
1840233294Sstas	krb5_free_error_message(context, msg);
1841178825Sdfr	goto out;
1842178825Sdfr    }
1843178825Sdfr
1844233294Sstas    /* also check the krbtgt for signature */
1845233294Sstas    ret = check_KRB5SignedPath(context,
1846233294Sstas			       config,
1847233294Sstas			       krbtgt,
1848233294Sstas			       cp,
1849233294Sstas			       tgt,
1850233294Sstas			       &spp,
1851233294Sstas			       &signedpath);
1852233294Sstas    if (ret) {
1853233294Sstas	const char *msg = krb5_get_error_message(context, ret);
1854233294Sstas	kdc_log(context, config, 0,
1855233294Sstas		"KRB5SignedPath check failed for %s (%s) from %s with %s",
1856233294Sstas		spn, cpn, from, msg);
1857233294Sstas	krb5_free_error_message(context, msg);
1858233294Sstas	goto out;
1859233294Sstas    }
1860233294Sstas
1861178825Sdfr    /*
1862233294Sstas     * Process request
1863178825Sdfr     */
1864178825Sdfr
1865233294Sstas    /* by default the tgt principal matches the client principal */
1866233294Sstas    tp = cp;
1867233294Sstas    tpn = cpn;
1868178825Sdfr
1869178825Sdfr    if (client) {
1870178825Sdfr	const PA_DATA *sdata;
1871178825Sdfr	int i = 0;
1872178825Sdfr
1873233294Sstas	sdata = _kdc_find_padata(req, &i, KRB5_PADATA_FOR_USER);
1874178825Sdfr	if (sdata) {
1875178825Sdfr	    krb5_crypto crypto;
1876178825Sdfr	    krb5_data datack;
1877178825Sdfr	    PA_S4U2Self self;
1878178825Sdfr	    const char *str;
1879178825Sdfr
1880233294Sstas	    ret = decode_PA_S4U2Self(sdata->padata_value.data,
1881178825Sdfr				     sdata->padata_value.length,
1882178825Sdfr				     &self, NULL);
1883178825Sdfr	    if (ret) {
1884178825Sdfr		kdc_log(context, config, 0, "Failed to decode PA-S4U2Self");
1885178825Sdfr		goto out;
1886178825Sdfr	    }
1887178825Sdfr
1888178825Sdfr	    ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
1889178825Sdfr	    if (ret)
1890178825Sdfr		goto out;
1891178825Sdfr
1892178825Sdfr	    ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
1893178825Sdfr	    if (ret) {
1894233294Sstas		const char *msg = krb5_get_error_message(context, ret);
1895178825Sdfr		free_PA_S4U2Self(&self);
1896178825Sdfr		krb5_data_free(&datack);
1897233294Sstas		kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
1898233294Sstas		krb5_free_error_message(context, msg);
1899178825Sdfr		goto out;
1900178825Sdfr	    }
1901178825Sdfr
1902178825Sdfr	    ret = krb5_verify_checksum(context,
1903178825Sdfr				       crypto,
1904178825Sdfr				       KRB5_KU_OTHER_CKSUM,
1905233294Sstas				       datack.data,
1906233294Sstas				       datack.length,
1907178825Sdfr				       &self.cksum);
1908178825Sdfr	    krb5_data_free(&datack);
1909178825Sdfr	    krb5_crypto_destroy(context, crypto);
1910178825Sdfr	    if (ret) {
1911233294Sstas		const char *msg = krb5_get_error_message(context, ret);
1912178825Sdfr		free_PA_S4U2Self(&self);
1913233294Sstas		kdc_log(context, config, 0,
1914233294Sstas			"krb5_verify_checksum failed for S4U2Self: %s", msg);
1915233294Sstas		krb5_free_error_message(context, msg);
1916178825Sdfr		goto out;
1917178825Sdfr	    }
1918178825Sdfr
1919178825Sdfr	    ret = _krb5_principalname2krb5_principal(context,
1920233294Sstas						     &tp,
1921178825Sdfr						     self.name,
1922178825Sdfr						     self.realm);
1923178825Sdfr	    free_PA_S4U2Self(&self);
1924178825Sdfr	    if (ret)
1925178825Sdfr		goto out;
1926178825Sdfr
1927233294Sstas	    ret = krb5_unparse_name(context, tp, &tpn);
1928178825Sdfr	    if (ret)
1929178825Sdfr		goto out;
1930178825Sdfr
1931233294Sstas	    /* If we were about to put a PAC into the ticket, we better fix it to be the right PAC */
1932233294Sstas	    if(rspac.data) {
1933233294Sstas		krb5_pac p = NULL;
1934233294Sstas		krb5_data_free(&rspac);
1935233294Sstas		ret = _kdc_db_fetch(context, config, tp, HDB_F_GET_CLIENT | flags,
1936233294Sstas				    NULL, &s4u2self_impersonated_clientdb, &s4u2self_impersonated_client);
1937233294Sstas		if (ret) {
1938233294Sstas		    const char *msg;
1939233294Sstas
1940233294Sstas		    /*
1941233294Sstas		     * If the client belongs to the same realm as our krbtgt, it
1942233294Sstas		     * should exist in the local database.
1943233294Sstas		     *
1944233294Sstas		     */
1945233294Sstas
1946233294Sstas		    if (ret == HDB_ERR_NOENTRY)
1947233294Sstas			ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1948233294Sstas		    msg = krb5_get_error_message(context, ret);
1949233294Sstas		    kdc_log(context, config, 1,
1950233294Sstas			    "S2U4Self principal to impersonate %s not found in database: %s",
1951233294Sstas			    tpn, msg);
1952233294Sstas		    krb5_free_error_message(context, msg);
1953233294Sstas		    goto out;
1954233294Sstas		}
1955233294Sstas		ret = _kdc_pac_generate(context, s4u2self_impersonated_client, &p);
1956233294Sstas		if (ret) {
1957233294Sstas		    kdc_log(context, config, 0, "PAC generation failed for -- %s",
1958233294Sstas			    tpn);
1959233294Sstas		    goto out;
1960233294Sstas		}
1961233294Sstas		if (p != NULL) {
1962233294Sstas		    ret = _krb5_pac_sign(context, p, ticket->ticket.authtime,
1963233294Sstas					 s4u2self_impersonated_client->entry.principal,
1964233294Sstas					 ekey, &tkey_sign->key,
1965233294Sstas					 &rspac);
1966233294Sstas		    krb5_pac_free(context, p);
1967233294Sstas		    if (ret) {
1968233294Sstas			kdc_log(context, config, 0, "PAC signing failed for -- %s",
1969233294Sstas				tpn);
1970233294Sstas			goto out;
1971233294Sstas		    }
1972233294Sstas		}
1973233294Sstas	    }
1974233294Sstas
1975178825Sdfr	    /*
1976178825Sdfr	     * Check that service doing the impersonating is
1977178825Sdfr	     * requesting a ticket to it-self.
1978178825Sdfr	     */
1979233294Sstas	    ret = check_s4u2self(context, config, clientdb, client, sp);
1980233294Sstas	    if (ret) {
1981178825Sdfr		kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
1982233294Sstas			"to impersonate to service "
1983178825Sdfr			"(tried for user %s to service %s)",
1984233294Sstas			cpn, tpn, spn);
1985178825Sdfr		goto out;
1986178825Sdfr	    }
1987178825Sdfr
1988178825Sdfr	    /*
1989178825Sdfr	     * If the service isn't trusted for authentication to
1990178825Sdfr	     * delegation, remove the forward flag.
1991178825Sdfr	     */
1992178825Sdfr
1993178825Sdfr	    if (client->entry.flags.trusted_for_delegation) {
1994178825Sdfr		str = "[forwardable]";
1995178825Sdfr	    } else {
1996178825Sdfr		b->kdc_options.forwardable = 0;
1997178825Sdfr		str = "";
1998178825Sdfr	    }
1999178825Sdfr	    kdc_log(context, config, 0, "s4u2self %s impersonating %s to "
2000233294Sstas		    "service %s %s", cpn, tpn, spn, str);
2001178825Sdfr	}
2002178825Sdfr    }
2003178825Sdfr
2004178825Sdfr    /*
2005178825Sdfr     * Constrained delegation
2006178825Sdfr     */
2007178825Sdfr
2008178825Sdfr    if (client != NULL
2009178825Sdfr	&& b->additional_tickets != NULL
2010178825Sdfr	&& b->additional_tickets->len != 0
2011178825Sdfr	&& b->kdc_options.enc_tkt_in_skey == 0)
2012178825Sdfr    {
2013233294Sstas	int ad_signedpath = 0;
2014178825Sdfr	Key *clientkey;
2015178825Sdfr	Ticket *t;
2016178825Sdfr
2017233294Sstas	/*
2018233294Sstas	 * Require that the KDC have issued the service's krbtgt (not
2019233294Sstas	 * self-issued ticket with kimpersonate(1).
2020233294Sstas	 */
2021233294Sstas	if (!signedpath) {
2022233294Sstas	    ret = KRB5KDC_ERR_BADOPTION;
2023233294Sstas	    kdc_log(context, config, 0,
2024233294Sstas		    "Constrained delegation done on service ticket %s/%s",
2025233294Sstas		    cpn, spn);
2026233294Sstas	    goto out;
2027233294Sstas	}
2028233294Sstas
2029178825Sdfr	t = &b->additional_tickets->val[0];
2030178825Sdfr
2031233294Sstas	ret = hdb_enctype2key(context, &client->entry,
2032178825Sdfr			      t->enc_part.etype, &clientkey);
2033178825Sdfr	if(ret){
2034178825Sdfr	    ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
2035178825Sdfr	    goto out;
2036178825Sdfr	}
2037178825Sdfr
2038178825Sdfr	ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
2039178825Sdfr	if (ret) {
2040178825Sdfr	    kdc_log(context, config, 0,
2041178825Sdfr		    "failed to decrypt ticket for "
2042233294Sstas		    "constrained delegation from %s to %s ", cpn, spn);
2043178825Sdfr	    goto out;
2044178825Sdfr	}
2045178825Sdfr
2046233294Sstas	ret = _krb5_principalname2krb5_principal(context,
2047233294Sstas						 &tp,
2048233294Sstas						 adtkt.cname,
2049233294Sstas						 adtkt.crealm);
2050233294Sstas	if (ret)
2051233294Sstas	    goto out;
2052233294Sstas
2053233294Sstas	ret = krb5_unparse_name(context, tp, &tpn);
2054233294Sstas	if (ret)
2055233294Sstas	    goto out;
2056233294Sstas
2057233294Sstas	ret = _krb5_principalname2krb5_principal(context,
2058233294Sstas						 &dp,
2059233294Sstas						 t->sname,
2060233294Sstas						 t->realm);
2061233294Sstas	if (ret)
2062233294Sstas	    goto out;
2063233294Sstas
2064233294Sstas	ret = krb5_unparse_name(context, dp, &dpn);
2065233294Sstas	if (ret)
2066233294Sstas	    goto out;
2067233294Sstas
2068178825Sdfr	/* check that ticket is valid */
2069178825Sdfr	if (adtkt.flags.forwardable == 0) {
2070178825Sdfr	    kdc_log(context, config, 0,
2071178825Sdfr		    "Missing forwardable flag on ticket for "
2072233294Sstas		    "constrained delegation from %s (%s) as %s to %s ",
2073233294Sstas		    cpn, dpn, tpn, spn);
2074233294Sstas	    ret = KRB5KDC_ERR_BADOPTION;
2075178825Sdfr	    goto out;
2076178825Sdfr	}
2077178825Sdfr
2078233294Sstas	ret = check_constrained_delegation(context, config, clientdb,
2079233294Sstas					   client, server, sp);
2080178825Sdfr	if (ret) {
2081178825Sdfr	    kdc_log(context, config, 0,
2082233294Sstas		    "constrained delegation from %s (%s) as %s to %s not allowed",
2083233294Sstas		    cpn, dpn, tpn, spn);
2084178825Sdfr	    goto out;
2085178825Sdfr	}
2086178825Sdfr
2087233294Sstas	ret = verify_flags(context, config, &adtkt, tpn);
2088233294Sstas	if (ret) {
2089178825Sdfr	    goto out;
2090233294Sstas	}
2091178825Sdfr
2092233294Sstas	krb5_data_free(&rspac);
2093178825Sdfr
2094233294Sstas	/*
2095233294Sstas	 * generate the PAC for the user.
2096233294Sstas	 *
2097233294Sstas	 * TODO: pass in t->sname and t->realm and build
2098233294Sstas	 * a S4U_DELEGATION_INFO blob to the PAC.
2099233294Sstas	 */
2100233294Sstas	ret = check_PAC(context, config, tp, dp,
2101233294Sstas			client, server, krbtgt,
2102233294Sstas			&clientkey->key, &tkey_check->key,
2103233294Sstas			ekey, &tkey_sign->key,
2104233294Sstas			&adtkt, &rspac, &ad_signedpath);
2105178825Sdfr	if (ret) {
2106233294Sstas	    const char *msg = krb5_get_error_message(context, ret);
2107233294Sstas	    kdc_log(context, config, 0,
2108233294Sstas		    "Verify delegated PAC failed to %s for client"
2109233294Sstas		    "%s (%s) as %s from %s with %s",
2110233294Sstas		    spn, cpn, dpn, tpn, from, msg);
2111233294Sstas	    krb5_free_error_message(context, msg);
2112178825Sdfr	    goto out;
2113178825Sdfr	}
2114178825Sdfr
2115178825Sdfr	/*
2116233294Sstas	 * Check that the KDC issued the user's ticket.
2117178825Sdfr	 */
2118178825Sdfr	ret = check_KRB5SignedPath(context,
2119178825Sdfr				   config,
2120178825Sdfr				   krbtgt,
2121233294Sstas				   cp,
2122178825Sdfr				   &adtkt,
2123233294Sstas				   NULL,
2124233294Sstas				   &ad_signedpath);
2125178825Sdfr	if (ret) {
2126233294Sstas	    const char *msg = krb5_get_error_message(context, ret);
2127178825Sdfr	    kdc_log(context, config, 0,
2128178825Sdfr		    "KRB5SignedPath check from service %s failed "
2129233294Sstas		    "for delegation to %s for client %s (%s)"
2130178825Sdfr		    "from %s failed with %s",
2131233294Sstas		    spn, tpn, dpn, cpn, from, msg);
2132233294Sstas	    krb5_free_error_message(context, msg);
2133178825Sdfr	    goto out;
2134178825Sdfr	}
2135178825Sdfr
2136233294Sstas	if (!ad_signedpath) {
2137233294Sstas	    ret = KRB5KDC_ERR_BADOPTION;
2138233294Sstas	    kdc_log(context, config, 0,
2139233294Sstas		    "Ticket not signed with PAC nor SignedPath service %s failed "
2140233294Sstas		    "for delegation to %s for client %s (%s)"
2141233294Sstas		    "from %s",
2142233294Sstas		    spn, tpn, dpn, cpn, from);
2143233294Sstas	    goto out;
2144233294Sstas	}
2145233294Sstas
2146178825Sdfr	kdc_log(context, config, 0, "constrained delegation for %s "
2147233294Sstas		"from %s (%s) to %s", tpn, cpn, dpn, spn);
2148178825Sdfr    }
2149178825Sdfr
2150178825Sdfr    /*
2151178825Sdfr     * Check flags
2152178825Sdfr     */
2153178825Sdfr
2154233294Sstas    ret = kdc_check_flags(context, config,
2155233294Sstas			  client, cpn,
2156233294Sstas			  server, spn,
2157233294Sstas			  FALSE);
2158178825Sdfr    if(ret)
2159178825Sdfr	goto out;
2160178825Sdfr
2161233294Sstas    if((b->kdc_options.validate || b->kdc_options.renew) &&
2162233294Sstas       !krb5_principal_compare(context,
2163178825Sdfr			       krbtgt->entry.principal,
2164178825Sdfr			       server->entry.principal)){
2165178825Sdfr	kdc_log(context, config, 0, "Inconsistent request.");
2166178825Sdfr	ret = KRB5KDC_ERR_SERVER_NOMATCH;
2167178825Sdfr	goto out;
2168178825Sdfr    }
2169178825Sdfr
2170178825Sdfr    /* check for valid set of addresses */
2171178825Sdfr    if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) {
2172178825Sdfr	ret = KRB5KRB_AP_ERR_BADADDR;
2173178825Sdfr	kdc_log(context, config, 0, "Request from wrong address");
2174178825Sdfr	goto out;
2175178825Sdfr    }
2176233294Sstas
2177178825Sdfr    /*
2178233294Sstas     * If this is an referral, add server referral data to the
2179233294Sstas     * auth_data reply .
2180178825Sdfr     */
2181233294Sstas    if (ref_realm) {
2182233294Sstas	PA_DATA pa;
2183233294Sstas	krb5_crypto crypto;
2184178825Sdfr
2185233294Sstas	kdc_log(context, config, 0,
2186233294Sstas		"Adding server referral to %s", ref_realm);
2187178825Sdfr
2188233294Sstas	ret = krb5_crypto_init(context, &sessionkey, 0, &crypto);
2189178825Sdfr	if (ret)
2190178825Sdfr	    goto out;
2191178825Sdfr
2192233294Sstas	ret = build_server_referral(context, config, crypto, ref_realm,
2193233294Sstas				    NULL, s, &pa.padata_value);
2194233294Sstas	krb5_crypto_destroy(context, crypto);
2195233294Sstas	if (ret) {
2196178825Sdfr	    kdc_log(context, config, 0,
2197233294Sstas		    "Failed building server referral");
2198178825Sdfr	    goto out;
2199178825Sdfr	}
2200233294Sstas	pa.padata_type = KRB5_PADATA_SERVER_REFERRAL;
2201178825Sdfr
2202233294Sstas	ret = add_METHOD_DATA(&enc_pa_data, &pa);
2203233294Sstas	krb5_data_free(&pa.padata_value);
2204178825Sdfr	if (ret) {
2205178825Sdfr	    kdc_log(context, config, 0,
2206233294Sstas		    "Add server referral METHOD-DATA failed");
2207178825Sdfr	    goto out;
2208178825Sdfr	}
2209178825Sdfr    }
2210178825Sdfr
2211178825Sdfr    /*
2212178825Sdfr     *
2213178825Sdfr     */
2214178825Sdfr
2215178825Sdfr    ret = tgs_make_reply(context,
2216233294Sstas			 config,
2217233294Sstas			 b,
2218233294Sstas			 tp,
2219233294Sstas			 tgt,
2220233294Sstas			 replykey,
2221233294Sstas			 rk_is_subkey,
2222178825Sdfr			 ekey,
2223178825Sdfr			 &sessionkey,
2224178825Sdfr			 kvno,
2225233294Sstas			 *auth_data,
2226233294Sstas			 server,
2227233294Sstas			 rsp,
2228178825Sdfr			 spn,
2229233294Sstas			 client,
2230233294Sstas			 cp,
2231233294Sstas			 krbtgt_out,
2232178825Sdfr			 krbtgt_etype,
2233178825Sdfr			 spp,
2234178825Sdfr			 &rspac,
2235233294Sstas			 &enc_pa_data,
2236178825Sdfr			 e_text,
2237178825Sdfr			 reply);
2238233294Sstas
2239178825Sdfrout:
2240233294Sstas    if (tpn != cpn)
2241233294Sstas	    free(tpn);
2242178825Sdfr    free(spn);
2243178825Sdfr    free(cpn);
2244233294Sstas    if (dpn)
2245233294Sstas	free(dpn);
2246233294Sstas
2247178825Sdfr    krb5_data_free(&rspac);
2248178825Sdfr    krb5_free_keyblock_contents(context, &sessionkey);
2249233294Sstas    if(krbtgt_out)
2250233294Sstas	_kdc_free_ent(context, krbtgt_out);
2251178825Sdfr    if(server)
2252178825Sdfr	_kdc_free_ent(context, server);
2253178825Sdfr    if(client)
2254178825Sdfr	_kdc_free_ent(context, client);
2255233294Sstas    if(s4u2self_impersonated_client)
2256233294Sstas	_kdc_free_ent(context, s4u2self_impersonated_client);
2257178825Sdfr
2258233294Sstas    if (tp && tp != cp)
2259233294Sstas	krb5_free_principal(context, tp);
2260178825Sdfr    if (cp)
2261178825Sdfr	krb5_free_principal(context, cp);
2262233294Sstas    if (dp)
2263233294Sstas	krb5_free_principal(context, dp);
2264178825Sdfr    if (sp)
2265178825Sdfr	krb5_free_principal(context, sp);
2266233294Sstas    if (ref_realm)
2267233294Sstas	free(ref_realm);
2268233294Sstas    free_METHOD_DATA(&enc_pa_data);
2269178825Sdfr
2270178825Sdfr    free_EncTicketPart(&adtkt);
2271178825Sdfr
2272178825Sdfr    return ret;
2273178825Sdfr}
2274178825Sdfr
2275178825Sdfr/*
2276178825Sdfr *
2277178825Sdfr */
2278178825Sdfr
2279178825Sdfrkrb5_error_code
2280233294Sstas_kdc_tgs_rep(krb5_context context,
2281178825Sdfr	     krb5_kdc_configuration *config,
2282233294Sstas	     KDC_REQ *req,
2283178825Sdfr	     krb5_data *data,
2284178825Sdfr	     const char *from,
2285178825Sdfr	     struct sockaddr *from_addr,
2286178825Sdfr	     int datagram_reply)
2287178825Sdfr{
2288178825Sdfr    AuthorizationData *auth_data = NULL;
2289178825Sdfr    krb5_error_code ret;
2290178825Sdfr    int i = 0;
2291178825Sdfr    const PA_DATA *tgs_req;
2292178825Sdfr
2293178825Sdfr    hdb_entry_ex *krbtgt = NULL;
2294178825Sdfr    krb5_ticket *ticket = NULL;
2295178825Sdfr    const char *e_text = NULL;
2296178825Sdfr    krb5_enctype krbtgt_etype = ETYPE_NULL;
2297178825Sdfr
2298233294Sstas    krb5_keyblock *replykey = NULL;
2299233294Sstas    int rk_is_subkey = 0;
2300178825Sdfr    time_t *csec = NULL;
2301178825Sdfr    int *cusec = NULL;
2302178825Sdfr
2303178825Sdfr    if(req->padata == NULL){
2304178825Sdfr	ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
2305178825Sdfr	kdc_log(context, config, 0,
2306178825Sdfr		"TGS-REQ from %s without PA-DATA", from);
2307178825Sdfr	goto out;
2308178825Sdfr    }
2309233294Sstas
2310178825Sdfr    tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
2311178825Sdfr
2312178825Sdfr    if(tgs_req == NULL){
2313178825Sdfr	ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2314233294Sstas
2315233294Sstas	kdc_log(context, config, 0,
2316178825Sdfr		"TGS-REQ from %s without PA-TGS-REQ", from);
2317178825Sdfr	goto out;
2318178825Sdfr    }
2319233294Sstas    ret = tgs_parse_request(context, config,
2320178825Sdfr			    &req->req_body, tgs_req,
2321178825Sdfr			    &krbtgt,
2322178825Sdfr			    &krbtgt_etype,
2323178825Sdfr			    &ticket,
2324178825Sdfr			    &e_text,
2325178825Sdfr			    from, from_addr,
2326178825Sdfr			    &csec, &cusec,
2327233294Sstas			    &auth_data,
2328233294Sstas			    &replykey,
2329233294Sstas			    &rk_is_subkey);
2330233294Sstas    if (ret == HDB_ERR_NOT_FOUND_HERE) {
2331233294Sstas	/* kdc_log() is called in tgs_parse_request() */
2332233294Sstas	goto out;
2333233294Sstas    }
2334178825Sdfr    if (ret) {
2335233294Sstas	kdc_log(context, config, 0,
2336178825Sdfr		"Failed parsing TGS-REQ from %s", from);
2337178825Sdfr	goto out;
2338178825Sdfr    }
2339178825Sdfr
2340178825Sdfr    ret = tgs_build_reply(context,
2341178825Sdfr			  config,
2342178825Sdfr			  req,
2343178825Sdfr			  &req->req_body,
2344178825Sdfr			  krbtgt,
2345178825Sdfr			  krbtgt_etype,
2346233294Sstas			  replykey,
2347233294Sstas			  rk_is_subkey,
2348178825Sdfr			  ticket,
2349178825Sdfr			  data,
2350178825Sdfr			  from,
2351178825Sdfr			  &e_text,
2352233294Sstas			  &auth_data,
2353233294Sstas			  from_addr);
2354178825Sdfr    if (ret) {
2355233294Sstas	kdc_log(context, config, 0,
2356178825Sdfr		"Failed building TGS-REP to %s", from);
2357178825Sdfr	goto out;
2358178825Sdfr    }
2359178825Sdfr
2360178825Sdfr    /* */
2361178825Sdfr    if (datagram_reply && data->length > config->max_datagram_reply_length) {
2362178825Sdfr	krb5_data_free(data);
2363178825Sdfr	ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
2364178825Sdfr	e_text = "Reply packet too large";
2365178825Sdfr    }
2366178825Sdfr
2367178825Sdfrout:
2368233294Sstas    if (replykey)
2369233294Sstas	krb5_free_keyblock(context, replykey);
2370233294Sstas    if(ret && ret != HDB_ERR_NOT_FOUND_HERE && data->data == NULL){
2371178825Sdfr	krb5_mk_error(context,
2372178825Sdfr		      ret,
2373178825Sdfr		      NULL,
2374178825Sdfr		      NULL,
2375178825Sdfr		      NULL,
2376178825Sdfr		      NULL,
2377178825Sdfr		      csec,
2378178825Sdfr		      cusec,
2379178825Sdfr		      data);
2380233294Sstas	ret = 0;
2381178825Sdfr    }
2382178825Sdfr    free(csec);
2383178825Sdfr    free(cusec);
2384178825Sdfr    if (ticket)
2385178825Sdfr	krb5_free_ticket(context, ticket);
2386178825Sdfr    if(krbtgt)
2387178825Sdfr	_kdc_free_ent(context, krbtgt);
2388178825Sdfr
2389178825Sdfr    if (auth_data) {
2390178825Sdfr	free_AuthorizationData(auth_data);
2391178825Sdfr	free(auth_data);
2392178825Sdfr    }
2393178825Sdfr
2394233294Sstas    return ret;
2395178825Sdfr}
2396