1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* kdc/tgs_policy.c */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert * Copyright (C) 2012 by the Massachusetts Institute of Technology.
5*7f2fe78bSCy Schubert * All rights reserved.
6*7f2fe78bSCy Schubert *
7*7f2fe78bSCy Schubert * Redistribution and use in source and binary forms, with or without
8*7f2fe78bSCy Schubert * modification, are permitted provided that the following conditions
9*7f2fe78bSCy Schubert * are met:
10*7f2fe78bSCy Schubert *
11*7f2fe78bSCy Schubert * * Redistributions of source code must retain the above copyright
12*7f2fe78bSCy Schubert * notice, this list of conditions and the following disclaimer.
13*7f2fe78bSCy Schubert *
14*7f2fe78bSCy Schubert * * Redistributions in binary form must reproduce the above copyright
15*7f2fe78bSCy Schubert * notice, this list of conditions and the following disclaimer in
16*7f2fe78bSCy Schubert * the documentation and/or other materials provided with the
17*7f2fe78bSCy Schubert * distribution.
18*7f2fe78bSCy Schubert *
19*7f2fe78bSCy Schubert * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20*7f2fe78bSCy Schubert * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21*7f2fe78bSCy Schubert * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22*7f2fe78bSCy Schubert * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23*7f2fe78bSCy Schubert * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24*7f2fe78bSCy Schubert * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25*7f2fe78bSCy Schubert * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26*7f2fe78bSCy Schubert * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*7f2fe78bSCy Schubert * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28*7f2fe78bSCy Schubert * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29*7f2fe78bSCy Schubert * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30*7f2fe78bSCy Schubert * OF THE POSSIBILITY OF SUCH DAMAGE.
31*7f2fe78bSCy Schubert */
32*7f2fe78bSCy Schubert
33*7f2fe78bSCy Schubert #include "k5-int.h"
34*7f2fe78bSCy Schubert #include "kdc_util.h"
35*7f2fe78bSCy Schubert
36*7f2fe78bSCy Schubert /*
37*7f2fe78bSCy Schubert * Routines that validate a TGS request; checks a lot of things. :-)
38*7f2fe78bSCy Schubert *
39*7f2fe78bSCy Schubert * Returns a Kerberos protocol error number, which is _not_ the same
40*7f2fe78bSCy Schubert * as a com_err error number!
41*7f2fe78bSCy Schubert */
42*7f2fe78bSCy Schubert
43*7f2fe78bSCy Schubert struct tgsflagrule {
44*7f2fe78bSCy Schubert krb5_flags reqflags; /* Flag(s) in TGS-REQ */
45*7f2fe78bSCy Schubert krb5_flags checkflag; /* Flags to check against */
46*7f2fe78bSCy Schubert char *status; /* Status string */
47*7f2fe78bSCy Schubert int err; /* Protocol error code */
48*7f2fe78bSCy Schubert };
49*7f2fe78bSCy Schubert
50*7f2fe78bSCy Schubert /* Service principal TGS policy checking functions */
51*7f2fe78bSCy Schubert typedef int (check_tgs_svc_pol_fn)(krb5_kdc_req *, krb5_db_entry *,
52*7f2fe78bSCy Schubert krb5_ticket *, krb5_timestamp,
53*7f2fe78bSCy Schubert const char **);
54*7f2fe78bSCy Schubert
55*7f2fe78bSCy Schubert static check_tgs_svc_pol_fn check_tgs_svc_deny_opts;
56*7f2fe78bSCy Schubert static check_tgs_svc_pol_fn check_tgs_svc_deny_all;
57*7f2fe78bSCy Schubert static check_tgs_svc_pol_fn check_tgs_svc_reqd_flags;
58*7f2fe78bSCy Schubert static check_tgs_svc_pol_fn check_tgs_svc_time;
59*7f2fe78bSCy Schubert
60*7f2fe78bSCy Schubert static check_tgs_svc_pol_fn * const svc_pol_fns[] = {
61*7f2fe78bSCy Schubert check_tgs_svc_deny_opts, check_tgs_svc_deny_all, check_tgs_svc_reqd_flags,
62*7f2fe78bSCy Schubert check_tgs_svc_time
63*7f2fe78bSCy Schubert };
64*7f2fe78bSCy Schubert
65*7f2fe78bSCy Schubert static const struct tgsflagrule tgsflagrules[] = {
66*7f2fe78bSCy Schubert { KDC_OPT_FORWARDED, TKT_FLG_FORWARDABLE,
67*7f2fe78bSCy Schubert "TGT NOT FORWARDABLE", KRB5KDC_ERR_BADOPTION },
68*7f2fe78bSCy Schubert { KDC_OPT_PROXY, TKT_FLG_PROXIABLE,
69*7f2fe78bSCy Schubert "TGT NOT PROXIABLE", KRB5KDC_ERR_BADOPTION },
70*7f2fe78bSCy Schubert { (KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED), TKT_FLG_MAY_POSTDATE,
71*7f2fe78bSCy Schubert "TGT NOT POSTDATABLE", KRB5KDC_ERR_BADOPTION },
72*7f2fe78bSCy Schubert { KDC_OPT_VALIDATE, TKT_FLG_INVALID,
73*7f2fe78bSCy Schubert "VALIDATE VALID TICKET", KRB5KDC_ERR_BADOPTION },
74*7f2fe78bSCy Schubert { KDC_OPT_RENEW, TKT_FLG_RENEWABLE,
75*7f2fe78bSCy Schubert "TICKET NOT RENEWABLE", KRB5KDC_ERR_BADOPTION }
76*7f2fe78bSCy Schubert };
77*7f2fe78bSCy Schubert
78*7f2fe78bSCy Schubert /*
79*7f2fe78bSCy Schubert * Some TGS-REQ options require that the ticket have corresponding flags set.
80*7f2fe78bSCy Schubert */
81*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_opts(krb5_kdc_req * req,krb5_ticket * tkt,const char ** status)82*7f2fe78bSCy Schubert check_tgs_opts(krb5_kdc_req *req, krb5_ticket *tkt, const char **status)
83*7f2fe78bSCy Schubert {
84*7f2fe78bSCy Schubert size_t i;
85*7f2fe78bSCy Schubert size_t nrules = sizeof(tgsflagrules) / sizeof(tgsflagrules[0]);
86*7f2fe78bSCy Schubert const struct tgsflagrule *r;
87*7f2fe78bSCy Schubert
88*7f2fe78bSCy Schubert for (i = 0; i < nrules; i++) {
89*7f2fe78bSCy Schubert r = &tgsflagrules[i];
90*7f2fe78bSCy Schubert if (r->reqflags & req->kdc_options) {
91*7f2fe78bSCy Schubert if (!(r->checkflag & tkt->enc_part2->flags)) {
92*7f2fe78bSCy Schubert *status = r->status;
93*7f2fe78bSCy Schubert return r->err;
94*7f2fe78bSCy Schubert }
95*7f2fe78bSCy Schubert }
96*7f2fe78bSCy Schubert }
97*7f2fe78bSCy Schubert
98*7f2fe78bSCy Schubert if (isflagset(tkt->enc_part2->flags, TKT_FLG_INVALID) &&
99*7f2fe78bSCy Schubert !isflagset(req->kdc_options, KDC_OPT_VALIDATE)) {
100*7f2fe78bSCy Schubert *status = "TICKET NOT VALID";
101*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_TKT_NYV;
102*7f2fe78bSCy Schubert }
103*7f2fe78bSCy Schubert
104*7f2fe78bSCy Schubert return 0;
105*7f2fe78bSCy Schubert }
106*7f2fe78bSCy Schubert
107*7f2fe78bSCy Schubert static const struct tgsflagrule svcdenyrules[] = {
108*7f2fe78bSCy Schubert { KDC_OPT_RENEWABLE, KRB5_KDB_DISALLOW_RENEWABLE,
109*7f2fe78bSCy Schubert "NON-RENEWABLE TICKET", KRB5KDC_ERR_POLICY },
110*7f2fe78bSCy Schubert { KDC_OPT_ALLOW_POSTDATE, KRB5_KDB_DISALLOW_POSTDATED,
111*7f2fe78bSCy Schubert "NON-POSTDATABLE TICKET", KRB5KDC_ERR_CANNOT_POSTDATE },
112*7f2fe78bSCy Schubert { KDC_OPT_ENC_TKT_IN_SKEY, KRB5_KDB_DISALLOW_DUP_SKEY,
113*7f2fe78bSCy Schubert "DUP_SKEY DISALLOWED", KRB5KDC_ERR_POLICY }
114*7f2fe78bSCy Schubert };
115*7f2fe78bSCy Schubert
116*7f2fe78bSCy Schubert /*
117*7f2fe78bSCy Schubert * A service principal can forbid some TGS-REQ options.
118*7f2fe78bSCy Schubert */
119*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_svc_deny_opts(krb5_kdc_req * req,krb5_db_entry * server,krb5_ticket * tkt,krb5_timestamp kdc_time,const char ** status)120*7f2fe78bSCy Schubert check_tgs_svc_deny_opts(krb5_kdc_req *req, krb5_db_entry *server,
121*7f2fe78bSCy Schubert krb5_ticket *tkt, krb5_timestamp kdc_time,
122*7f2fe78bSCy Schubert const char **status)
123*7f2fe78bSCy Schubert {
124*7f2fe78bSCy Schubert size_t i;
125*7f2fe78bSCy Schubert size_t nrules = sizeof(svcdenyrules) / sizeof(svcdenyrules[0]);
126*7f2fe78bSCy Schubert const struct tgsflagrule *r;
127*7f2fe78bSCy Schubert
128*7f2fe78bSCy Schubert for (i = 0; i < nrules; i++) {
129*7f2fe78bSCy Schubert r = &svcdenyrules[i];
130*7f2fe78bSCy Schubert if (!(r->reqflags & req->kdc_options))
131*7f2fe78bSCy Schubert continue;
132*7f2fe78bSCy Schubert if (r->checkflag & server->attributes) {
133*7f2fe78bSCy Schubert *status = r->status;
134*7f2fe78bSCy Schubert return r->err;
135*7f2fe78bSCy Schubert }
136*7f2fe78bSCy Schubert }
137*7f2fe78bSCy Schubert return 0;
138*7f2fe78bSCy Schubert }
139*7f2fe78bSCy Schubert
140*7f2fe78bSCy Schubert /*
141*7f2fe78bSCy Schubert * A service principal can deny all TGS-REQs for it.
142*7f2fe78bSCy Schubert */
143*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_svc_deny_all(krb5_kdc_req * req,krb5_db_entry * server,krb5_ticket * tkt,krb5_timestamp kdc_time,const char ** status)144*7f2fe78bSCy Schubert check_tgs_svc_deny_all(krb5_kdc_req *req, krb5_db_entry *server,
145*7f2fe78bSCy Schubert krb5_ticket *tkt, krb5_timestamp kdc_time,
146*7f2fe78bSCy Schubert const char **status)
147*7f2fe78bSCy Schubert {
148*7f2fe78bSCy Schubert if (server->attributes & KRB5_KDB_DISALLOW_ALL_TIX) {
149*7f2fe78bSCy Schubert *status = "SERVER LOCKED OUT";
150*7f2fe78bSCy Schubert return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
151*7f2fe78bSCy Schubert }
152*7f2fe78bSCy Schubert if ((server->attributes & KRB5_KDB_DISALLOW_SVR) &&
153*7f2fe78bSCy Schubert !(req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY)) {
154*7f2fe78bSCy Schubert *status = "SERVER NOT ALLOWED";
155*7f2fe78bSCy Schubert return KRB5KDC_ERR_MUST_USE_USER2USER;
156*7f2fe78bSCy Schubert }
157*7f2fe78bSCy Schubert if (server->attributes & KRB5_KDB_DISALLOW_TGT_BASED) {
158*7f2fe78bSCy Schubert if (krb5_is_tgs_principal(tkt->server)) {
159*7f2fe78bSCy Schubert *status = "TGT BASED NOT ALLOWED";
160*7f2fe78bSCy Schubert return KRB5KDC_ERR_POLICY;
161*7f2fe78bSCy Schubert }
162*7f2fe78bSCy Schubert }
163*7f2fe78bSCy Schubert return 0;
164*7f2fe78bSCy Schubert }
165*7f2fe78bSCy Schubert
166*7f2fe78bSCy Schubert /*
167*7f2fe78bSCy Schubert * A service principal can require certain TGT flags.
168*7f2fe78bSCy Schubert */
169*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_svc_reqd_flags(krb5_kdc_req * req,krb5_db_entry * server,krb5_ticket * tkt,krb5_timestamp kdc_time,const char ** status)170*7f2fe78bSCy Schubert check_tgs_svc_reqd_flags(krb5_kdc_req *req, krb5_db_entry *server,
171*7f2fe78bSCy Schubert krb5_ticket *tkt,
172*7f2fe78bSCy Schubert krb5_timestamp kdc_time, const char **status)
173*7f2fe78bSCy Schubert {
174*7f2fe78bSCy Schubert if (server->attributes & KRB5_KDB_REQUIRES_HW_AUTH) {
175*7f2fe78bSCy Schubert if (!(tkt->enc_part2->flags & TKT_FLG_HW_AUTH)) {
176*7f2fe78bSCy Schubert *status = "NO HW PREAUTH";
177*7f2fe78bSCy Schubert return KRB5KRB_ERR_GENERIC;
178*7f2fe78bSCy Schubert }
179*7f2fe78bSCy Schubert }
180*7f2fe78bSCy Schubert if (server->attributes & KRB5_KDB_REQUIRES_PRE_AUTH) {
181*7f2fe78bSCy Schubert if (!(tkt->enc_part2->flags & TKT_FLG_PRE_AUTH)) {
182*7f2fe78bSCy Schubert *status = "NO PREAUTH";
183*7f2fe78bSCy Schubert return KRB5KRB_ERR_GENERIC;
184*7f2fe78bSCy Schubert }
185*7f2fe78bSCy Schubert }
186*7f2fe78bSCy Schubert return 0;
187*7f2fe78bSCy Schubert }
188*7f2fe78bSCy Schubert
189*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_svc_time(krb5_kdc_req * req,krb5_db_entry * server,krb5_ticket * tkt,krb5_timestamp kdc_time,const char ** status)190*7f2fe78bSCy Schubert check_tgs_svc_time(krb5_kdc_req *req, krb5_db_entry *server, krb5_ticket *tkt,
191*7f2fe78bSCy Schubert krb5_timestamp kdc_time, const char **status)
192*7f2fe78bSCy Schubert {
193*7f2fe78bSCy Schubert if (server->expiration && ts_after(kdc_time, server->expiration)) {
194*7f2fe78bSCy Schubert *status = "SERVICE EXPIRED";
195*7f2fe78bSCy Schubert return KRB5KDC_ERR_SERVICE_EXP;
196*7f2fe78bSCy Schubert }
197*7f2fe78bSCy Schubert return 0;
198*7f2fe78bSCy Schubert }
199*7f2fe78bSCy Schubert
200*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_svc_policy(krb5_kdc_req * req,krb5_db_entry * server,krb5_ticket * tkt,krb5_timestamp kdc_time,const char ** status)201*7f2fe78bSCy Schubert check_tgs_svc_policy(krb5_kdc_req *req, krb5_db_entry *server,
202*7f2fe78bSCy Schubert krb5_ticket *tkt, krb5_timestamp kdc_time,
203*7f2fe78bSCy Schubert const char **status)
204*7f2fe78bSCy Schubert {
205*7f2fe78bSCy Schubert int errcode;
206*7f2fe78bSCy Schubert size_t i;
207*7f2fe78bSCy Schubert size_t nfns = sizeof(svc_pol_fns) / sizeof(svc_pol_fns[0]);
208*7f2fe78bSCy Schubert
209*7f2fe78bSCy Schubert for (i = 0; i < nfns; i++) {
210*7f2fe78bSCy Schubert errcode = svc_pol_fns[i](req, server, tkt, kdc_time, status);
211*7f2fe78bSCy Schubert if (errcode != 0)
212*7f2fe78bSCy Schubert return errcode;
213*7f2fe78bSCy Schubert }
214*7f2fe78bSCy Schubert return 0;
215*7f2fe78bSCy Schubert }
216*7f2fe78bSCy Schubert
217*7f2fe78bSCy Schubert /*
218*7f2fe78bSCy Schubert * Check header ticket timestamps against the current time.
219*7f2fe78bSCy Schubert */
220*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_times(krb5_kdc_req * req,krb5_ticket_times * times,krb5_timestamp kdc_time,const char ** status)221*7f2fe78bSCy Schubert check_tgs_times(krb5_kdc_req *req, krb5_ticket_times *times,
222*7f2fe78bSCy Schubert krb5_timestamp kdc_time, const char **status)
223*7f2fe78bSCy Schubert {
224*7f2fe78bSCy Schubert krb5_timestamp starttime;
225*7f2fe78bSCy Schubert
226*7f2fe78bSCy Schubert /* For validating a postdated ticket, check the start time vs. the
227*7f2fe78bSCy Schubert KDC time. */
228*7f2fe78bSCy Schubert if (req->kdc_options & KDC_OPT_VALIDATE) {
229*7f2fe78bSCy Schubert starttime = times->starttime ? times->starttime : times->authtime;
230*7f2fe78bSCy Schubert if (ts_after(starttime, kdc_time)) {
231*7f2fe78bSCy Schubert *status = "NOT_YET_VALID";
232*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_TKT_NYV;
233*7f2fe78bSCy Schubert }
234*7f2fe78bSCy Schubert }
235*7f2fe78bSCy Schubert /*
236*7f2fe78bSCy Schubert * Check the renew_till time. The endtime was already
237*7f2fe78bSCy Schubert * been checked in the initial authentication check.
238*7f2fe78bSCy Schubert */
239*7f2fe78bSCy Schubert if ((req->kdc_options & KDC_OPT_RENEW) &&
240*7f2fe78bSCy Schubert ts_after(kdc_time, times->renew_till)) {
241*7f2fe78bSCy Schubert *status = "TKT_EXPIRED";
242*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_TKT_EXPIRED;
243*7f2fe78bSCy Schubert }
244*7f2fe78bSCy Schubert return 0;
245*7f2fe78bSCy Schubert }
246*7f2fe78bSCy Schubert
247*7f2fe78bSCy Schubert /* Check for local user tickets issued by foreign realms. This check is
248*7f2fe78bSCy Schubert * skipped for S4U2Self requests. */
249*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_lineage(krb5_db_entry * server,krb5_ticket * tkt,krb5_boolean is_crossrealm,const char ** status)250*7f2fe78bSCy Schubert check_tgs_lineage(krb5_db_entry *server, krb5_ticket *tkt,
251*7f2fe78bSCy Schubert krb5_boolean is_crossrealm, const char **status)
252*7f2fe78bSCy Schubert {
253*7f2fe78bSCy Schubert if (is_crossrealm && data_eq(tkt->enc_part2->client->realm,
254*7f2fe78bSCy Schubert server->princ->realm)) {
255*7f2fe78bSCy Schubert *status = "INVALID LINEAGE";
256*7f2fe78bSCy Schubert return KRB5KDC_ERR_POLICY;
257*7f2fe78bSCy Schubert }
258*7f2fe78bSCy Schubert return 0;
259*7f2fe78bSCy Schubert }
260*7f2fe78bSCy Schubert
261*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_s4u2self(kdc_realm_t * realm,krb5_kdc_req * req,krb5_db_entry * server,krb5_ticket * tkt,krb5_pac pac,krb5_timestamp kdc_time,krb5_pa_s4u_x509_user * s4u_x509_user,krb5_db_entry * client,krb5_boolean is_crossrealm,krb5_boolean is_referral,const char ** status,krb5_pa_data *** e_data)262*7f2fe78bSCy Schubert check_tgs_s4u2self(kdc_realm_t *realm, krb5_kdc_req *req,
263*7f2fe78bSCy Schubert krb5_db_entry *server, krb5_ticket *tkt, krb5_pac pac,
264*7f2fe78bSCy Schubert krb5_timestamp kdc_time,
265*7f2fe78bSCy Schubert krb5_pa_s4u_x509_user *s4u_x509_user, krb5_db_entry *client,
266*7f2fe78bSCy Schubert krb5_boolean is_crossrealm, krb5_boolean is_referral,
267*7f2fe78bSCy Schubert const char **status, krb5_pa_data ***e_data)
268*7f2fe78bSCy Schubert {
269*7f2fe78bSCy Schubert krb5_context context = realm->realm_context;
270*7f2fe78bSCy Schubert krb5_db_entry empty_server = { 0 };
271*7f2fe78bSCy Schubert
272*7f2fe78bSCy Schubert /* If the server is local, check that the request is for self. */
273*7f2fe78bSCy Schubert if (!is_referral &&
274*7f2fe78bSCy Schubert !is_client_db_alias(context, server, tkt->enc_part2->client)) {
275*7f2fe78bSCy Schubert *status = "INVALID_S4U2SELF_REQUEST_SERVER_MISMATCH";
276*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_BADMATCH;
277*7f2fe78bSCy Schubert }
278*7f2fe78bSCy Schubert
279*7f2fe78bSCy Schubert /* S4U2Self requests must use options valid for AS requests. */
280*7f2fe78bSCy Schubert if (req->kdc_options & AS_INVALID_OPTIONS) {
281*7f2fe78bSCy Schubert *status = "INVALID S4U2SELF OPTIONS";
282*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
283*7f2fe78bSCy Schubert }
284*7f2fe78bSCy Schubert
285*7f2fe78bSCy Schubert /*
286*7f2fe78bSCy Schubert * Valid S4U2Self requests can occur in the following combinations:
287*7f2fe78bSCy Schubert *
288*7f2fe78bSCy Schubert * (1) local TGT, local user, local server
289*7f2fe78bSCy Schubert * (2) cross TGT, local user, issuing referral
290*7f2fe78bSCy Schubert * (3) cross TGT, non-local user, issuing referral
291*7f2fe78bSCy Schubert * (4) cross TGT, non-local user, local server
292*7f2fe78bSCy Schubert *
293*7f2fe78bSCy Schubert * The first case is for a single-realm S4U2Self scenario; the second,
294*7f2fe78bSCy Schubert * third, and fourth cases are for the initial, intermediate (if any), and
295*7f2fe78bSCy Schubert * final cross-realm requests in a multi-realm scenario.
296*7f2fe78bSCy Schubert */
297*7f2fe78bSCy Schubert
298*7f2fe78bSCy Schubert if (!is_crossrealm && is_referral) {
299*7f2fe78bSCy Schubert /* This could happen if the requesting server no longer exists, and we
300*7f2fe78bSCy Schubert * found a referral instead. Treat this as a server lookup failure. */
301*7f2fe78bSCy Schubert *status = "LOOKING_UP_SERVER";
302*7f2fe78bSCy Schubert return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
303*7f2fe78bSCy Schubert }
304*7f2fe78bSCy Schubert if (client != NULL && is_crossrealm && !is_referral) {
305*7f2fe78bSCy Schubert /* A local server should not need a cross-realm TGT to impersonate
306*7f2fe78bSCy Schubert * a local principal. */
307*7f2fe78bSCy Schubert *status = "NOT_CROSS_REALM_REQUEST";
308*7f2fe78bSCy Schubert return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; /* match Windows error */
309*7f2fe78bSCy Schubert }
310*7f2fe78bSCy Schubert if (client == NULL && !is_crossrealm) {
311*7f2fe78bSCy Schubert /*
312*7f2fe78bSCy Schubert * The server is asking to impersonate a principal from another realm,
313*7f2fe78bSCy Schubert * using a local TGT. It should instead ask that principal's realm and
314*7f2fe78bSCy Schubert * follow referrals back to us.
315*7f2fe78bSCy Schubert */
316*7f2fe78bSCy Schubert *status = "S4U2SELF_CLIENT_NOT_OURS";
317*7f2fe78bSCy Schubert return KRB5KDC_ERR_POLICY; /* match Windows error */
318*7f2fe78bSCy Schubert }
319*7f2fe78bSCy Schubert if (client == NULL && s4u_x509_user->user_id.user->length == 0) {
320*7f2fe78bSCy Schubert /*
321*7f2fe78bSCy Schubert * Only a KDC in the client realm can handle a certificate-only
322*7f2fe78bSCy Schubert * S4U2Self request. Other KDCs require a principal name and ignore
323*7f2fe78bSCy Schubert * the subject-certificate field.
324*7f2fe78bSCy Schubert */
325*7f2fe78bSCy Schubert *status = "INVALID_XREALM_S4U2SELF_REQUEST";
326*7f2fe78bSCy Schubert return KRB5KDC_ERR_POLICY; /* match Windows error */
327*7f2fe78bSCy Schubert }
328*7f2fe78bSCy Schubert
329*7f2fe78bSCy Schubert /* The header ticket PAC must be present. */
330*7f2fe78bSCy Schubert if (pac == NULL) {
331*7f2fe78bSCy Schubert *status = "S4U2SELF_NO_PAC";
332*7f2fe78bSCy Schubert return KRB5KDC_ERR_TGT_REVOKED;
333*7f2fe78bSCy Schubert }
334*7f2fe78bSCy Schubert
335*7f2fe78bSCy Schubert if (client != NULL) {
336*7f2fe78bSCy Schubert /* The header ticket PAC must be for the impersonator. */
337*7f2fe78bSCy Schubert if (krb5_pac_verify(context, pac, tkt->enc_part2->times.authtime,
338*7f2fe78bSCy Schubert tkt->enc_part2->client, NULL, NULL) != 0) {
339*7f2fe78bSCy Schubert *status = "S4U2SELF_LOCAL_PAC_CLIENT";
340*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
341*7f2fe78bSCy Schubert }
342*7f2fe78bSCy Schubert
343*7f2fe78bSCy Schubert /* Validate the client policy. Use an empty server principal to bypass
344*7f2fe78bSCy Schubert * server policy checks. */
345*7f2fe78bSCy Schubert return validate_as_request(realm, req, client, &empty_server, kdc_time,
346*7f2fe78bSCy Schubert status, e_data);
347*7f2fe78bSCy Schubert } else {
348*7f2fe78bSCy Schubert /* The header ticket PAC must be for the subject, with realm. */
349*7f2fe78bSCy Schubert if (krb5_pac_verify_ext(context, pac, tkt->enc_part2->times.authtime,
350*7f2fe78bSCy Schubert s4u_x509_user->user_id.user, NULL, NULL,
351*7f2fe78bSCy Schubert TRUE) != 0) {
352*7f2fe78bSCy Schubert *status = "S4U2SELF_FOREIGN_PAC_CLIENT";
353*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
354*7f2fe78bSCy Schubert }
355*7f2fe78bSCy Schubert }
356*7f2fe78bSCy Schubert
357*7f2fe78bSCy Schubert return 0;
358*7f2fe78bSCy Schubert }
359*7f2fe78bSCy Schubert
360*7f2fe78bSCy Schubert /*
361*7f2fe78bSCy Schubert * Validate pac as an S4U2Proxy subject PAC contained within a cross-realm TGT.
362*7f2fe78bSCy Schubert * If target_server is non-null, verify that it matches the PAC proxy target.
363*7f2fe78bSCy Schubert * Return 0 on success, non-zero on failure.
364*7f2fe78bSCy Schubert */
365*7f2fe78bSCy Schubert static int
verify_deleg_pac(krb5_context context,krb5_pac pac,krb5_enc_tkt_part * enc_tkt,krb5_const_principal target_server)366*7f2fe78bSCy Schubert verify_deleg_pac(krb5_context context, krb5_pac pac,
367*7f2fe78bSCy Schubert krb5_enc_tkt_part *enc_tkt,
368*7f2fe78bSCy Schubert krb5_const_principal target_server)
369*7f2fe78bSCy Schubert {
370*7f2fe78bSCy Schubert krb5_timestamp pac_authtime;
371*7f2fe78bSCy Schubert krb5_data deleg_buf = empty_data();
372*7f2fe78bSCy Schubert krb5_principal princ = NULL;
373*7f2fe78bSCy Schubert struct pac_s4u_delegation_info *di = NULL;
374*7f2fe78bSCy Schubert char *client_str = NULL, *target_str = NULL;
375*7f2fe78bSCy Schubert const char *last_transited;
376*7f2fe78bSCy Schubert int result = -1;
377*7f2fe78bSCy Schubert
378*7f2fe78bSCy Schubert /* Make sure the PAC client string can be parsed as a principal with
379*7f2fe78bSCy Schubert * realm. */
380*7f2fe78bSCy Schubert if (get_pac_princ_with_realm(context, pac, &princ, &pac_authtime) != 0)
381*7f2fe78bSCy Schubert goto cleanup;
382*7f2fe78bSCy Schubert if (pac_authtime != enc_tkt->times.authtime)
383*7f2fe78bSCy Schubert goto cleanup;
384*7f2fe78bSCy Schubert
385*7f2fe78bSCy Schubert if (krb5_pac_get_buffer(context, pac, KRB5_PAC_DELEGATION_INFO,
386*7f2fe78bSCy Schubert &deleg_buf) != 0)
387*7f2fe78bSCy Schubert goto cleanup;
388*7f2fe78bSCy Schubert
389*7f2fe78bSCy Schubert if (ndr_dec_delegation_info(&deleg_buf, &di) != 0)
390*7f2fe78bSCy Schubert goto cleanup;
391*7f2fe78bSCy Schubert
392*7f2fe78bSCy Schubert if (target_server != NULL) {
393*7f2fe78bSCy Schubert if (krb5_unparse_name_flags(context, target_server,
394*7f2fe78bSCy Schubert KRB5_PRINCIPAL_UNPARSE_DISPLAY |
395*7f2fe78bSCy Schubert KRB5_PRINCIPAL_UNPARSE_NO_REALM,
396*7f2fe78bSCy Schubert &target_str) != 0)
397*7f2fe78bSCy Schubert goto cleanup;
398*7f2fe78bSCy Schubert if (strcmp(target_str, di->proxy_target) != 0)
399*7f2fe78bSCy Schubert goto cleanup;
400*7f2fe78bSCy Schubert }
401*7f2fe78bSCy Schubert
402*7f2fe78bSCy Schubert /* Check that the most recently added PAC transited service matches the
403*7f2fe78bSCy Schubert * requesting impersonator. */
404*7f2fe78bSCy Schubert if (di->transited_services_length == 0)
405*7f2fe78bSCy Schubert goto cleanup;
406*7f2fe78bSCy Schubert if (krb5_unparse_name(context, enc_tkt->client, &client_str) != 0)
407*7f2fe78bSCy Schubert goto cleanup;
408*7f2fe78bSCy Schubert last_transited = di->transited_services[di->transited_services_length - 1];
409*7f2fe78bSCy Schubert if (strcmp(last_transited, client_str) != 0)
410*7f2fe78bSCy Schubert goto cleanup;
411*7f2fe78bSCy Schubert
412*7f2fe78bSCy Schubert result = 0;
413*7f2fe78bSCy Schubert
414*7f2fe78bSCy Schubert cleanup:
415*7f2fe78bSCy Schubert free(target_str);
416*7f2fe78bSCy Schubert free(client_str);
417*7f2fe78bSCy Schubert ndr_free_delegation_info(di);
418*7f2fe78bSCy Schubert krb5_free_principal(context, princ);
419*7f2fe78bSCy Schubert krb5_free_data_contents(context, &deleg_buf);
420*7f2fe78bSCy Schubert return result;
421*7f2fe78bSCy Schubert }
422*7f2fe78bSCy Schubert
423*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_s4u2proxy(krb5_context context,krb5_kdc_req * req,krb5_db_entry * server,krb5_ticket * tkt,krb5_pac pac,const krb5_ticket * stkt,krb5_pac stkt_pac,krb5_db_entry * stkt_server,krb5_boolean is_crossrealm,krb5_boolean is_referral,const char ** status)424*7f2fe78bSCy Schubert check_tgs_s4u2proxy(krb5_context context, krb5_kdc_req *req,
425*7f2fe78bSCy Schubert krb5_db_entry *server, krb5_ticket *tkt, krb5_pac pac,
426*7f2fe78bSCy Schubert const krb5_ticket *stkt, krb5_pac stkt_pac,
427*7f2fe78bSCy Schubert krb5_db_entry *stkt_server, krb5_boolean is_crossrealm,
428*7f2fe78bSCy Schubert krb5_boolean is_referral, const char **status)
429*7f2fe78bSCy Schubert {
430*7f2fe78bSCy Schubert /* A forwardable second ticket must be present in the request. */
431*7f2fe78bSCy Schubert if (stkt == NULL) {
432*7f2fe78bSCy Schubert *status = "NO_2ND_TKT";
433*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
434*7f2fe78bSCy Schubert }
435*7f2fe78bSCy Schubert if (!(stkt->enc_part2->flags & TKT_FLG_FORWARDABLE)) {
436*7f2fe78bSCy Schubert *status = "EVIDENCE_TKT_NOT_FORWARDABLE";
437*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
438*7f2fe78bSCy Schubert }
439*7f2fe78bSCy Schubert
440*7f2fe78bSCy Schubert /* Constrained delegation is mutually exclusive with renew/forward/etc.
441*7f2fe78bSCy Schubert * (and therefore requires the header ticket to be a TGT). */
442*7f2fe78bSCy Schubert if (req->kdc_options & (NON_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY)) {
443*7f2fe78bSCy Schubert *status = "INVALID_S4U2PROXY_OPTIONS";
444*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
445*7f2fe78bSCy Schubert }
446*7f2fe78bSCy Schubert
447*7f2fe78bSCy Schubert /* Can't get a TGT (otherwise it would be unconstrained delegation). */
448*7f2fe78bSCy Schubert if (krb5_is_tgs_principal(req->server)) {
449*7f2fe78bSCy Schubert *status = "NOT_ALLOWED_TO_DELEGATE";
450*7f2fe78bSCy Schubert return KRB5KDC_ERR_POLICY;
451*7f2fe78bSCy Schubert }
452*7f2fe78bSCy Schubert
453*7f2fe78bSCy Schubert /* The header ticket PAC must be present and for the impersonator. */
454*7f2fe78bSCy Schubert if (pac == NULL) {
455*7f2fe78bSCy Schubert *status = "S4U2PROXY_NO_HEADER_PAC";
456*7f2fe78bSCy Schubert return KRB5KDC_ERR_TGT_REVOKED;
457*7f2fe78bSCy Schubert }
458*7f2fe78bSCy Schubert if (krb5_pac_verify(context, pac, tkt->enc_part2->times.authtime,
459*7f2fe78bSCy Schubert tkt->enc_part2->client, NULL, NULL) != 0) {
460*7f2fe78bSCy Schubert *status = "S4U2PROXY_HEADER_PAC";
461*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
462*7f2fe78bSCy Schubert }
463*7f2fe78bSCy Schubert
464*7f2fe78bSCy Schubert /*
465*7f2fe78bSCy Schubert * An S4U2Proxy request must be an initial request to the impersonator's
466*7f2fe78bSCy Schubert * realm (possibly for a target resource in the same realm), or a final
467*7f2fe78bSCy Schubert * cross-realm RBCD request to the resource realm. Intermediate
468*7f2fe78bSCy Schubert * referral-chasing requests do not use the CNAME-IN-ADDL-TKT flag.
469*7f2fe78bSCy Schubert */
470*7f2fe78bSCy Schubert
471*7f2fe78bSCy Schubert if (stkt_pac == NULL) {
472*7f2fe78bSCy Schubert *status = "S4U2PROXY_NO_STKT_PAC";
473*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_MODIFIED;
474*7f2fe78bSCy Schubert }
475*7f2fe78bSCy Schubert if (!is_crossrealm) {
476*7f2fe78bSCy Schubert /* For an initial or same-realm request, the second ticket server and
477*7f2fe78bSCy Schubert * header ticket client must be the same principal. */
478*7f2fe78bSCy Schubert if (!is_client_db_alias(context, stkt_server,
479*7f2fe78bSCy Schubert tkt->enc_part2->client)) {
480*7f2fe78bSCy Schubert *status = "EVIDENCE_TICKET_MISMATCH";
481*7f2fe78bSCy Schubert return KRB5KDC_ERR_SERVER_NOMATCH;
482*7f2fe78bSCy Schubert }
483*7f2fe78bSCy Schubert
484*7f2fe78bSCy Schubert /* The second ticket client and PAC client are the subject, and must
485*7f2fe78bSCy Schubert * match. */
486*7f2fe78bSCy Schubert if (krb5_pac_verify(context, stkt_pac, stkt->enc_part2->times.authtime,
487*7f2fe78bSCy Schubert stkt->enc_part2->client, NULL, NULL) != 0) {
488*7f2fe78bSCy Schubert *status = "S4U2PROXY_LOCAL_STKT_PAC";
489*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
490*7f2fe78bSCy Schubert }
491*7f2fe78bSCy Schubert
492*7f2fe78bSCy Schubert } else {
493*7f2fe78bSCy Schubert
494*7f2fe78bSCy Schubert /*
495*7f2fe78bSCy Schubert * For a cross-realm request, the second ticket must be a referral TGT
496*7f2fe78bSCy Schubert * to our realm with the impersonator as client. The target server
497*7f2fe78bSCy Schubert * must also be local, so we must not be issuing a referral.
498*7f2fe78bSCy Schubert */
499*7f2fe78bSCy Schubert if (is_referral || !is_cross_tgs_principal(stkt_server->princ) ||
500*7f2fe78bSCy Schubert !data_eq(stkt_server->princ->data[1], server->princ->realm) ||
501*7f2fe78bSCy Schubert !krb5_principal_compare(context, stkt->enc_part2->client,
502*7f2fe78bSCy Schubert tkt->enc_part2->client)) {
503*7f2fe78bSCy Schubert *status = "XREALM_EVIDENCE_TICKET_MISMATCH";
504*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
505*7f2fe78bSCy Schubert }
506*7f2fe78bSCy Schubert
507*7f2fe78bSCy Schubert /* The second ticket PAC must be present and for the impersonated
508*7f2fe78bSCy Schubert * client, with delegation info. */
509*7f2fe78bSCy Schubert if (stkt_pac == NULL ||
510*7f2fe78bSCy Schubert verify_deleg_pac(context, stkt_pac, stkt->enc_part2,
511*7f2fe78bSCy Schubert req->server) != 0) {
512*7f2fe78bSCy Schubert *status = "S4U2PROXY_CROSS_STKT_PAC";
513*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
514*7f2fe78bSCy Schubert }
515*7f2fe78bSCy Schubert }
516*7f2fe78bSCy Schubert
517*7f2fe78bSCy Schubert return 0;
518*7f2fe78bSCy Schubert }
519*7f2fe78bSCy Schubert
520*7f2fe78bSCy Schubert /* Check the KDB policy for a final RBCD request. */
521*7f2fe78bSCy Schubert static krb5_error_code
check_s4u2proxy_policy(krb5_context context,krb5_kdc_req * req,krb5_principal desired_client,krb5_principal impersonator_name,krb5_db_entry * impersonator,krb5_pac impersonator_pac,krb5_principal resource_name,krb5_db_entry * resource,krb5_boolean is_crossrealm,krb5_boolean is_referral,const char ** status)522*7f2fe78bSCy Schubert check_s4u2proxy_policy(krb5_context context, krb5_kdc_req *req,
523*7f2fe78bSCy Schubert krb5_principal desired_client,
524*7f2fe78bSCy Schubert krb5_principal impersonator_name,
525*7f2fe78bSCy Schubert krb5_db_entry *impersonator, krb5_pac impersonator_pac,
526*7f2fe78bSCy Schubert krb5_principal resource_name, krb5_db_entry *resource,
527*7f2fe78bSCy Schubert krb5_boolean is_crossrealm, krb5_boolean is_referral,
528*7f2fe78bSCy Schubert const char **status)
529*7f2fe78bSCy Schubert {
530*7f2fe78bSCy Schubert krb5_error_code ret;
531*7f2fe78bSCy Schubert krb5_boolean support_rbcd, policy_denial = FALSE;
532*7f2fe78bSCy Schubert
533*7f2fe78bSCy Schubert /* Check if the client supports resource-based constrained delegation. */
534*7f2fe78bSCy Schubert ret = kdc_get_pa_pac_rbcd(context, req->padata, &support_rbcd);
535*7f2fe78bSCy Schubert if (ret)
536*7f2fe78bSCy Schubert return ret;
537*7f2fe78bSCy Schubert
538*7f2fe78bSCy Schubert if (is_referral) {
539*7f2fe78bSCy Schubert if (!support_rbcd) {
540*7f2fe78bSCy Schubert /* The client must support RBCD for a referral to be useful. */
541*7f2fe78bSCy Schubert *status = "UNSUPPORTED_S4U2PROXY_REQUEST";
542*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
543*7f2fe78bSCy Schubert }
544*7f2fe78bSCy Schubert /* Policy will be checked in the resource realm. */
545*7f2fe78bSCy Schubert return 0;
546*7f2fe78bSCy Schubert }
547*7f2fe78bSCy Schubert
548*7f2fe78bSCy Schubert /* Try resource-based authorization if the client supports RBCD. */
549*7f2fe78bSCy Schubert if (support_rbcd) {
550*7f2fe78bSCy Schubert ret = krb5_db_allowed_to_delegate_from(context, desired_client,
551*7f2fe78bSCy Schubert impersonator_name,
552*7f2fe78bSCy Schubert impersonator_pac, resource);
553*7f2fe78bSCy Schubert if (ret == KRB5KDC_ERR_BADOPTION)
554*7f2fe78bSCy Schubert policy_denial = TRUE;
555*7f2fe78bSCy Schubert else if (ret != KRB5_PLUGIN_OP_NOTSUPP)
556*7f2fe78bSCy Schubert return ret;
557*7f2fe78bSCy Schubert }
558*7f2fe78bSCy Schubert
559*7f2fe78bSCy Schubert /* Try traditional authorization if the requestor is in this realm. */
560*7f2fe78bSCy Schubert if (!is_crossrealm) {
561*7f2fe78bSCy Schubert ret = krb5_db_check_allowed_to_delegate(context, desired_client,
562*7f2fe78bSCy Schubert impersonator, resource_name);
563*7f2fe78bSCy Schubert if (ret == KRB5KDC_ERR_BADOPTION)
564*7f2fe78bSCy Schubert policy_denial = TRUE;
565*7f2fe78bSCy Schubert else if (ret != KRB5_PLUGIN_OP_NOTSUPP)
566*7f2fe78bSCy Schubert return ret;
567*7f2fe78bSCy Schubert }
568*7f2fe78bSCy Schubert
569*7f2fe78bSCy Schubert *status = policy_denial ? "NOT_ALLOWED_TO_DELEGATE" :
570*7f2fe78bSCy Schubert "UNSUPPORTED_S4U2PROXY_REQUEST";
571*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
572*7f2fe78bSCy Schubert }
573*7f2fe78bSCy Schubert
574*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_u2u(krb5_context context,krb5_kdc_req * req,const krb5_ticket * stkt,krb5_db_entry * server,const char ** status)575*7f2fe78bSCy Schubert check_tgs_u2u(krb5_context context, krb5_kdc_req *req, const krb5_ticket *stkt,
576*7f2fe78bSCy Schubert krb5_db_entry *server, const char **status)
577*7f2fe78bSCy Schubert {
578*7f2fe78bSCy Schubert /* A second ticket must be present in the request. */
579*7f2fe78bSCy Schubert if (stkt == NULL) {
580*7f2fe78bSCy Schubert *status = "NO_2ND_TKT";
581*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
582*7f2fe78bSCy Schubert }
583*7f2fe78bSCy Schubert
584*7f2fe78bSCy Schubert /* The second ticket must be a TGT to the server realm. */
585*7f2fe78bSCy Schubert if (!is_local_tgs_principal(stkt->server) ||
586*7f2fe78bSCy Schubert !data_eq(stkt->server->data[1], server->princ->realm)) {
587*7f2fe78bSCy Schubert *status = "2ND_TKT_NOT_TGS";
588*7f2fe78bSCy Schubert return KRB5KDC_ERR_POLICY;
589*7f2fe78bSCy Schubert }
590*7f2fe78bSCy Schubert
591*7f2fe78bSCy Schubert /* The second ticket client must match the requested server. */
592*7f2fe78bSCy Schubert if (!is_client_db_alias(context, server, stkt->enc_part2->client)) {
593*7f2fe78bSCy Schubert *status = "2ND_TKT_MISMATCH";
594*7f2fe78bSCy Schubert return KRB5KDC_ERR_SERVER_NOMATCH;
595*7f2fe78bSCy Schubert }
596*7f2fe78bSCy Schubert
597*7f2fe78bSCy Schubert return 0;
598*7f2fe78bSCy Schubert }
599*7f2fe78bSCy Schubert
600*7f2fe78bSCy Schubert /* Validate the PAC of a non-S4U TGS request, if one is present. */
601*7f2fe78bSCy Schubert static krb5_error_code
check_normal_tgs_pac(krb5_context context,krb5_enc_tkt_part * enc_tkt,krb5_pac pac,krb5_db_entry * server,krb5_boolean is_crossrealm,const char ** status)602*7f2fe78bSCy Schubert check_normal_tgs_pac(krb5_context context, krb5_enc_tkt_part *enc_tkt,
603*7f2fe78bSCy Schubert krb5_pac pac, krb5_db_entry *server,
604*7f2fe78bSCy Schubert krb5_boolean is_crossrealm, const char **status)
605*7f2fe78bSCy Schubert {
606*7f2fe78bSCy Schubert /* We don't require a PAC for regular TGS requests. */
607*7f2fe78bSCy Schubert if (pac == NULL)
608*7f2fe78bSCy Schubert return 0;
609*7f2fe78bSCy Schubert
610*7f2fe78bSCy Schubert /* For most requests the header ticket PAC will be for the ticket
611*7f2fe78bSCy Schubert * client. */
612*7f2fe78bSCy Schubert if (krb5_pac_verify(context, pac, enc_tkt->times.authtime, enc_tkt->client,
613*7f2fe78bSCy Schubert NULL, NULL) == 0)
614*7f2fe78bSCy Schubert return 0;
615*7f2fe78bSCy Schubert
616*7f2fe78bSCy Schubert /* For intermediate RBCD requests the header ticket PAC will be for the
617*7f2fe78bSCy Schubert * impersonated client. */
618*7f2fe78bSCy Schubert if (is_crossrealm && is_cross_tgs_principal(server->princ) &&
619*7f2fe78bSCy Schubert verify_deleg_pac(context, pac, enc_tkt, NULL) == 0)
620*7f2fe78bSCy Schubert return 0;
621*7f2fe78bSCy Schubert
622*7f2fe78bSCy Schubert *status = "HEADER_PAC";
623*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
624*7f2fe78bSCy Schubert }
625*7f2fe78bSCy Schubert
626*7f2fe78bSCy Schubert /*
627*7f2fe78bSCy Schubert * Some TGS-REQ options allow for a non-TGS principal in the ticket. Do some
628*7f2fe78bSCy Schubert * checks that are peculiar to these cases. (e.g., ticket service principal
629*7f2fe78bSCy Schubert * matches requested service principal)
630*7f2fe78bSCy Schubert */
631*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_nontgt(krb5_context context,krb5_kdc_req * req,krb5_ticket * tkt,const char ** status)632*7f2fe78bSCy Schubert check_tgs_nontgt(krb5_context context, krb5_kdc_req *req, krb5_ticket *tkt,
633*7f2fe78bSCy Schubert const char **status)
634*7f2fe78bSCy Schubert {
635*7f2fe78bSCy Schubert if (!krb5_principal_compare(context, tkt->server, req->server)) {
636*7f2fe78bSCy Schubert *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC";
637*7f2fe78bSCy Schubert return KRB5KDC_ERR_SERVER_NOMATCH;
638*7f2fe78bSCy Schubert }
639*7f2fe78bSCy Schubert /* Cannot proxy ticket granting tickets. */
640*7f2fe78bSCy Schubert if ((req->kdc_options & KDC_OPT_PROXY) &&
641*7f2fe78bSCy Schubert krb5_is_tgs_principal(req->server)) {
642*7f2fe78bSCy Schubert *status = "CAN'T PROXY TGT";
643*7f2fe78bSCy Schubert return KRB5KDC_ERR_BADOPTION;
644*7f2fe78bSCy Schubert }
645*7f2fe78bSCy Schubert return 0;
646*7f2fe78bSCy Schubert }
647*7f2fe78bSCy Schubert
648*7f2fe78bSCy Schubert /*
649*7f2fe78bSCy Schubert * Do some checks for a normal TGS-REQ (where the ticket service must be a TGS
650*7f2fe78bSCy Schubert * principal).
651*7f2fe78bSCy Schubert */
652*7f2fe78bSCy Schubert static krb5_error_code
check_tgs_tgt(krb5_kdc_req * req,krb5_ticket * tkt,const char ** status)653*7f2fe78bSCy Schubert check_tgs_tgt(krb5_kdc_req *req, krb5_ticket *tkt, const char **status)
654*7f2fe78bSCy Schubert {
655*7f2fe78bSCy Schubert /* Make sure it's a TGS principal. */
656*7f2fe78bSCy Schubert if (!krb5_is_tgs_principal(tkt->server)) {
657*7f2fe78bSCy Schubert *status = "BAD TGS SERVER NAME";
658*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_NOT_US;
659*7f2fe78bSCy Schubert }
660*7f2fe78bSCy Schubert /* TGS principal second component must match service realm. */
661*7f2fe78bSCy Schubert if (!data_eq(tkt->server->data[1], req->server->realm)) {
662*7f2fe78bSCy Schubert *status = "BAD TGS SERVER INSTANCE";
663*7f2fe78bSCy Schubert return KRB5KRB_AP_ERR_NOT_US;
664*7f2fe78bSCy Schubert }
665*7f2fe78bSCy Schubert return 0;
666*7f2fe78bSCy Schubert }
667*7f2fe78bSCy Schubert
668*7f2fe78bSCy Schubert krb5_error_code
check_tgs_constraints(kdc_realm_t * realm,krb5_kdc_req * request,krb5_db_entry * server,krb5_ticket * ticket,krb5_pac pac,const krb5_ticket * stkt,krb5_pac stkt_pac,krb5_db_entry * stkt_server,krb5_timestamp kdc_time,krb5_pa_s4u_x509_user * s4u_x509_user,krb5_db_entry * s4u2self_client,krb5_boolean is_crossrealm,krb5_boolean is_referral,const char ** status,krb5_pa_data *** e_data)669*7f2fe78bSCy Schubert check_tgs_constraints(kdc_realm_t *realm, krb5_kdc_req *request,
670*7f2fe78bSCy Schubert krb5_db_entry *server, krb5_ticket *ticket, krb5_pac pac,
671*7f2fe78bSCy Schubert const krb5_ticket *stkt, krb5_pac stkt_pac,
672*7f2fe78bSCy Schubert krb5_db_entry *stkt_server, krb5_timestamp kdc_time,
673*7f2fe78bSCy Schubert krb5_pa_s4u_x509_user *s4u_x509_user,
674*7f2fe78bSCy Schubert krb5_db_entry *s4u2self_client,
675*7f2fe78bSCy Schubert krb5_boolean is_crossrealm, krb5_boolean is_referral,
676*7f2fe78bSCy Schubert const char **status, krb5_pa_data ***e_data)
677*7f2fe78bSCy Schubert {
678*7f2fe78bSCy Schubert krb5_context context = realm->realm_context;
679*7f2fe78bSCy Schubert int errcode;
680*7f2fe78bSCy Schubert
681*7f2fe78bSCy Schubert /* Depends only on request and ticket. */
682*7f2fe78bSCy Schubert errcode = check_tgs_opts(request, ticket, status);
683*7f2fe78bSCy Schubert if (errcode != 0)
684*7f2fe78bSCy Schubert return errcode;
685*7f2fe78bSCy Schubert
686*7f2fe78bSCy Schubert /* Depends only on request, ticket times, and current time. */
687*7f2fe78bSCy Schubert errcode = check_tgs_times(request, &ticket->enc_part2->times, kdc_time,
688*7f2fe78bSCy Schubert status);
689*7f2fe78bSCy Schubert if (errcode != 0)
690*7f2fe78bSCy Schubert return errcode;
691*7f2fe78bSCy Schubert
692*7f2fe78bSCy Schubert if (request->kdc_options & NON_TGT_OPTION)
693*7f2fe78bSCy Schubert errcode = check_tgs_nontgt(context, request, ticket, status);
694*7f2fe78bSCy Schubert else
695*7f2fe78bSCy Schubert errcode = check_tgs_tgt(request, ticket, status);
696*7f2fe78bSCy Schubert if (errcode != 0)
697*7f2fe78bSCy Schubert return errcode;
698*7f2fe78bSCy Schubert
699*7f2fe78bSCy Schubert if (s4u_x509_user != NULL) {
700*7f2fe78bSCy Schubert errcode = check_tgs_s4u2self(realm, request, server, ticket, pac,
701*7f2fe78bSCy Schubert kdc_time, s4u_x509_user, s4u2self_client,
702*7f2fe78bSCy Schubert is_crossrealm, is_referral, status,
703*7f2fe78bSCy Schubert e_data);
704*7f2fe78bSCy Schubert } else {
705*7f2fe78bSCy Schubert errcode = check_tgs_lineage(server, ticket, is_crossrealm, status);
706*7f2fe78bSCy Schubert }
707*7f2fe78bSCy Schubert if (errcode != 0)
708*7f2fe78bSCy Schubert return errcode;
709*7f2fe78bSCy Schubert
710*7f2fe78bSCy Schubert if (request->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) {
711*7f2fe78bSCy Schubert errcode = check_tgs_u2u(context, request, stkt, server, status);
712*7f2fe78bSCy Schubert if (errcode != 0)
713*7f2fe78bSCy Schubert return errcode;
714*7f2fe78bSCy Schubert }
715*7f2fe78bSCy Schubert
716*7f2fe78bSCy Schubert if (request->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT) {
717*7f2fe78bSCy Schubert errcode = check_tgs_s4u2proxy(context, request, server, ticket, pac,
718*7f2fe78bSCy Schubert stkt, stkt_pac, stkt_server,
719*7f2fe78bSCy Schubert is_crossrealm, is_referral, status);
720*7f2fe78bSCy Schubert if (errcode != 0)
721*7f2fe78bSCy Schubert return errcode;
722*7f2fe78bSCy Schubert } else if (s4u_x509_user == NULL) {
723*7f2fe78bSCy Schubert errcode = check_normal_tgs_pac(context, ticket->enc_part2, pac, server,
724*7f2fe78bSCy Schubert is_crossrealm, status);
725*7f2fe78bSCy Schubert if (errcode != 0)
726*7f2fe78bSCy Schubert return errcode;
727*7f2fe78bSCy Schubert }
728*7f2fe78bSCy Schubert
729*7f2fe78bSCy Schubert return 0;
730*7f2fe78bSCy Schubert }
731*7f2fe78bSCy Schubert
732*7f2fe78bSCy Schubert krb5_error_code
check_tgs_policy(kdc_realm_t * realm,krb5_kdc_req * request,krb5_db_entry * server,krb5_ticket * ticket,krb5_pac pac,const krb5_ticket * stkt,krb5_pac stkt_pac,krb5_principal stkt_pac_client,krb5_db_entry * stkt_server,krb5_timestamp kdc_time,krb5_boolean is_crossrealm,krb5_boolean is_referral,const char ** status,krb5_pa_data *** e_data)733*7f2fe78bSCy Schubert check_tgs_policy(kdc_realm_t *realm, krb5_kdc_req *request,
734*7f2fe78bSCy Schubert krb5_db_entry *server, krb5_ticket *ticket,
735*7f2fe78bSCy Schubert krb5_pac pac, const krb5_ticket *stkt, krb5_pac stkt_pac,
736*7f2fe78bSCy Schubert krb5_principal stkt_pac_client, krb5_db_entry *stkt_server,
737*7f2fe78bSCy Schubert krb5_timestamp kdc_time, krb5_boolean is_crossrealm,
738*7f2fe78bSCy Schubert krb5_boolean is_referral, const char **status,
739*7f2fe78bSCy Schubert krb5_pa_data ***e_data)
740*7f2fe78bSCy Schubert {
741*7f2fe78bSCy Schubert krb5_context context = realm->realm_context;
742*7f2fe78bSCy Schubert int errcode;
743*7f2fe78bSCy Schubert krb5_error_code ret;
744*7f2fe78bSCy Schubert krb5_principal desired_client;
745*7f2fe78bSCy Schubert
746*7f2fe78bSCy Schubert errcode = check_tgs_svc_policy(request, server, ticket, kdc_time, status);
747*7f2fe78bSCy Schubert if (errcode != 0)
748*7f2fe78bSCy Schubert return errcode;
749*7f2fe78bSCy Schubert
750*7f2fe78bSCy Schubert if (request->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT) {
751*7f2fe78bSCy Schubert desired_client = (stkt_pac_client != NULL) ? stkt_pac_client :
752*7f2fe78bSCy Schubert stkt->enc_part2->client;
753*7f2fe78bSCy Schubert errcode = check_s4u2proxy_policy(context, request, desired_client,
754*7f2fe78bSCy Schubert ticket->enc_part2->client,
755*7f2fe78bSCy Schubert stkt_server, pac, request->server,
756*7f2fe78bSCy Schubert server, is_crossrealm, is_referral,
757*7f2fe78bSCy Schubert status);
758*7f2fe78bSCy Schubert if (errcode != 0)
759*7f2fe78bSCy Schubert return errcode;
760*7f2fe78bSCy Schubert }
761*7f2fe78bSCy Schubert
762*7f2fe78bSCy Schubert if (check_anon(realm, ticket->enc_part2->client, request->server) != 0) {
763*7f2fe78bSCy Schubert *status = "ANONYMOUS NOT ALLOWED";
764*7f2fe78bSCy Schubert return KRB5KDC_ERR_POLICY;
765*7f2fe78bSCy Schubert }
766*7f2fe78bSCy Schubert
767*7f2fe78bSCy Schubert /* Perform KDB module policy checks. */
768*7f2fe78bSCy Schubert ret = krb5_db_check_policy_tgs(context, request, server, ticket, status,
769*7f2fe78bSCy Schubert e_data);
770*7f2fe78bSCy Schubert return (ret == KRB5_PLUGIN_OP_NOTSUPP) ? 0 : ret;
771*7f2fe78bSCy Schubert }
772