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