1178825Sdfr/*
2178825Sdfr * Copyright (c) 1997-2007 Kungliga Tekniska H�gskolan
3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden).
4178825Sdfr * All rights reserved.
5178825Sdfr *
6178825Sdfr * Redistribution and use in source and binary forms, with or without
7178825Sdfr * modification, are permitted provided that the following conditions
8178825Sdfr * are met:
9178825Sdfr *
10178825Sdfr * 1. Redistributions of source code must retain the above copyright
11178825Sdfr *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
14178825Sdfr *    notice, this list of conditions and the following disclaimer in the
15178825Sdfr *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors
18178825Sdfr *    may be used to endorse or promote products derived from this software
19178825Sdfr *    without specific prior written permission.
20178825Sdfr *
21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31178825Sdfr * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include "kdc_locl.h"
35178825Sdfr
36178825SdfrRCSID("$Id: krb5tgs.c 22071 2007-11-14 20:04:50Z lha $");
37178825Sdfr
38178825Sdfr/*
39178825Sdfr * return the realm of a krbtgt-ticket or NULL
40178825Sdfr */
41178825Sdfr
42178825Sdfrstatic Realm
43178825Sdfrget_krbtgt_realm(const PrincipalName *p)
44178825Sdfr{
45178825Sdfr    if(p->name_string.len == 2
46178825Sdfr       && strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0)
47178825Sdfr	return p->name_string.val[1];
48178825Sdfr    else
49178825Sdfr	return NULL;
50178825Sdfr}
51178825Sdfr
52178825Sdfr/*
53178825Sdfr * The KDC might add a signed path to the ticket authorization data
54178825Sdfr * field. This is to avoid server impersonating clients and the
55178825Sdfr * request constrained delegation.
56178825Sdfr *
57178825Sdfr * This is done by storing a KRB5_AUTHDATA_IF_RELEVANT with a single
58178825Sdfr * entry of type KRB5SignedPath.
59178825Sdfr */
60178825Sdfr
61178825Sdfrstatic krb5_error_code
62178825Sdfrfind_KRB5SignedPath(krb5_context context,
63178825Sdfr		    const AuthorizationData *ad,
64178825Sdfr		    krb5_data *data)
65178825Sdfr{
66178825Sdfr    AuthorizationData child;
67178825Sdfr    krb5_error_code ret;
68178825Sdfr    int pos;
69178825Sdfr
70178825Sdfr    if (ad == NULL || ad->len == 0)
71178825Sdfr	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
72178825Sdfr
73178825Sdfr    pos = ad->len - 1;
74178825Sdfr
75178825Sdfr    if (ad->val[pos].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
76178825Sdfr	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
77178825Sdfr
78178825Sdfr    ret = decode_AuthorizationData(ad->val[pos].ad_data.data,
79178825Sdfr				   ad->val[pos].ad_data.length,
80178825Sdfr				   &child,
81178825Sdfr				   NULL);
82178825Sdfr    if (ret) {
83178825Sdfr	krb5_set_error_string(context, "Failed to decode "
84178825Sdfr			      "IF_RELEVANT with %d", ret);
85178825Sdfr	return ret;
86178825Sdfr    }
87178825Sdfr
88178825Sdfr    if (child.len != 1) {
89178825Sdfr	free_AuthorizationData(&child);
90178825Sdfr	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
91178825Sdfr    }
92178825Sdfr
93178825Sdfr    if (child.val[0].ad_type != KRB5_AUTHDATA_SIGNTICKET) {
94178825Sdfr	free_AuthorizationData(&child);
95178825Sdfr	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
96178825Sdfr    }
97178825Sdfr
98178825Sdfr    if (data)
99178825Sdfr	ret = der_copy_octet_string(&child.val[0].ad_data, data);
100178825Sdfr    free_AuthorizationData(&child);
101178825Sdfr    return ret;
102178825Sdfr}
103178825Sdfr
104178825Sdfrkrb5_error_code
105178825Sdfr_kdc_add_KRB5SignedPath(krb5_context context,
106178825Sdfr			krb5_kdc_configuration *config,
107178825Sdfr			hdb_entry_ex *krbtgt,
108178825Sdfr			krb5_enctype enctype,
109178825Sdfr			krb5_const_principal server,
110178825Sdfr			KRB5SignedPathPrincipals *principals,
111178825Sdfr			EncTicketPart *tkt)
112178825Sdfr{
113178825Sdfr    krb5_error_code ret;
114178825Sdfr    KRB5SignedPath sp;
115178825Sdfr    krb5_data data;
116178825Sdfr    krb5_crypto crypto = NULL;
117178825Sdfr    size_t size;
118178825Sdfr
119178825Sdfr    if (server && principals) {
120178825Sdfr	ret = add_KRB5SignedPathPrincipals(principals, server);
121178825Sdfr	if (ret)
122178825Sdfr	    return ret;
123178825Sdfr    }
124178825Sdfr
125178825Sdfr    {
126178825Sdfr	KRB5SignedPathData spd;
127178825Sdfr
128178825Sdfr	spd.encticket = *tkt;
129178825Sdfr	spd.delegated = principals;
130178825Sdfr
131178825Sdfr	ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
132178825Sdfr			   &spd, &size, ret);
133178825Sdfr	if (ret)
134178825Sdfr	    return ret;
135178825Sdfr	if (data.length != size)
136178825Sdfr	    krb5_abortx(context, "internal asn.1 encoder error");
137178825Sdfr    }
138178825Sdfr
139178825Sdfr    {
140178825Sdfr	Key *key;
141178825Sdfr	ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key);
142178825Sdfr	if (ret == 0)
143178825Sdfr	    ret = krb5_crypto_init(context, &key->key, 0, &crypto);
144178825Sdfr	if (ret) {
145178825Sdfr	    free(data.data);
146178825Sdfr	    return ret;
147178825Sdfr	}
148178825Sdfr    }
149178825Sdfr
150178825Sdfr    /*
151178825Sdfr     * Fill in KRB5SignedPath
152178825Sdfr     */
153178825Sdfr
154178825Sdfr    sp.etype = enctype;
155178825Sdfr    sp.delegated = principals;
156178825Sdfr
157178825Sdfr    ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0,
158178825Sdfr			       data.data, data.length, &sp.cksum);
159178825Sdfr    krb5_crypto_destroy(context, crypto);
160178825Sdfr    free(data.data);
161178825Sdfr    if (ret)
162178825Sdfr	return ret;
163178825Sdfr
164178825Sdfr    ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret);
165178825Sdfr    free_Checksum(&sp.cksum);
166178825Sdfr    if (ret)
167178825Sdfr	return ret;
168178825Sdfr    if (data.length != size)
169178825Sdfr	krb5_abortx(context, "internal asn.1 encoder error");
170178825Sdfr
171178825Sdfr
172178825Sdfr    /*
173178825Sdfr     * Add IF-RELEVANT(KRB5SignedPath) to the last slot in
174178825Sdfr     * authorization data field.
175178825Sdfr     */
176178825Sdfr
177178825Sdfr    ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
178178825Sdfr				      KRB5_AUTHDATA_SIGNTICKET, &data);
179178825Sdfr    krb5_data_free(&data);
180178825Sdfr
181178825Sdfr    return ret;
182178825Sdfr}
183178825Sdfr
184178825Sdfrstatic krb5_error_code
185178825Sdfrcheck_KRB5SignedPath(krb5_context context,
186178825Sdfr		     krb5_kdc_configuration *config,
187178825Sdfr		     hdb_entry_ex *krbtgt,
188178825Sdfr		     EncTicketPart *tkt,
189178825Sdfr		     KRB5SignedPathPrincipals **delegated,
190178825Sdfr		     int require_signedpath)
191178825Sdfr{
192178825Sdfr    krb5_error_code ret;
193178825Sdfr    krb5_data data;
194178825Sdfr    krb5_crypto crypto = NULL;
195178825Sdfr
196178825Sdfr    *delegated = NULL;
197178825Sdfr
198178825Sdfr    ret = find_KRB5SignedPath(context, tkt->authorization_data, &data);
199178825Sdfr    if (ret == 0) {
200178825Sdfr	KRB5SignedPathData spd;
201178825Sdfr	KRB5SignedPath sp;
202178825Sdfr	AuthorizationData *ad;
203178825Sdfr	size_t size;
204178825Sdfr
205178825Sdfr	ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL);
206178825Sdfr	krb5_data_free(&data);
207178825Sdfr	if (ret)
208178825Sdfr	    return ret;
209178825Sdfr
210178825Sdfr	spd.encticket = *tkt;
211178825Sdfr	/* the KRB5SignedPath is the last entry */
212178825Sdfr	ad = spd.encticket.authorization_data;
213178825Sdfr	if (--ad->len == 0)
214178825Sdfr	    spd.encticket.authorization_data = NULL;
215178825Sdfr	spd.delegated = sp.delegated;
216178825Sdfr
217178825Sdfr	ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
218178825Sdfr			   &spd, &size, ret);
219178825Sdfr	ad->len++;
220178825Sdfr	spd.encticket.authorization_data = ad;
221178825Sdfr	if (ret) {
222178825Sdfr	    free_KRB5SignedPath(&sp);
223178825Sdfr	    return ret;
224178825Sdfr	}
225178825Sdfr	if (data.length != size)
226178825Sdfr	    krb5_abortx(context, "internal asn.1 encoder error");
227178825Sdfr
228178825Sdfr	{
229178825Sdfr	    Key *key;
230178825Sdfr	    ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key);
231178825Sdfr	    if (ret == 0)
232178825Sdfr		ret = krb5_crypto_init(context, &key->key, 0, &crypto);
233178825Sdfr	    if (ret) {
234178825Sdfr		free(data.data);
235178825Sdfr		free_KRB5SignedPath(&sp);
236178825Sdfr		return ret;
237178825Sdfr	    }
238178825Sdfr	}
239178825Sdfr	ret = krb5_verify_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH,
240178825Sdfr				   data.data, data.length,
241178825Sdfr				   &sp.cksum);
242178825Sdfr	krb5_crypto_destroy(context, crypto);
243178825Sdfr	free(data.data);
244178825Sdfr	if (ret) {
245178825Sdfr	    free_KRB5SignedPath(&sp);
246178825Sdfr	    return ret;
247178825Sdfr	}
248178825Sdfr
249178825Sdfr	if (sp.delegated) {
250178825Sdfr
251178825Sdfr	    *delegated = malloc(sizeof(*sp.delegated));
252178825Sdfr	    if (*delegated == NULL) {
253178825Sdfr		free_KRB5SignedPath(&sp);
254178825Sdfr		return ENOMEM;
255178825Sdfr	    }
256178825Sdfr
257178825Sdfr	    ret = copy_KRB5SignedPathPrincipals(*delegated, sp.delegated);
258178825Sdfr	    if (ret) {
259178825Sdfr		free_KRB5SignedPath(&sp);
260178825Sdfr		free(*delegated);
261178825Sdfr		*delegated = NULL;
262178825Sdfr		return ret;
263178825Sdfr	    }
264178825Sdfr	}
265178825Sdfr	free_KRB5SignedPath(&sp);
266178825Sdfr
267178825Sdfr    } else {
268178825Sdfr	if (require_signedpath)
269178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
270178825Sdfr    }
271178825Sdfr
272178825Sdfr    return 0;
273178825Sdfr}
274178825Sdfr
275178825Sdfr/*
276178825Sdfr *
277178825Sdfr */
278178825Sdfr
279178825Sdfrstatic krb5_error_code
280178825Sdfrcheck_PAC(krb5_context context,
281178825Sdfr	  krb5_kdc_configuration *config,
282178825Sdfr	  const krb5_principal client_principal,
283178825Sdfr	  hdb_entry_ex *client,
284178825Sdfr	  hdb_entry_ex *server,
285178825Sdfr	  const EncryptionKey *server_key,
286178825Sdfr	  const EncryptionKey *krbtgt_key,
287178825Sdfr	  EncTicketPart *tkt,
288178825Sdfr	  krb5_data *rspac,
289178825Sdfr	  int *require_signedpath)
290178825Sdfr{
291178825Sdfr    AuthorizationData *ad = tkt->authorization_data;
292178825Sdfr    unsigned i, j;
293178825Sdfr    krb5_error_code ret;
294178825Sdfr
295178825Sdfr    if (ad == NULL || ad->len == 0)
296178825Sdfr	return 0;
297178825Sdfr
298178825Sdfr    for (i = 0; i < ad->len; i++) {
299178825Sdfr	AuthorizationData child;
300178825Sdfr
301178825Sdfr	if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
302178825Sdfr	    continue;
303178825Sdfr
304178825Sdfr	ret = decode_AuthorizationData(ad->val[i].ad_data.data,
305178825Sdfr				       ad->val[i].ad_data.length,
306178825Sdfr				       &child,
307178825Sdfr				       NULL);
308178825Sdfr	if (ret) {
309178825Sdfr	    krb5_set_error_string(context, "Failed to decode "
310178825Sdfr				  "IF_RELEVANT with %d", ret);
311178825Sdfr	    return ret;
312178825Sdfr	}
313178825Sdfr	for (j = 0; j < child.len; j++) {
314178825Sdfr
315178825Sdfr	    if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
316178825Sdfr		krb5_pac pac;
317178825Sdfr
318178825Sdfr		/* Found PAC */
319178825Sdfr		ret = krb5_pac_parse(context,
320178825Sdfr				     child.val[j].ad_data.data,
321178825Sdfr				     child.val[j].ad_data.length,
322178825Sdfr				     &pac);
323178825Sdfr		free_AuthorizationData(&child);
324178825Sdfr		if (ret)
325178825Sdfr		    return ret;
326178825Sdfr
327178825Sdfr		ret = krb5_pac_verify(context, pac, tkt->authtime,
328178825Sdfr				      client_principal,
329178825Sdfr				      krbtgt_key, NULL);
330178825Sdfr		if (ret) {
331178825Sdfr		    krb5_pac_free(context, pac);
332178825Sdfr		    return ret;
333178825Sdfr		}
334178825Sdfr
335178825Sdfr		ret = _kdc_pac_verify(context, client_principal,
336178825Sdfr				      client, server, &pac);
337178825Sdfr		if (ret) {
338178825Sdfr		    krb5_pac_free(context, pac);
339178825Sdfr		    return ret;
340178825Sdfr		}
341178825Sdfr		*require_signedpath = 0;
342178825Sdfr
343178825Sdfr		ret = _krb5_pac_sign(context, pac, tkt->authtime,
344178825Sdfr				     client_principal,
345178825Sdfr				     server_key, krbtgt_key, rspac);
346178825Sdfr
347178825Sdfr		krb5_pac_free(context, pac);
348178825Sdfr
349178825Sdfr		return ret;
350178825Sdfr	    }
351178825Sdfr	}
352178825Sdfr	free_AuthorizationData(&child);
353178825Sdfr    }
354178825Sdfr    return 0;
355178825Sdfr}
356178825Sdfr
357178825Sdfr/*
358178825Sdfr *
359178825Sdfr */
360178825Sdfr
361178825Sdfrstatic krb5_error_code
362178825Sdfrcheck_tgs_flags(krb5_context context,
363178825Sdfr		krb5_kdc_configuration *config,
364178825Sdfr		KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et)
365178825Sdfr{
366178825Sdfr    KDCOptions f = b->kdc_options;
367178825Sdfr
368178825Sdfr    if(f.validate){
369178825Sdfr	if(!tgt->flags.invalid || tgt->starttime == NULL){
370178825Sdfr	    kdc_log(context, config, 0,
371178825Sdfr		    "Bad request to validate ticket");
372178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
373178825Sdfr	}
374178825Sdfr	if(*tgt->starttime > kdc_time){
375178825Sdfr	    kdc_log(context, config, 0,
376178825Sdfr		    "Early request to validate ticket");
377178825Sdfr	    return KRB5KRB_AP_ERR_TKT_NYV;
378178825Sdfr	}
379178825Sdfr	/* XXX  tkt = tgt */
380178825Sdfr	et->flags.invalid = 0;
381178825Sdfr    }else if(tgt->flags.invalid){
382178825Sdfr	kdc_log(context, config, 0,
383178825Sdfr		"Ticket-granting ticket has INVALID flag set");
384178825Sdfr	return KRB5KRB_AP_ERR_TKT_INVALID;
385178825Sdfr    }
386178825Sdfr
387178825Sdfr    if(f.forwardable){
388178825Sdfr	if(!tgt->flags.forwardable){
389178825Sdfr	    kdc_log(context, config, 0,
390178825Sdfr		    "Bad request for forwardable ticket");
391178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
392178825Sdfr	}
393178825Sdfr	et->flags.forwardable = 1;
394178825Sdfr    }
395178825Sdfr    if(f.forwarded){
396178825Sdfr	if(!tgt->flags.forwardable){
397178825Sdfr	    kdc_log(context, config, 0,
398178825Sdfr		    "Request to forward non-forwardable ticket");
399178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
400178825Sdfr	}
401178825Sdfr	et->flags.forwarded = 1;
402178825Sdfr	et->caddr = b->addresses;
403178825Sdfr    }
404178825Sdfr    if(tgt->flags.forwarded)
405178825Sdfr	et->flags.forwarded = 1;
406178825Sdfr
407178825Sdfr    if(f.proxiable){
408178825Sdfr	if(!tgt->flags.proxiable){
409178825Sdfr	    kdc_log(context, config, 0,
410178825Sdfr		    "Bad request for proxiable ticket");
411178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
412178825Sdfr	}
413178825Sdfr	et->flags.proxiable = 1;
414178825Sdfr    }
415178825Sdfr    if(f.proxy){
416178825Sdfr	if(!tgt->flags.proxiable){
417178825Sdfr	    kdc_log(context, config, 0,
418178825Sdfr		    "Request to proxy non-proxiable ticket");
419178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
420178825Sdfr	}
421178825Sdfr	et->flags.proxy = 1;
422178825Sdfr	et->caddr = b->addresses;
423178825Sdfr    }
424178825Sdfr    if(tgt->flags.proxy)
425178825Sdfr	et->flags.proxy = 1;
426178825Sdfr
427178825Sdfr    if(f.allow_postdate){
428178825Sdfr	if(!tgt->flags.may_postdate){
429178825Sdfr	    kdc_log(context, config, 0,
430178825Sdfr		    "Bad request for post-datable ticket");
431178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
432178825Sdfr	}
433178825Sdfr	et->flags.may_postdate = 1;
434178825Sdfr    }
435178825Sdfr    if(f.postdated){
436178825Sdfr	if(!tgt->flags.may_postdate){
437178825Sdfr	    kdc_log(context, config, 0,
438178825Sdfr		    "Bad request for postdated ticket");
439178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
440178825Sdfr	}
441178825Sdfr	if(b->from)
442178825Sdfr	    *et->starttime = *b->from;
443178825Sdfr	et->flags.postdated = 1;
444178825Sdfr	et->flags.invalid = 1;
445178825Sdfr    }else if(b->from && *b->from > kdc_time + context->max_skew){
446178825Sdfr	kdc_log(context, config, 0, "Ticket cannot be postdated");
447178825Sdfr	return KRB5KDC_ERR_CANNOT_POSTDATE;
448178825Sdfr    }
449178825Sdfr
450178825Sdfr    if(f.renewable){
451178825Sdfr	if(!tgt->flags.renewable){
452178825Sdfr	    kdc_log(context, config, 0,
453178825Sdfr		    "Bad request for renewable ticket");
454178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
455178825Sdfr	}
456178825Sdfr	et->flags.renewable = 1;
457178825Sdfr	ALLOC(et->renew_till);
458178825Sdfr	_kdc_fix_time(&b->rtime);
459178825Sdfr	*et->renew_till = *b->rtime;
460178825Sdfr    }
461178825Sdfr    if(f.renew){
462178825Sdfr	time_t old_life;
463178825Sdfr	if(!tgt->flags.renewable || tgt->renew_till == NULL){
464178825Sdfr	    kdc_log(context, config, 0,
465178825Sdfr		    "Request to renew non-renewable ticket");
466178825Sdfr	    return KRB5KDC_ERR_BADOPTION;
467178825Sdfr	}
468178825Sdfr	old_life = tgt->endtime;
469178825Sdfr	if(tgt->starttime)
470178825Sdfr	    old_life -= *tgt->starttime;
471178825Sdfr	else
472178825Sdfr	    old_life -= tgt->authtime;
473178825Sdfr	et->endtime = *et->starttime + old_life;
474178825Sdfr	if (et->renew_till != NULL)
475178825Sdfr	    et->endtime = min(*et->renew_till, et->endtime);
476178825Sdfr    }
477178825Sdfr
478178825Sdfr#if 0
479178825Sdfr    /* checks for excess flags */
480178825Sdfr    if(f.request_anonymous && !config->allow_anonymous){
481178825Sdfr	kdc_log(context, config, 0,
482178825Sdfr		"Request for anonymous ticket");
483178825Sdfr	return KRB5KDC_ERR_BADOPTION;
484178825Sdfr    }
485178825Sdfr#endif
486178825Sdfr    return 0;
487178825Sdfr}
488178825Sdfr
489178825Sdfr/*
490178825Sdfr *
491178825Sdfr */
492178825Sdfr
493178825Sdfrstatic krb5_error_code
494178825Sdfrcheck_constrained_delegation(krb5_context context,
495178825Sdfr			     krb5_kdc_configuration *config,
496178825Sdfr			     hdb_entry_ex *client,
497178825Sdfr			     krb5_const_principal server)
498178825Sdfr{
499178825Sdfr    const HDB_Ext_Constrained_delegation_acl *acl;
500178825Sdfr    krb5_error_code ret;
501178825Sdfr    int i;
502178825Sdfr
503178825Sdfr    ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
504178825Sdfr    if (ret) {
505178825Sdfr	krb5_clear_error_string(context);
506178825Sdfr	return ret;
507178825Sdfr    }
508178825Sdfr
509178825Sdfr    if (acl) {
510178825Sdfr	for (i = 0; i < acl->len; i++) {
511178825Sdfr	    if (krb5_principal_compare(context, server, &acl->val[i]) == TRUE)
512178825Sdfr		return 0;
513178825Sdfr	}
514178825Sdfr    }
515178825Sdfr    kdc_log(context, config, 0,
516178825Sdfr	    "Bad request for constrained delegation");
517178825Sdfr    return KRB5KDC_ERR_BADOPTION;
518178825Sdfr}
519178825Sdfr
520178825Sdfr/*
521178825Sdfr *
522178825Sdfr */
523178825Sdfr
524178825Sdfrstatic krb5_error_code
525178825Sdfrverify_flags (krb5_context context,
526178825Sdfr	      krb5_kdc_configuration *config,
527178825Sdfr	      const EncTicketPart *et,
528178825Sdfr	      const char *pstr)
529178825Sdfr{
530178825Sdfr    if(et->endtime < kdc_time){
531178825Sdfr	kdc_log(context, config, 0, "Ticket expired (%s)", pstr);
532178825Sdfr	return KRB5KRB_AP_ERR_TKT_EXPIRED;
533178825Sdfr    }
534178825Sdfr    if(et->flags.invalid){
535178825Sdfr	kdc_log(context, config, 0, "Ticket not valid (%s)", pstr);
536178825Sdfr	return KRB5KRB_AP_ERR_TKT_NYV;
537178825Sdfr    }
538178825Sdfr    return 0;
539178825Sdfr}
540178825Sdfr
541178825Sdfr/*
542178825Sdfr *
543178825Sdfr */
544178825Sdfr
545178825Sdfrstatic krb5_error_code
546178825Sdfrfix_transited_encoding(krb5_context context,
547178825Sdfr		       krb5_kdc_configuration *config,
548178825Sdfr		       krb5_boolean check_policy,
549178825Sdfr		       const TransitedEncoding *tr,
550178825Sdfr		       EncTicketPart *et,
551178825Sdfr		       const char *client_realm,
552178825Sdfr		       const char *server_realm,
553178825Sdfr		       const char *tgt_realm)
554178825Sdfr{
555178825Sdfr    krb5_error_code ret = 0;
556178825Sdfr    char **realms, **tmp;
557178825Sdfr    int num_realms;
558178825Sdfr    int i;
559178825Sdfr
560178825Sdfr    switch (tr->tr_type) {
561178825Sdfr    case DOMAIN_X500_COMPRESS:
562178825Sdfr	break;
563178825Sdfr    case 0:
564178825Sdfr	/*
565178825Sdfr	 * Allow empty content of type 0 because that is was Microsoft
566178825Sdfr	 * generates in their TGT.
567178825Sdfr	 */
568178825Sdfr	if (tr->contents.length == 0)
569178825Sdfr	    break;
570178825Sdfr	kdc_log(context, config, 0,
571178825Sdfr		"Transited type 0 with non empty content");
572178825Sdfr	return KRB5KDC_ERR_TRTYPE_NOSUPP;
573178825Sdfr    default:
574178825Sdfr	kdc_log(context, config, 0,
575178825Sdfr		"Unknown transited type: %u", tr->tr_type);
576178825Sdfr	return KRB5KDC_ERR_TRTYPE_NOSUPP;
577178825Sdfr    }
578178825Sdfr
579178825Sdfr    ret = krb5_domain_x500_decode(context,
580178825Sdfr				  tr->contents,
581178825Sdfr				  &realms,
582178825Sdfr				  &num_realms,
583178825Sdfr				  client_realm,
584178825Sdfr				  server_realm);
585178825Sdfr    if(ret){
586178825Sdfr	krb5_warn(context, ret,
587178825Sdfr		  "Decoding transited encoding");
588178825Sdfr	return ret;
589178825Sdfr    }
590178825Sdfr    if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
591178825Sdfr	/* not us, so add the previous realm to transited set */
592178825Sdfr	if (num_realms < 0 || num_realms + 1 > UINT_MAX/sizeof(*realms)) {
593178825Sdfr	    ret = ERANGE;
594178825Sdfr	    goto free_realms;
595178825Sdfr	}
596178825Sdfr	tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
597178825Sdfr	if(tmp == NULL){
598178825Sdfr	    ret = ENOMEM;
599178825Sdfr	    goto free_realms;
600178825Sdfr	}
601178825Sdfr	realms = tmp;
602178825Sdfr	realms[num_realms] = strdup(tgt_realm);
603178825Sdfr	if(realms[num_realms] == NULL){
604178825Sdfr	    ret = ENOMEM;
605178825Sdfr	    goto free_realms;
606178825Sdfr	}
607178825Sdfr	num_realms++;
608178825Sdfr    }
609178825Sdfr    if(num_realms == 0) {
610178825Sdfr	if(strcmp(client_realm, server_realm))
611178825Sdfr	    kdc_log(context, config, 0,
612178825Sdfr		    "cross-realm %s -> %s", client_realm, server_realm);
613178825Sdfr    } else {
614178825Sdfr	size_t l = 0;
615178825Sdfr	char *rs;
616178825Sdfr	for(i = 0; i < num_realms; i++)
617178825Sdfr	    l += strlen(realms[i]) + 2;
618178825Sdfr	rs = malloc(l);
619178825Sdfr	if(rs != NULL) {
620178825Sdfr	    *rs = '\0';
621178825Sdfr	    for(i = 0; i < num_realms; i++) {
622178825Sdfr		if(i > 0)
623178825Sdfr		    strlcat(rs, ", ", l);
624178825Sdfr		strlcat(rs, realms[i], l);
625178825Sdfr	    }
626178825Sdfr	    kdc_log(context, config, 0,
627178825Sdfr		    "cross-realm %s -> %s via [%s]",
628178825Sdfr		    client_realm, server_realm, rs);
629178825Sdfr	    free(rs);
630178825Sdfr	}
631178825Sdfr    }
632178825Sdfr    if(check_policy) {
633178825Sdfr	ret = krb5_check_transited(context, client_realm,
634178825Sdfr				   server_realm,
635178825Sdfr				   realms, num_realms, NULL);
636178825Sdfr	if(ret) {
637178825Sdfr	    krb5_warn(context, ret, "cross-realm %s -> %s",
638178825Sdfr		      client_realm, server_realm);
639178825Sdfr	    goto free_realms;
640178825Sdfr	}
641178825Sdfr	et->flags.transited_policy_checked = 1;
642178825Sdfr    }
643178825Sdfr    et->transited.tr_type = DOMAIN_X500_COMPRESS;
644178825Sdfr    ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
645178825Sdfr    if(ret)
646178825Sdfr	krb5_warn(context, ret, "Encoding transited encoding");
647178825Sdfr  free_realms:
648178825Sdfr    for(i = 0; i < num_realms; i++)
649178825Sdfr	free(realms[i]);
650178825Sdfr    free(realms);
651178825Sdfr    return ret;
652178825Sdfr}
653178825Sdfr
654178825Sdfr
655178825Sdfrstatic krb5_error_code
656178825Sdfrtgs_make_reply(krb5_context context,
657178825Sdfr	       krb5_kdc_configuration *config,
658178825Sdfr	       KDC_REQ_BODY *b,
659178825Sdfr	       krb5_const_principal tgt_name,
660178825Sdfr	       const EncTicketPart *tgt,
661178825Sdfr	       const EncryptionKey *serverkey,
662178825Sdfr	       const krb5_keyblock *sessionkey,
663178825Sdfr	       krb5_kvno kvno,
664178825Sdfr	       AuthorizationData *auth_data,
665178825Sdfr	       hdb_entry_ex *server,
666178825Sdfr	       const char *server_name,
667178825Sdfr	       hdb_entry_ex *client,
668178825Sdfr	       krb5_principal client_principal,
669178825Sdfr	       hdb_entry_ex *krbtgt,
670178825Sdfr	       krb5_enctype krbtgt_etype,
671178825Sdfr	       KRB5SignedPathPrincipals *spp,
672178825Sdfr	       const krb5_data *rspac,
673178825Sdfr	       const char **e_text,
674178825Sdfr	       krb5_data *reply)
675178825Sdfr{
676178825Sdfr    KDC_REP rep;
677178825Sdfr    EncKDCRepPart ek;
678178825Sdfr    EncTicketPart et;
679178825Sdfr    KDCOptions f = b->kdc_options;
680178825Sdfr    krb5_error_code ret;
681178825Sdfr
682178825Sdfr    memset(&rep, 0, sizeof(rep));
683178825Sdfr    memset(&et, 0, sizeof(et));
684178825Sdfr    memset(&ek, 0, sizeof(ek));
685178825Sdfr
686178825Sdfr    rep.pvno = 5;
687178825Sdfr    rep.msg_type = krb_tgs_rep;
688178825Sdfr
689178825Sdfr    et.authtime = tgt->authtime;
690178825Sdfr    _kdc_fix_time(&b->till);
691178825Sdfr    et.endtime = min(tgt->endtime, *b->till);
692178825Sdfr    ALLOC(et.starttime);
693178825Sdfr    *et.starttime = kdc_time;
694178825Sdfr
695178825Sdfr    ret = check_tgs_flags(context, config, b, tgt, &et);
696178825Sdfr    if(ret)
697178825Sdfr	goto out;
698178825Sdfr
699178825Sdfr    /* We should check the transited encoding if:
700178825Sdfr       1) the request doesn't ask not to be checked
701178825Sdfr       2) globally enforcing a check
702178825Sdfr       3) principal requires checking
703178825Sdfr       4) we allow non-check per-principal, but principal isn't marked as allowing this
704178825Sdfr       5) we don't globally allow this
705178825Sdfr    */
706178825Sdfr
707178825Sdfr#define GLOBAL_FORCE_TRANSITED_CHECK		\
708178825Sdfr    (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
709178825Sdfr#define GLOBAL_ALLOW_PER_PRINCIPAL			\
710178825Sdfr    (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
711178825Sdfr#define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK			\
712178825Sdfr    (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
713178825Sdfr
714178825Sdfr/* these will consult the database in future release */
715178825Sdfr#define PRINCIPAL_FORCE_TRANSITED_CHECK(P)		0
716178825Sdfr#define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P)	0
717178825Sdfr
718178825Sdfr    ret = fix_transited_encoding(context, config,
719178825Sdfr				 !f.disable_transited_check ||
720178825Sdfr				 GLOBAL_FORCE_TRANSITED_CHECK ||
721178825Sdfr				 PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
722178825Sdfr				 !((GLOBAL_ALLOW_PER_PRINCIPAL &&
723178825Sdfr				    PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
724178825Sdfr				   GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
725178825Sdfr				 &tgt->transited, &et,
726178825Sdfr				 *krb5_princ_realm(context, client_principal),
727178825Sdfr				 *krb5_princ_realm(context, server->entry.principal),
728178825Sdfr				 *krb5_princ_realm(context, krbtgt->entry.principal));
729178825Sdfr    if(ret)
730178825Sdfr	goto out;
731178825Sdfr
732178825Sdfr    copy_Realm(krb5_princ_realm(context, server->entry.principal),
733178825Sdfr	       &rep.ticket.realm);
734178825Sdfr    _krb5_principal2principalname(&rep.ticket.sname, server->entry.principal);
735178825Sdfr    copy_Realm(&tgt_name->realm, &rep.crealm);
736178825Sdfr/*
737178825Sdfr    if (f.request_anonymous)
738178825Sdfr	_kdc_make_anonymous_principalname (&rep.cname);
739178825Sdfr    else */
740178825Sdfr
741178825Sdfr    copy_PrincipalName(&tgt_name->name, &rep.cname);
742178825Sdfr    rep.ticket.tkt_vno = 5;
743178825Sdfr
744178825Sdfr    ek.caddr = et.caddr;
745178825Sdfr    if(et.caddr == NULL)
746178825Sdfr	et.caddr = tgt->caddr;
747178825Sdfr
748178825Sdfr    {
749178825Sdfr	time_t life;
750178825Sdfr	life = et.endtime - *et.starttime;
751178825Sdfr	if(client && client->entry.max_life)
752178825Sdfr	    life = min(life, *client->entry.max_life);
753178825Sdfr	if(server->entry.max_life)
754178825Sdfr	    life = min(life, *server->entry.max_life);
755178825Sdfr	et.endtime = *et.starttime + life;
756178825Sdfr    }
757178825Sdfr    if(f.renewable_ok && tgt->flags.renewable &&
758178825Sdfr       et.renew_till == NULL && et.endtime < *b->till){
759178825Sdfr	et.flags.renewable = 1;
760178825Sdfr	ALLOC(et.renew_till);
761178825Sdfr	*et.renew_till = *b->till;
762178825Sdfr    }
763178825Sdfr    if(et.renew_till){
764178825Sdfr	time_t renew;
765178825Sdfr	renew = *et.renew_till - et.authtime;
766178825Sdfr	if(client && client->entry.max_renew)
767178825Sdfr	    renew = min(renew, *client->entry.max_renew);
768178825Sdfr	if(server->entry.max_renew)
769178825Sdfr	    renew = min(renew, *server->entry.max_renew);
770178825Sdfr	*et.renew_till = et.authtime + renew;
771178825Sdfr    }
772178825Sdfr
773178825Sdfr    if(et.renew_till){
774178825Sdfr	*et.renew_till = min(*et.renew_till, *tgt->renew_till);
775178825Sdfr	*et.starttime = min(*et.starttime, *et.renew_till);
776178825Sdfr	et.endtime = min(et.endtime, *et.renew_till);
777178825Sdfr    }
778178825Sdfr
779178825Sdfr    *et.starttime = min(*et.starttime, et.endtime);
780178825Sdfr
781178825Sdfr    if(*et.starttime == et.endtime){
782178825Sdfr	ret = KRB5KDC_ERR_NEVER_VALID;
783178825Sdfr	goto out;
784178825Sdfr    }
785178825Sdfr    if(et.renew_till && et.endtime == *et.renew_till){
786178825Sdfr	free(et.renew_till);
787178825Sdfr	et.renew_till = NULL;
788178825Sdfr	et.flags.renewable = 0;
789178825Sdfr    }
790178825Sdfr
791178825Sdfr    et.flags.pre_authent = tgt->flags.pre_authent;
792178825Sdfr    et.flags.hw_authent  = tgt->flags.hw_authent;
793178825Sdfr    et.flags.anonymous   = tgt->flags.anonymous;
794178825Sdfr    et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
795178825Sdfr
796178825Sdfr    if (auth_data) {
797178825Sdfr	/* XXX Check enc-authorization-data */
798178825Sdfr	et.authorization_data = calloc(1, sizeof(*et.authorization_data));
799178825Sdfr	if (et.authorization_data == NULL) {
800178825Sdfr	    ret = ENOMEM;
801178825Sdfr	    goto out;
802178825Sdfr	}
803178825Sdfr	ret = copy_AuthorizationData(auth_data, et.authorization_data);
804178825Sdfr	if (ret)
805178825Sdfr	    goto out;
806178825Sdfr
807178825Sdfr	/* Filter out type KRB5SignedPath */
808178825Sdfr	ret = find_KRB5SignedPath(context, et.authorization_data, NULL);
809178825Sdfr	if (ret == 0) {
810178825Sdfr	    if (et.authorization_data->len == 1) {
811178825Sdfr		free_AuthorizationData(et.authorization_data);
812178825Sdfr		free(et.authorization_data);
813178825Sdfr		et.authorization_data = NULL;
814178825Sdfr	    } else {
815178825Sdfr		AuthorizationData *ad = et.authorization_data;
816178825Sdfr		free_AuthorizationDataElement(&ad->val[ad->len - 1]);
817178825Sdfr		ad->len--;
818178825Sdfr	    }
819178825Sdfr	}
820178825Sdfr    }
821178825Sdfr
822178825Sdfr    if(rspac->length) {
823178825Sdfr	/*
824178825Sdfr	 * No not need to filter out the any PAC from the
825178825Sdfr	 * auth_data since it's signed by the KDC.
826178825Sdfr	 */
827178825Sdfr	ret = _kdc_tkt_add_if_relevant_ad(context, &et,
828178825Sdfr					  KRB5_AUTHDATA_WIN2K_PAC,
829178825Sdfr					  rspac);
830178825Sdfr	if (ret)
831178825Sdfr	    goto out;
832178825Sdfr    }
833178825Sdfr
834178825Sdfr    ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key);
835178825Sdfr    if (ret)
836178825Sdfr	goto out;
837178825Sdfr    et.crealm = tgt->crealm;
838178825Sdfr    et.cname = tgt_name->name;
839178825Sdfr
840178825Sdfr    ek.key = et.key;
841178825Sdfr    /* MIT must have at least one last_req */
842178825Sdfr    ek.last_req.len = 1;
843178825Sdfr    ek.last_req.val = calloc(1, sizeof(*ek.last_req.val));
844178825Sdfr    if (ek.last_req.val == NULL) {
845178825Sdfr	ret = ENOMEM;
846178825Sdfr	goto out;
847178825Sdfr    }
848178825Sdfr    ek.nonce = b->nonce;
849178825Sdfr    ek.flags = et.flags;
850178825Sdfr    ek.authtime = et.authtime;
851178825Sdfr    ek.starttime = et.starttime;
852178825Sdfr    ek.endtime = et.endtime;
853178825Sdfr    ek.renew_till = et.renew_till;
854178825Sdfr    ek.srealm = rep.ticket.realm;
855178825Sdfr    ek.sname = rep.ticket.sname;
856178825Sdfr
857178825Sdfr    _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime,
858178825Sdfr		       et.endtime, et.renew_till);
859178825Sdfr
860178825Sdfr    /* Don't sign cross realm tickets, they can't be checked anyway */
861178825Sdfr    {
862178825Sdfr	char *r = get_krbtgt_realm(&ek.sname);
863178825Sdfr
864178825Sdfr	if (r == NULL || strcmp(r, ek.srealm) == 0) {
865178825Sdfr	    ret = _kdc_add_KRB5SignedPath(context,
866178825Sdfr					  config,
867178825Sdfr					  krbtgt,
868178825Sdfr					  krbtgt_etype,
869178825Sdfr					  NULL,
870178825Sdfr					  spp,
871178825Sdfr					  &et);
872178825Sdfr	    if (ret)
873178825Sdfr		goto out;
874178825Sdfr	}
875178825Sdfr    }
876178825Sdfr
877178825Sdfr    /* It is somewhat unclear where the etype in the following
878178825Sdfr       encryption should come from. What we have is a session
879178825Sdfr       key in the passed tgt, and a list of preferred etypes
880178825Sdfr       *for the new ticket*. Should we pick the best possible
881178825Sdfr       etype, given the keytype in the tgt, or should we look
882178825Sdfr       at the etype list here as well?  What if the tgt
883178825Sdfr       session key is DES3 and we want a ticket with a (say)
884178825Sdfr       CAST session key. Should the DES3 etype be added to the
885178825Sdfr       etype list, even if we don't want a session key with
886178825Sdfr       DES3? */
887178825Sdfr    ret = _kdc_encode_reply(context, config,
888178825Sdfr			    &rep, &et, &ek, et.key.keytype,
889178825Sdfr			    kvno,
890178825Sdfr			    serverkey, 0, &tgt->key, e_text, reply);
891178825Sdfrout:
892178825Sdfr    free_TGS_REP(&rep);
893178825Sdfr    free_TransitedEncoding(&et.transited);
894178825Sdfr    if(et.starttime)
895178825Sdfr	free(et.starttime);
896178825Sdfr    if(et.renew_till)
897178825Sdfr	free(et.renew_till);
898178825Sdfr    if(et.authorization_data) {
899178825Sdfr	free_AuthorizationData(et.authorization_data);
900178825Sdfr	free(et.authorization_data);
901178825Sdfr    }
902178825Sdfr    free_LastReq(&ek.last_req);
903178825Sdfr    memset(et.key.keyvalue.data, 0, et.key.keyvalue.length);
904178825Sdfr    free_EncryptionKey(&et.key);
905178825Sdfr    return ret;
906178825Sdfr}
907178825Sdfr
908178825Sdfrstatic krb5_error_code
909178825Sdfrtgs_check_authenticator(krb5_context context,
910178825Sdfr			krb5_kdc_configuration *config,
911178825Sdfr	                krb5_auth_context ac,
912178825Sdfr			KDC_REQ_BODY *b,
913178825Sdfr			const char **e_text,
914178825Sdfr			krb5_keyblock *key)
915178825Sdfr{
916178825Sdfr    krb5_authenticator auth;
917178825Sdfr    size_t len;
918178825Sdfr    unsigned char *buf;
919178825Sdfr    size_t buf_size;
920178825Sdfr    krb5_error_code ret;
921178825Sdfr    krb5_crypto crypto;
922178825Sdfr
923178825Sdfr    krb5_auth_con_getauthenticator(context, ac, &auth);
924178825Sdfr    if(auth->cksum == NULL){
925178825Sdfr	kdc_log(context, config, 0, "No authenticator in request");
926178825Sdfr	ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
927178825Sdfr	goto out;
928178825Sdfr    }
929178825Sdfr    /*
930178825Sdfr     * according to RFC1510 it doesn't need to be keyed,
931178825Sdfr     * but according to the latest draft it needs to.
932178825Sdfr     */
933178825Sdfr    if (
934178825Sdfr#if 0
935178825Sdfr!krb5_checksum_is_keyed(context, auth->cksum->cksumtype)
936178825Sdfr	||
937178825Sdfr#endif
938178825Sdfr !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
939178825Sdfr	kdc_log(context, config, 0, "Bad checksum type in authenticator: %d",
940178825Sdfr		auth->cksum->cksumtype);
941178825Sdfr	ret =  KRB5KRB_AP_ERR_INAPP_CKSUM;
942178825Sdfr	goto out;
943178825Sdfr    }
944178825Sdfr
945178825Sdfr    /* XXX should not re-encode this */
946178825Sdfr    ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
947178825Sdfr    if(ret){
948178825Sdfr	kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s",
949178825Sdfr		krb5_get_err_text(context, ret));
950178825Sdfr	goto out;
951178825Sdfr    }
952178825Sdfr    if(buf_size != len) {
953178825Sdfr	free(buf);
954178825Sdfr	kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
955178825Sdfr	*e_text = "KDC internal error";
956178825Sdfr	ret = KRB5KRB_ERR_GENERIC;
957178825Sdfr	goto out;
958178825Sdfr    }
959178825Sdfr    ret = krb5_crypto_init(context, key, 0, &crypto);
960178825Sdfr    if (ret) {
961178825Sdfr	free(buf);
962178825Sdfr	kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
963178825Sdfr		krb5_get_err_text(context, ret));
964178825Sdfr	goto out;
965178825Sdfr    }
966178825Sdfr    ret = krb5_verify_checksum(context,
967178825Sdfr			       crypto,
968178825Sdfr			       KRB5_KU_TGS_REQ_AUTH_CKSUM,
969178825Sdfr			       buf,
970178825Sdfr			       len,
971178825Sdfr			       auth->cksum);
972178825Sdfr    free(buf);
973178825Sdfr    krb5_crypto_destroy(context, crypto);
974178825Sdfr    if(ret){
975178825Sdfr	kdc_log(context, config, 0,
976178825Sdfr		"Failed to verify authenticator checksum: %s",
977178825Sdfr		krb5_get_err_text(context, ret));
978178825Sdfr    }
979178825Sdfrout:
980178825Sdfr    free_Authenticator(auth);
981178825Sdfr    free(auth);
982178825Sdfr    return ret;
983178825Sdfr}
984178825Sdfr
985178825Sdfr/*
986178825Sdfr *
987178825Sdfr */
988178825Sdfr
989178825Sdfrstatic const char *
990178825Sdfrfind_rpath(krb5_context context, Realm crealm, Realm srealm)
991178825Sdfr{
992178825Sdfr    const char *new_realm = krb5_config_get_string(context,
993178825Sdfr						   NULL,
994178825Sdfr						   "capaths",
995178825Sdfr						   crealm,
996178825Sdfr						   srealm,
997178825Sdfr						   NULL);
998178825Sdfr    return new_realm;
999178825Sdfr}
1000178825Sdfr
1001178825Sdfr
1002178825Sdfrstatic krb5_boolean
1003178825Sdfrneed_referral(krb5_context context, krb5_principal server, krb5_realm **realms)
1004178825Sdfr{
1005178825Sdfr    if(server->name.name_type != KRB5_NT_SRV_INST ||
1006178825Sdfr       server->name.name_string.len != 2)
1007178825Sdfr	return FALSE;
1008178825Sdfr
1009178825Sdfr    return _krb5_get_host_realm_int(context, server->name.name_string.val[1],
1010178825Sdfr				    FALSE, realms) == 0;
1011178825Sdfr}
1012178825Sdfr
1013178825Sdfrstatic krb5_error_code
1014178825Sdfrtgs_parse_request(krb5_context context,
1015178825Sdfr		  krb5_kdc_configuration *config,
1016178825Sdfr		  KDC_REQ_BODY *b,
1017178825Sdfr		  const PA_DATA *tgs_req,
1018178825Sdfr		  hdb_entry_ex **krbtgt,
1019178825Sdfr		  krb5_enctype *krbtgt_etype,
1020178825Sdfr		  krb5_ticket **ticket,
1021178825Sdfr		  const char **e_text,
1022178825Sdfr		  const char *from,
1023178825Sdfr		  const struct sockaddr *from_addr,
1024178825Sdfr		  time_t **csec,
1025178825Sdfr		  int **cusec,
1026178825Sdfr		  AuthorizationData **auth_data)
1027178825Sdfr{
1028178825Sdfr    krb5_ap_req ap_req;
1029178825Sdfr    krb5_error_code ret;
1030178825Sdfr    krb5_principal princ;
1031178825Sdfr    krb5_auth_context ac = NULL;
1032178825Sdfr    krb5_flags ap_req_options;
1033178825Sdfr    krb5_flags verify_ap_req_flags;
1034178825Sdfr    krb5_crypto crypto;
1035178825Sdfr    Key *tkey;
1036178825Sdfr
1037178825Sdfr    *auth_data = NULL;
1038178825Sdfr    *csec  = NULL;
1039178825Sdfr    *cusec = NULL;
1040178825Sdfr
1041178825Sdfr    memset(&ap_req, 0, sizeof(ap_req));
1042178825Sdfr    ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
1043178825Sdfr    if(ret){
1044178825Sdfr	kdc_log(context, config, 0, "Failed to decode AP-REQ: %s",
1045178825Sdfr		krb5_get_err_text(context, ret));
1046178825Sdfr	goto out;
1047178825Sdfr    }
1048178825Sdfr
1049178825Sdfr    if(!get_krbtgt_realm(&ap_req.ticket.sname)){
1050178825Sdfr	/* XXX check for ticket.sname == req.sname */
1051178825Sdfr	kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
1052178825Sdfr	ret = KRB5KDC_ERR_POLICY; /* ? */
1053178825Sdfr	goto out;
1054178825Sdfr    }
1055178825Sdfr
1056178825Sdfr    _krb5_principalname2krb5_principal(context,
1057178825Sdfr				       &princ,
1058178825Sdfr				       ap_req.ticket.sname,
1059178825Sdfr				       ap_req.ticket.realm);
1060178825Sdfr
1061178825Sdfr    ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, NULL, krbtgt);
1062178825Sdfr
1063178825Sdfr    if(ret) {
1064178825Sdfr	char *p;
1065178825Sdfr	ret = krb5_unparse_name(context, princ, &p);
1066178825Sdfr	if (ret != 0)
1067178825Sdfr	    p = "<unparse_name failed>";
1068178825Sdfr	krb5_free_principal(context, princ);
1069178825Sdfr	kdc_log(context, config, 0,
1070178825Sdfr		"Ticket-granting ticket not found in database: %s: %s",
1071178825Sdfr		p, krb5_get_err_text(context, ret));
1072178825Sdfr	if (ret == 0)
1073178825Sdfr	    free(p);
1074178825Sdfr	ret = KRB5KRB_AP_ERR_NOT_US;
1075178825Sdfr	goto out;
1076178825Sdfr    }
1077178825Sdfr
1078178825Sdfr    if(ap_req.ticket.enc_part.kvno &&
1079178825Sdfr       *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){
1080178825Sdfr	char *p;
1081178825Sdfr
1082178825Sdfr	ret = krb5_unparse_name (context, princ, &p);
1083178825Sdfr	krb5_free_principal(context, princ);
1084178825Sdfr	if (ret != 0)
1085178825Sdfr	    p = "<unparse_name failed>";
1086178825Sdfr	kdc_log(context, config, 0,
1087178825Sdfr		"Ticket kvno = %d, DB kvno = %d (%s)",
1088178825Sdfr		*ap_req.ticket.enc_part.kvno,
1089178825Sdfr		(*krbtgt)->entry.kvno,
1090178825Sdfr		p);
1091178825Sdfr	if (ret == 0)
1092178825Sdfr	    free (p);
1093178825Sdfr	ret = KRB5KRB_AP_ERR_BADKEYVER;
1094178825Sdfr	goto out;
1095178825Sdfr    }
1096178825Sdfr
1097178825Sdfr    *krbtgt_etype = ap_req.ticket.enc_part.etype;
1098178825Sdfr
1099178825Sdfr    ret = hdb_enctype2key(context, &(*krbtgt)->entry,
1100178825Sdfr			  ap_req.ticket.enc_part.etype, &tkey);
1101178825Sdfr    if(ret){
1102178825Sdfr	char *str = NULL, *p = NULL;
1103178825Sdfr
1104178825Sdfr	krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
1105178825Sdfr	krb5_unparse_name(context, princ, &p);
1106178825Sdfr 	kdc_log(context, config, 0,
1107178825Sdfr		"No server key with enctype %s found for %s",
1108178825Sdfr		str ? str : "<unknown enctype>",
1109178825Sdfr		p ? p : "<unparse_name failed>");
1110178825Sdfr	free(str);
1111178825Sdfr	free(p);
1112178825Sdfr	ret = KRB5KRB_AP_ERR_BADKEYVER;
1113178825Sdfr	goto out;
1114178825Sdfr    }
1115178825Sdfr
1116178825Sdfr    if (b->kdc_options.validate)
1117178825Sdfr	verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
1118178825Sdfr    else
1119178825Sdfr	verify_ap_req_flags = 0;
1120178825Sdfr
1121178825Sdfr    ret = krb5_verify_ap_req2(context,
1122178825Sdfr			      &ac,
1123178825Sdfr			      &ap_req,
1124178825Sdfr			      princ,
1125178825Sdfr			      &tkey->key,
1126178825Sdfr			      verify_ap_req_flags,
1127178825Sdfr			      &ap_req_options,
1128178825Sdfr			      ticket,
1129178825Sdfr			      KRB5_KU_TGS_REQ_AUTH);
1130178825Sdfr
1131178825Sdfr    krb5_free_principal(context, princ);
1132178825Sdfr    if(ret) {
1133178825Sdfr	kdc_log(context, config, 0, "Failed to verify AP-REQ: %s",
1134178825Sdfr		krb5_get_err_text(context, ret));
1135178825Sdfr	goto out;
1136178825Sdfr    }
1137178825Sdfr
1138178825Sdfr    {
1139178825Sdfr	krb5_authenticator auth;
1140178825Sdfr
1141178825Sdfr	ret = krb5_auth_con_getauthenticator(context, ac, &auth);
1142178825Sdfr	if (ret == 0) {
1143178825Sdfr	    *csec   = malloc(sizeof(**csec));
1144178825Sdfr	    if (*csec == NULL) {
1145178825Sdfr		krb5_free_authenticator(context, &auth);
1146178825Sdfr		kdc_log(context, config, 0, "malloc failed");
1147178825Sdfr		goto out;
1148178825Sdfr	    }
1149178825Sdfr	    **csec  = auth->ctime;
1150178825Sdfr	    *cusec  = malloc(sizeof(**cusec));
1151178825Sdfr	    if (*cusec == NULL) {
1152178825Sdfr		krb5_free_authenticator(context, &auth);
1153178825Sdfr		kdc_log(context, config, 0, "malloc failed");
1154178825Sdfr		goto out;
1155178825Sdfr	    }
1156178825Sdfr	    **cusec  = auth->cusec;
1157178825Sdfr	    krb5_free_authenticator(context, &auth);
1158178825Sdfr	}
1159178825Sdfr    }
1160178825Sdfr
1161178825Sdfr    ret = tgs_check_authenticator(context, config,
1162178825Sdfr				  ac, b, e_text, &(*ticket)->ticket.key);
1163178825Sdfr    if (ret) {
1164178825Sdfr	krb5_auth_con_free(context, ac);
1165178825Sdfr	goto out;
1166178825Sdfr    }
1167178825Sdfr
1168178825Sdfr    if (b->enc_authorization_data) {
1169178825Sdfr	unsigned usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
1170178825Sdfr	krb5_keyblock *subkey;
1171178825Sdfr	krb5_data ad;
1172178825Sdfr
1173178825Sdfr	ret = krb5_auth_con_getremotesubkey(context,
1174178825Sdfr					    ac,
1175178825Sdfr					    &subkey);
1176178825Sdfr	if(ret){
1177178825Sdfr	    krb5_auth_con_free(context, ac);
1178178825Sdfr	    kdc_log(context, config, 0, "Failed to get remote subkey: %s",
1179178825Sdfr		    krb5_get_err_text(context, ret));
1180178825Sdfr	    goto out;
1181178825Sdfr	}
1182178825Sdfr	if(subkey == NULL){
1183178825Sdfr	    usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
1184178825Sdfr	    ret = krb5_auth_con_getkey(context, ac, &subkey);
1185178825Sdfr	    if(ret) {
1186178825Sdfr		krb5_auth_con_free(context, ac);
1187178825Sdfr		kdc_log(context, config, 0, "Failed to get session key: %s",
1188178825Sdfr			krb5_get_err_text(context, ret));
1189178825Sdfr		goto out;
1190178825Sdfr	    }
1191178825Sdfr	}
1192178825Sdfr	if(subkey == NULL){
1193178825Sdfr	    krb5_auth_con_free(context, ac);
1194178825Sdfr	    kdc_log(context, config, 0,
1195178825Sdfr		    "Failed to get key for enc-authorization-data");
1196178825Sdfr	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1197178825Sdfr	    goto out;
1198178825Sdfr	}
1199178825Sdfr	ret = krb5_crypto_init(context, subkey, 0, &crypto);
1200178825Sdfr	if (ret) {
1201178825Sdfr	    krb5_auth_con_free(context, ac);
1202178825Sdfr	    kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
1203178825Sdfr		    krb5_get_err_text(context, ret));
1204178825Sdfr	    goto out;
1205178825Sdfr	}
1206178825Sdfr	ret = krb5_decrypt_EncryptedData (context,
1207178825Sdfr					  crypto,
1208178825Sdfr					  usage,
1209178825Sdfr					  b->enc_authorization_data,
1210178825Sdfr					  &ad);
1211178825Sdfr	krb5_crypto_destroy(context, crypto);
1212178825Sdfr	if(ret){
1213178825Sdfr	    krb5_auth_con_free(context, ac);
1214178825Sdfr	    kdc_log(context, config, 0,
1215178825Sdfr		    "Failed to decrypt enc-authorization-data");
1216178825Sdfr	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1217178825Sdfr	    goto out;
1218178825Sdfr	}
1219178825Sdfr	krb5_free_keyblock(context, subkey);
1220178825Sdfr	ALLOC(*auth_data);
1221178825Sdfr	if (*auth_data == NULL) {
1222178825Sdfr	    krb5_auth_con_free(context, ac);
1223178825Sdfr	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1224178825Sdfr	    goto out;
1225178825Sdfr	}
1226178825Sdfr	ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
1227178825Sdfr	if(ret){
1228178825Sdfr	    krb5_auth_con_free(context, ac);
1229178825Sdfr	    free(*auth_data);
1230178825Sdfr	    *auth_data = NULL;
1231178825Sdfr	    kdc_log(context, config, 0, "Failed to decode authorization data");
1232178825Sdfr	    ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1233178825Sdfr	    goto out;
1234178825Sdfr	}
1235178825Sdfr    }
1236178825Sdfr
1237178825Sdfr    krb5_auth_con_free(context, ac);
1238178825Sdfr
1239178825Sdfrout:
1240178825Sdfr    free_AP_REQ(&ap_req);
1241178825Sdfr
1242178825Sdfr    return ret;
1243178825Sdfr}
1244178825Sdfr
1245178825Sdfrstatic krb5_error_code
1246178825Sdfrtgs_build_reply(krb5_context context,
1247178825Sdfr		krb5_kdc_configuration *config,
1248178825Sdfr		KDC_REQ *req,
1249178825Sdfr		KDC_REQ_BODY *b,
1250178825Sdfr		hdb_entry_ex *krbtgt,
1251178825Sdfr		krb5_enctype krbtgt_etype,
1252178825Sdfr		krb5_ticket *ticket,
1253178825Sdfr		krb5_data *reply,
1254178825Sdfr		const char *from,
1255178825Sdfr		const char **e_text,
1256178825Sdfr		AuthorizationData *auth_data,
1257178825Sdfr		const struct sockaddr *from_addr,
1258178825Sdfr		int datagram_reply)
1259178825Sdfr{
1260178825Sdfr    krb5_error_code ret;
1261178825Sdfr    krb5_principal cp = NULL, sp = NULL;
1262178825Sdfr    krb5_principal client_principal = NULL;
1263178825Sdfr    char *spn = NULL, *cpn = NULL;
1264178825Sdfr    hdb_entry_ex *server = NULL, *client = NULL;
1265178825Sdfr    EncTicketPart *tgt = &ticket->ticket;
1266178825Sdfr    KRB5SignedPathPrincipals *spp = NULL;
1267178825Sdfr    const EncryptionKey *ekey;
1268178825Sdfr    krb5_keyblock sessionkey;
1269178825Sdfr    krb5_kvno kvno;
1270178825Sdfr    krb5_data rspac;
1271178825Sdfr    int cross_realm = 0;
1272178825Sdfr
1273178825Sdfr    PrincipalName *s;
1274178825Sdfr    Realm r;
1275178825Sdfr    int nloop = 0;
1276178825Sdfr    EncTicketPart adtkt;
1277178825Sdfr    char opt_str[128];
1278178825Sdfr    int require_signedpath = 0;
1279178825Sdfr
1280178825Sdfr    memset(&sessionkey, 0, sizeof(sessionkey));
1281178825Sdfr    memset(&adtkt, 0, sizeof(adtkt));
1282178825Sdfr    krb5_data_zero(&rspac);
1283178825Sdfr
1284178825Sdfr    s = b->sname;
1285178825Sdfr    r = b->realm;
1286178825Sdfr
1287178825Sdfr    if(b->kdc_options.enc_tkt_in_skey){
1288178825Sdfr	Ticket *t;
1289178825Sdfr	hdb_entry_ex *uu;
1290178825Sdfr	krb5_principal p;
1291178825Sdfr	Key *uukey;
1292178825Sdfr
1293178825Sdfr	if(b->additional_tickets == NULL ||
1294178825Sdfr	   b->additional_tickets->len == 0){
1295178825Sdfr	    ret = KRB5KDC_ERR_BADOPTION; /* ? */
1296178825Sdfr	    kdc_log(context, config, 0,
1297178825Sdfr		    "No second ticket present in request");
1298178825Sdfr	    goto out;
1299178825Sdfr	}
1300178825Sdfr	t = &b->additional_tickets->val[0];
1301178825Sdfr	if(!get_krbtgt_realm(&t->sname)){
1302178825Sdfr	    kdc_log(context, config, 0,
1303178825Sdfr		    "Additional ticket is not a ticket-granting ticket");
1304178825Sdfr	    ret = KRB5KDC_ERR_POLICY;
1305178825Sdfr	    goto out;
1306178825Sdfr	}
1307178825Sdfr	_krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
1308178825Sdfr	ret = _kdc_db_fetch(context, config, p,
1309178825Sdfr			    HDB_F_GET_CLIENT|HDB_F_GET_SERVER,
1310178825Sdfr			    NULL, &uu);
1311178825Sdfr	krb5_free_principal(context, p);
1312178825Sdfr	if(ret){
1313178825Sdfr	    if (ret == HDB_ERR_NOENTRY)
1314178825Sdfr		ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1315178825Sdfr	    goto out;
1316178825Sdfr	}
1317178825Sdfr	ret = hdb_enctype2key(context, &uu->entry,
1318178825Sdfr			      t->enc_part.etype, &uukey);
1319178825Sdfr	if(ret){
1320178825Sdfr	    _kdc_free_ent(context, uu);
1321178825Sdfr	    ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1322178825Sdfr	    goto out;
1323178825Sdfr	}
1324178825Sdfr	ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
1325178825Sdfr	_kdc_free_ent(context, uu);
1326178825Sdfr	if(ret)
1327178825Sdfr	    goto out;
1328178825Sdfr
1329178825Sdfr	ret = verify_flags(context, config, &adtkt, spn);
1330178825Sdfr	if (ret)
1331178825Sdfr	    goto out;
1332178825Sdfr
1333178825Sdfr	s = &adtkt.cname;
1334178825Sdfr	r = adtkt.crealm;
1335178825Sdfr    }
1336178825Sdfr
1337178825Sdfr    _krb5_principalname2krb5_principal(context, &sp, *s, r);
1338178825Sdfr    ret = krb5_unparse_name(context, sp, &spn);
1339178825Sdfr    if (ret)
1340178825Sdfr	goto out;
1341178825Sdfr    _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
1342178825Sdfr    ret = krb5_unparse_name(context, cp, &cpn);
1343178825Sdfr    if (ret)
1344178825Sdfr	goto out;
1345178825Sdfr    unparse_flags (KDCOptions2int(b->kdc_options),
1346178825Sdfr		   asn1_KDCOptions_units(),
1347178825Sdfr		   opt_str, sizeof(opt_str));
1348178825Sdfr    if(*opt_str)
1349178825Sdfr	kdc_log(context, config, 0,
1350178825Sdfr		"TGS-REQ %s from %s for %s [%s]",
1351178825Sdfr		cpn, from, spn, opt_str);
1352178825Sdfr    else
1353178825Sdfr	kdc_log(context, config, 0,
1354178825Sdfr		"TGS-REQ %s from %s for %s", cpn, from, spn);
1355178825Sdfr
1356178825Sdfr    /*
1357178825Sdfr     * Fetch server
1358178825Sdfr     */
1359178825Sdfr
1360178825Sdfrserver_lookup:
1361178825Sdfr    ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER, NULL, &server);
1362178825Sdfr
1363178825Sdfr    if(ret){
1364178825Sdfr	const char *new_rlm;
1365178825Sdfr	Realm req_rlm;
1366178825Sdfr	krb5_realm *realms;
1367178825Sdfr
1368178825Sdfr	if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
1369178825Sdfr	    if(nloop++ < 2) {
1370178825Sdfr		new_rlm = find_rpath(context, tgt->crealm, req_rlm);
1371178825Sdfr		if(new_rlm) {
1372178825Sdfr		    kdc_log(context, config, 5, "krbtgt for realm %s "
1373178825Sdfr			    "not found, trying %s",
1374178825Sdfr			    req_rlm, new_rlm);
1375178825Sdfr		    krb5_free_principal(context, sp);
1376178825Sdfr		    free(spn);
1377178825Sdfr		    krb5_make_principal(context, &sp, r,
1378178825Sdfr					KRB5_TGS_NAME, new_rlm, NULL);
1379178825Sdfr		    ret = krb5_unparse_name(context, sp, &spn);
1380178825Sdfr		    if (ret)
1381178825Sdfr			goto out;
1382178825Sdfr		    auth_data = NULL; /* ms don't handle AD in referals */
1383178825Sdfr		    goto server_lookup;
1384178825Sdfr		}
1385178825Sdfr	    }
1386178825Sdfr	} else if(need_referral(context, sp, &realms)) {
1387178825Sdfr	    if (strcmp(realms[0], sp->realm) != 0) {
1388178825Sdfr		kdc_log(context, config, 5,
1389178825Sdfr			"Returning a referral to realm %s for "
1390178825Sdfr			"server %s that was not found",
1391178825Sdfr			realms[0], spn);
1392178825Sdfr		krb5_free_principal(context, sp);
1393178825Sdfr		free(spn);
1394178825Sdfr		krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1395178825Sdfr				    realms[0], NULL);
1396178825Sdfr		ret = krb5_unparse_name(context, sp, &spn);
1397178825Sdfr		if (ret)
1398178825Sdfr		    goto out;
1399178825Sdfr		krb5_free_host_realm(context, realms);
1400178825Sdfr		auth_data = NULL; /* ms don't handle AD in referals */
1401178825Sdfr		goto server_lookup;
1402178825Sdfr	    }
1403178825Sdfr	    krb5_free_host_realm(context, realms);
1404178825Sdfr	}
1405178825Sdfr	kdc_log(context, config, 0,
1406178825Sdfr		"Server not found in database: %s: %s", spn,
1407178825Sdfr		krb5_get_err_text(context, ret));
1408178825Sdfr	if (ret == HDB_ERR_NOENTRY)
1409178825Sdfr	    ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1410178825Sdfr	goto out;
1411178825Sdfr    }
1412178825Sdfr
1413178825Sdfr    ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT, NULL, &client);
1414178825Sdfr    if(ret) {
1415178825Sdfr	const char *krbtgt_realm;
1416178825Sdfr
1417178825Sdfr	/*
1418178825Sdfr	 * If the client belongs to the same realm as our krbtgt, it
1419178825Sdfr	 * should exist in the local database.
1420178825Sdfr	 *
1421178825Sdfr	 */
1422178825Sdfr
1423178825Sdfr	krbtgt_realm =
1424178825Sdfr	    krb5_principal_get_comp_string(context,
1425178825Sdfr					   krbtgt->entry.principal, 1);
1426178825Sdfr
1427178825Sdfr	if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
1428178825Sdfr	    if (ret == HDB_ERR_NOENTRY)
1429178825Sdfr		ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1430178825Sdfr	    kdc_log(context, config, 1, "Client no longer in database: %s",
1431178825Sdfr		    cpn);
1432178825Sdfr	    goto out;
1433178825Sdfr	}
1434178825Sdfr
1435178825Sdfr	kdc_log(context, config, 1, "Client not found in database: %s: %s",
1436178825Sdfr		cpn, krb5_get_err_text(context, ret));
1437178825Sdfr
1438178825Sdfr	cross_realm = 1;
1439178825Sdfr    }
1440178825Sdfr
1441178825Sdfr    /*
1442178825Sdfr     * Check that service is in the same realm as the krbtgt. If it's
1443178825Sdfr     * not the same, it's someone that is using a uni-directional trust
1444178825Sdfr     * backward.
1445178825Sdfr     */
1446178825Sdfr
1447178825Sdfr    if (strcmp(krb5_principal_get_realm(context, sp),
1448178825Sdfr	       krb5_principal_get_comp_string(context,
1449178825Sdfr					      krbtgt->entry.principal,
1450178825Sdfr					      1)) != 0) {
1451178825Sdfr	char *tpn;
1452178825Sdfr	ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn);
1453178825Sdfr	kdc_log(context, config, 0,
1454178825Sdfr		"Request with wrong krbtgt: %s",
1455178825Sdfr		(ret == 0) ? tpn : "<unknown>");
1456178825Sdfr	if(ret == 0)
1457178825Sdfr	    free(tpn);
1458178825Sdfr	ret = KRB5KRB_AP_ERR_NOT_US;
1459178825Sdfr	goto out;
1460178825Sdfr    }
1461178825Sdfr
1462178825Sdfr    /*
1463178825Sdfr     *
1464178825Sdfr     */
1465178825Sdfr
1466178825Sdfr    client_principal = cp;
1467178825Sdfr
1468178825Sdfr    if (client) {
1469178825Sdfr	const PA_DATA *sdata;
1470178825Sdfr	int i = 0;
1471178825Sdfr
1472178825Sdfr	sdata = _kdc_find_padata(req, &i, KRB5_PADATA_S4U2SELF);
1473178825Sdfr	if (sdata) {
1474178825Sdfr	    krb5_crypto crypto;
1475178825Sdfr	    krb5_data datack;
1476178825Sdfr	    PA_S4U2Self self;
1477178825Sdfr	    char *selfcpn = NULL;
1478178825Sdfr	    const char *str;
1479178825Sdfr
1480178825Sdfr	    ret = decode_PA_S4U2Self(sdata->padata_value.data,
1481178825Sdfr				     sdata->padata_value.length,
1482178825Sdfr				     &self, NULL);
1483178825Sdfr	    if (ret) {
1484178825Sdfr		kdc_log(context, config, 0, "Failed to decode PA-S4U2Self");
1485178825Sdfr		goto out;
1486178825Sdfr	    }
1487178825Sdfr
1488178825Sdfr	    ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
1489178825Sdfr	    if (ret)
1490178825Sdfr		goto out;
1491178825Sdfr
1492178825Sdfr	    ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
1493178825Sdfr	    if (ret) {
1494178825Sdfr		free_PA_S4U2Self(&self);
1495178825Sdfr		krb5_data_free(&datack);
1496178825Sdfr		kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
1497178825Sdfr			krb5_get_err_text(context, ret));
1498178825Sdfr		goto out;
1499178825Sdfr	    }
1500178825Sdfr
1501178825Sdfr	    ret = krb5_verify_checksum(context,
1502178825Sdfr				       crypto,
1503178825Sdfr				       KRB5_KU_OTHER_CKSUM,
1504178825Sdfr				       datack.data,
1505178825Sdfr				       datack.length,
1506178825Sdfr				       &self.cksum);
1507178825Sdfr	    krb5_data_free(&datack);
1508178825Sdfr	    krb5_crypto_destroy(context, crypto);
1509178825Sdfr	    if (ret) {
1510178825Sdfr		free_PA_S4U2Self(&self);
1511178825Sdfr		kdc_log(context, config, 0,
1512178825Sdfr			"krb5_verify_checksum failed for S4U2Self: %s",
1513178825Sdfr			krb5_get_err_text(context, ret));
1514178825Sdfr		goto out;
1515178825Sdfr	    }
1516178825Sdfr
1517178825Sdfr	    ret = _krb5_principalname2krb5_principal(context,
1518178825Sdfr						     &client_principal,
1519178825Sdfr						     self.name,
1520178825Sdfr						     self.realm);
1521178825Sdfr	    free_PA_S4U2Self(&self);
1522178825Sdfr	    if (ret)
1523178825Sdfr		goto out;
1524178825Sdfr
1525178825Sdfr	    ret = krb5_unparse_name(context, client_principal, &selfcpn);
1526178825Sdfr	    if (ret)
1527178825Sdfr		goto out;
1528178825Sdfr
1529178825Sdfr	    /*
1530178825Sdfr	     * Check that service doing the impersonating is
1531178825Sdfr	     * requesting a ticket to it-self.
1532178825Sdfr	     */
1533178825Sdfr	    if (krb5_principal_compare(context, cp, sp) != TRUE) {
1534178825Sdfr		kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
1535178825Sdfr			"to impersonate some other user "
1536178825Sdfr			"(tried for user %s to service %s)",
1537178825Sdfr			cpn, selfcpn, spn);
1538178825Sdfr		free(selfcpn);
1539178825Sdfr		ret = KRB5KDC_ERR_BADOPTION; /* ? */
1540178825Sdfr		goto out;
1541178825Sdfr	    }
1542178825Sdfr
1543178825Sdfr	    /*
1544178825Sdfr	     * If the service isn't trusted for authentication to
1545178825Sdfr	     * delegation, remove the forward flag.
1546178825Sdfr	     */
1547178825Sdfr
1548178825Sdfr	    if (client->entry.flags.trusted_for_delegation) {
1549178825Sdfr		str = "[forwardable]";
1550178825Sdfr	    } else {
1551178825Sdfr		b->kdc_options.forwardable = 0;
1552178825Sdfr		str = "";
1553178825Sdfr	    }
1554178825Sdfr	    kdc_log(context, config, 0, "s4u2self %s impersonating %s to "
1555178825Sdfr		    "service %s %s", cpn, selfcpn, spn, str);
1556178825Sdfr	    free(selfcpn);
1557178825Sdfr	}
1558178825Sdfr    }
1559178825Sdfr
1560178825Sdfr    /*
1561178825Sdfr     * Constrained delegation
1562178825Sdfr     */
1563178825Sdfr
1564178825Sdfr    if (client != NULL
1565178825Sdfr	&& b->additional_tickets != NULL
1566178825Sdfr	&& b->additional_tickets->len != 0
1567178825Sdfr	&& b->kdc_options.enc_tkt_in_skey == 0)
1568178825Sdfr    {
1569178825Sdfr	Key *clientkey;
1570178825Sdfr	Ticket *t;
1571178825Sdfr	char *str;
1572178825Sdfr
1573178825Sdfr	t = &b->additional_tickets->val[0];
1574178825Sdfr
1575178825Sdfr	ret = hdb_enctype2key(context, &client->entry,
1576178825Sdfr			      t->enc_part.etype, &clientkey);
1577178825Sdfr	if(ret){
1578178825Sdfr	    ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1579178825Sdfr	    goto out;
1580178825Sdfr	}
1581178825Sdfr
1582178825Sdfr	ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
1583178825Sdfr	if (ret) {
1584178825Sdfr	    kdc_log(context, config, 0,
1585178825Sdfr		    "failed to decrypt ticket for "
1586178825Sdfr		    "constrained delegation from %s to %s ", spn, cpn);
1587178825Sdfr	    goto out;
1588178825Sdfr	}
1589178825Sdfr
1590178825Sdfr	/* check that ticket is valid */
1591178825Sdfr
1592178825Sdfr	if (adtkt.flags.forwardable == 0) {
1593178825Sdfr	    kdc_log(context, config, 0,
1594178825Sdfr		    "Missing forwardable flag on ticket for "
1595178825Sdfr		    "constrained delegation from %s to %s ", spn, cpn);
1596178825Sdfr	    ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1597178825Sdfr	    goto out;
1598178825Sdfr	}
1599178825Sdfr
1600178825Sdfr	ret = check_constrained_delegation(context, config, client, sp);
1601178825Sdfr	if (ret) {
1602178825Sdfr	    kdc_log(context, config, 0,
1603178825Sdfr		    "constrained delegation from %s to %s not allowed",
1604178825Sdfr		    spn, cpn);
1605178825Sdfr	    goto out;
1606178825Sdfr	}
1607178825Sdfr
1608178825Sdfr	ret = _krb5_principalname2krb5_principal(context,
1609178825Sdfr						 &client_principal,
1610178825Sdfr						 adtkt.cname,
1611178825Sdfr						 adtkt.crealm);
1612178825Sdfr	if (ret)
1613178825Sdfr	    goto out;
1614178825Sdfr
1615178825Sdfr	ret = krb5_unparse_name(context, client_principal, &str);
1616178825Sdfr	if (ret)
1617178825Sdfr	    goto out;
1618178825Sdfr
1619178825Sdfr	ret = verify_flags(context, config, &adtkt, str);
1620178825Sdfr	if (ret) {
1621178825Sdfr	    free(str);
1622178825Sdfr	    goto out;
1623178825Sdfr	}
1624178825Sdfr
1625178825Sdfr	/*
1626178825Sdfr	 * Check KRB5SignedPath in authorization data and add new entry to
1627178825Sdfr	 * make sure servers can't fake a ticket to us.
1628178825Sdfr	 */
1629178825Sdfr
1630178825Sdfr	ret = check_KRB5SignedPath(context,
1631178825Sdfr				   config,
1632178825Sdfr				   krbtgt,
1633178825Sdfr				   &adtkt,
1634178825Sdfr				   &spp,
1635178825Sdfr				   1);
1636178825Sdfr	if (ret) {
1637178825Sdfr	    kdc_log(context, config, 0,
1638178825Sdfr		    "KRB5SignedPath check from service %s failed "
1639178825Sdfr		    "for delegation to %s for client %s "
1640178825Sdfr		    "from %s failed with %s",
1641178825Sdfr		    spn, str, cpn, from, krb5_get_err_text(context, ret));
1642178825Sdfr	    free(str);
1643178825Sdfr	    goto out;
1644178825Sdfr	}
1645178825Sdfr
1646178825Sdfr	kdc_log(context, config, 0, "constrained delegation for %s "
1647178825Sdfr		"from %s to %s", str, cpn, spn);
1648178825Sdfr	free(str);
1649178825Sdfr
1650178825Sdfr	/*
1651178825Sdfr	 * Also require that the KDC have issue the service's krbtgt
1652178825Sdfr	 * used to do the request.
1653178825Sdfr	 */
1654178825Sdfr	require_signedpath = 1;
1655178825Sdfr    }
1656178825Sdfr
1657178825Sdfr    /*
1658178825Sdfr     * Check flags
1659178825Sdfr     */
1660178825Sdfr
1661178825Sdfr    ret = _kdc_check_flags(context, config,
1662178825Sdfr			   client, cpn,
1663178825Sdfr			   server, spn,
1664178825Sdfr			   FALSE);
1665178825Sdfr    if(ret)
1666178825Sdfr	goto out;
1667178825Sdfr
1668178825Sdfr    if((b->kdc_options.validate || b->kdc_options.renew) &&
1669178825Sdfr       !krb5_principal_compare(context,
1670178825Sdfr			       krbtgt->entry.principal,
1671178825Sdfr			       server->entry.principal)){
1672178825Sdfr	kdc_log(context, config, 0, "Inconsistent request.");
1673178825Sdfr	ret = KRB5KDC_ERR_SERVER_NOMATCH;
1674178825Sdfr	goto out;
1675178825Sdfr    }
1676178825Sdfr
1677178825Sdfr    /* check for valid set of addresses */
1678178825Sdfr    if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) {
1679178825Sdfr	ret = KRB5KRB_AP_ERR_BADADDR;
1680178825Sdfr	kdc_log(context, config, 0, "Request from wrong address");
1681178825Sdfr	goto out;
1682178825Sdfr    }
1683178825Sdfr
1684178825Sdfr    /*
1685178825Sdfr     * Select enctype, return key and kvno.
1686178825Sdfr     */
1687178825Sdfr
1688178825Sdfr    {
1689178825Sdfr	krb5_enctype etype;
1690178825Sdfr
1691178825Sdfr	if(b->kdc_options.enc_tkt_in_skey) {
1692178825Sdfr	    int i;
1693178825Sdfr	    ekey = &adtkt.key;
1694178825Sdfr	    for(i = 0; i < b->etype.len; i++)
1695178825Sdfr		if (b->etype.val[i] == adtkt.key.keytype)
1696178825Sdfr		    break;
1697178825Sdfr	    if(i == b->etype.len) {
1698178825Sdfr		krb5_clear_error_string(context);
1699178825Sdfr		return KRB5KDC_ERR_ETYPE_NOSUPP;
1700178825Sdfr	    }
1701178825Sdfr	    etype = b->etype.val[i];
1702178825Sdfr	    kvno = 0;
1703178825Sdfr	} else {
1704178825Sdfr	    Key *skey;
1705178825Sdfr
1706178825Sdfr	    ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len,
1707178825Sdfr				  &skey, &etype);
1708178825Sdfr	    if(ret) {
1709178825Sdfr		kdc_log(context, config, 0,
1710178825Sdfr			"Server (%s) has no support for etypes", spp);
1711178825Sdfr		return ret;
1712178825Sdfr	    }
1713178825Sdfr	    ekey = &skey->key;
1714178825Sdfr	    kvno = server->entry.kvno;
1715178825Sdfr	}
1716178825Sdfr
1717178825Sdfr	ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
1718178825Sdfr	if (ret)
1719178825Sdfr	    goto out;
1720178825Sdfr    }
1721178825Sdfr
1722178825Sdfr    /* check PAC if not cross realm and if there is one */
1723178825Sdfr    if (!cross_realm) {
1724178825Sdfr	Key *tkey;
1725178825Sdfr
1726178825Sdfr	ret = hdb_enctype2key(context, &krbtgt->entry,
1727178825Sdfr			      krbtgt_etype, &tkey);
1728178825Sdfr	if(ret) {
1729178825Sdfr	    kdc_log(context, config, 0,
1730178825Sdfr		    "Failed to find key for krbtgt PAC check");
1731178825Sdfr	    goto out;
1732178825Sdfr	}
1733178825Sdfr
1734178825Sdfr	ret = check_PAC(context, config, client_principal,
1735178825Sdfr			client, server, ekey, &tkey->key,
1736178825Sdfr			tgt, &rspac, &require_signedpath);
1737178825Sdfr	if (ret) {
1738178825Sdfr	    kdc_log(context, config, 0,
1739178825Sdfr		    "Verify PAC failed for %s (%s) from %s with %s",
1740178825Sdfr		    spn, cpn, from, krb5_get_err_text(context, ret));
1741178825Sdfr	    goto out;
1742178825Sdfr	}
1743178825Sdfr    }
1744178825Sdfr
1745178825Sdfr    /* also check the krbtgt for signature */
1746178825Sdfr    ret = check_KRB5SignedPath(context,
1747178825Sdfr			       config,
1748178825Sdfr			       krbtgt,
1749178825Sdfr			       tgt,
1750178825Sdfr			       &spp,
1751178825Sdfr			       require_signedpath);
1752178825Sdfr    if (ret) {
1753178825Sdfr	kdc_log(context, config, 0,
1754178825Sdfr		"KRB5SignedPath check failed for %s (%s) from %s with %s",
1755178825Sdfr		spn, cpn, from, krb5_get_err_text(context, ret));
1756178825Sdfr	goto out;
1757178825Sdfr    }
1758178825Sdfr
1759178825Sdfr    /*
1760178825Sdfr     *
1761178825Sdfr     */
1762178825Sdfr
1763178825Sdfr    ret = tgs_make_reply(context,
1764178825Sdfr			 config,
1765178825Sdfr			 b,
1766178825Sdfr			 client_principal,
1767178825Sdfr			 tgt,
1768178825Sdfr			 ekey,
1769178825Sdfr			 &sessionkey,
1770178825Sdfr			 kvno,
1771178825Sdfr			 auth_data,
1772178825Sdfr			 server,
1773178825Sdfr			 spn,
1774178825Sdfr			 client,
1775178825Sdfr			 cp,
1776178825Sdfr			 krbtgt,
1777178825Sdfr			 krbtgt_etype,
1778178825Sdfr			 spp,
1779178825Sdfr			 &rspac,
1780178825Sdfr			 e_text,
1781178825Sdfr			 reply);
1782178825Sdfr
1783178825Sdfrout:
1784178825Sdfr    free(spn);
1785178825Sdfr    free(cpn);
1786178825Sdfr
1787178825Sdfr    krb5_data_free(&rspac);
1788178825Sdfr    krb5_free_keyblock_contents(context, &sessionkey);
1789178825Sdfr    if(server)
1790178825Sdfr	_kdc_free_ent(context, server);
1791178825Sdfr    if(client)
1792178825Sdfr	_kdc_free_ent(context, client);
1793178825Sdfr
1794178825Sdfr    if (client_principal && client_principal != cp)
1795178825Sdfr	krb5_free_principal(context, client_principal);
1796178825Sdfr    if (cp)
1797178825Sdfr	krb5_free_principal(context, cp);
1798178825Sdfr    if (sp)
1799178825Sdfr	krb5_free_principal(context, sp);
1800178825Sdfr
1801178825Sdfr    free_EncTicketPart(&adtkt);
1802178825Sdfr
1803178825Sdfr    return ret;
1804178825Sdfr}
1805178825Sdfr
1806178825Sdfr/*
1807178825Sdfr *
1808178825Sdfr */
1809178825Sdfr
1810178825Sdfrkrb5_error_code
1811178825Sdfr_kdc_tgs_rep(krb5_context context,
1812178825Sdfr	     krb5_kdc_configuration *config,
1813178825Sdfr	     KDC_REQ *req,
1814178825Sdfr	     krb5_data *data,
1815178825Sdfr	     const char *from,
1816178825Sdfr	     struct sockaddr *from_addr,
1817178825Sdfr	     int datagram_reply)
1818178825Sdfr{
1819178825Sdfr    AuthorizationData *auth_data = NULL;
1820178825Sdfr    krb5_error_code ret;
1821178825Sdfr    int i = 0;
1822178825Sdfr    const PA_DATA *tgs_req;
1823178825Sdfr
1824178825Sdfr    hdb_entry_ex *krbtgt = NULL;
1825178825Sdfr    krb5_ticket *ticket = NULL;
1826178825Sdfr    const char *e_text = NULL;
1827178825Sdfr    krb5_enctype krbtgt_etype = ETYPE_NULL;
1828178825Sdfr
1829178825Sdfr    time_t *csec = NULL;
1830178825Sdfr    int *cusec = NULL;
1831178825Sdfr
1832178825Sdfr    if(req->padata == NULL){
1833178825Sdfr	ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
1834178825Sdfr	kdc_log(context, config, 0,
1835178825Sdfr		"TGS-REQ from %s without PA-DATA", from);
1836178825Sdfr	goto out;
1837178825Sdfr    }
1838178825Sdfr
1839178825Sdfr    tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
1840178825Sdfr
1841178825Sdfr    if(tgs_req == NULL){
1842178825Sdfr	ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
1843178825Sdfr
1844178825Sdfr	kdc_log(context, config, 0,
1845178825Sdfr		"TGS-REQ from %s without PA-TGS-REQ", from);
1846178825Sdfr	goto out;
1847178825Sdfr    }
1848178825Sdfr    ret = tgs_parse_request(context, config,
1849178825Sdfr			    &req->req_body, tgs_req,
1850178825Sdfr			    &krbtgt,
1851178825Sdfr			    &krbtgt_etype,
1852178825Sdfr			    &ticket,
1853178825Sdfr			    &e_text,
1854178825Sdfr			    from, from_addr,
1855178825Sdfr			    &csec, &cusec,
1856178825Sdfr			    &auth_data);
1857178825Sdfr    if (ret) {
1858178825Sdfr	kdc_log(context, config, 0,
1859178825Sdfr		"Failed parsing TGS-REQ from %s", from);
1860178825Sdfr	goto out;
1861178825Sdfr    }
1862178825Sdfr
1863178825Sdfr    ret = tgs_build_reply(context,
1864178825Sdfr			  config,
1865178825Sdfr			  req,
1866178825Sdfr			  &req->req_body,
1867178825Sdfr			  krbtgt,
1868178825Sdfr			  krbtgt_etype,
1869178825Sdfr			  ticket,
1870178825Sdfr			  data,
1871178825Sdfr			  from,
1872178825Sdfr			  &e_text,
1873178825Sdfr			  auth_data,
1874178825Sdfr			  from_addr,
1875178825Sdfr			  datagram_reply);
1876178825Sdfr    if (ret) {
1877178825Sdfr	kdc_log(context, config, 0,
1878178825Sdfr		"Failed building TGS-REP to %s", from);
1879178825Sdfr	goto out;
1880178825Sdfr    }
1881178825Sdfr
1882178825Sdfr    /* */
1883178825Sdfr    if (datagram_reply && data->length > config->max_datagram_reply_length) {
1884178825Sdfr	krb5_data_free(data);
1885178825Sdfr	ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
1886178825Sdfr	e_text = "Reply packet too large";
1887178825Sdfr    }
1888178825Sdfr
1889178825Sdfrout:
1890178825Sdfr    if(ret && data->data == NULL){
1891178825Sdfr	krb5_mk_error(context,
1892178825Sdfr		      ret,
1893178825Sdfr		      NULL,
1894178825Sdfr		      NULL,
1895178825Sdfr		      NULL,
1896178825Sdfr		      NULL,
1897178825Sdfr		      csec,
1898178825Sdfr		      cusec,
1899178825Sdfr		      data);
1900178825Sdfr    }
1901178825Sdfr    free(csec);
1902178825Sdfr    free(cusec);
1903178825Sdfr    if (ticket)
1904178825Sdfr	krb5_free_ticket(context, ticket);
1905178825Sdfr    if(krbtgt)
1906178825Sdfr	_kdc_free_ent(context, krbtgt);
1907178825Sdfr
1908178825Sdfr    if (auth_data) {
1909178825Sdfr	free_AuthorizationData(auth_data);
1910178825Sdfr	free(auth_data);
1911178825Sdfr    }
1912178825Sdfr
1913178825Sdfr    return 0;
1914178825Sdfr}
1915