2 * Copyright (c) 1997-2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * [MS-SFU] Kerberos Protocol Extensions:
38 * Service for User (S4U2Self) and Constrained Delegation Protocol (S4U2Proxy)
39 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/
43 * Determine if constrained delegation is allowed from this client to this server
46 static krb5_error_code
47 check_constrained_delegation(krb5_context context,
48 krb5_kdc_configuration *config,
52 krb5_const_principal target)
54 const HDB_Ext_Constrained_delegation_acl *acl;
59 * constrained delegation (S4U2Proxy) only works within
60 * the same realm. We use the already canonicalized version
61 * of the principals here, while "target" is the principal
62 * provided by the client.
64 if (!krb5_realm_compare(context, client->principal, server->principal)) {
65 ret = KRB5KDC_ERR_BADOPTION;
66 kdc_log(context, config, 4,
67 "Bad request for constrained delegation");
71 if (clientdb->hdb_check_constrained_delegation) {
72 ret = clientdb->hdb_check_constrained_delegation(context, clientdb, client, target);
76 /* if client delegates to itself, that ok */
77 if (krb5_principal_compare(context, client->principal, server->principal) == TRUE)
80 ret = hdb_entry_get_ConstrainedDelegACL(client, &acl);
82 krb5_clear_error_message(context);
87 for (i = 0; i < acl->len; i++) {
88 if (krb5_principal_compare(context, target, &acl->val[i]) == TRUE)
92 ret = KRB5KDC_ERR_BADOPTION;
94 kdc_log(context, config, 4,
95 "Bad request for constrained delegation");
100 * Validate a protocol transition (S4U2Self) request. If successfully
101 * validated then the client in the request structure will be replaced
102 * with the impersonated client.
106 _kdc_validate_protocol_transition(astgs_request_t r, const PA_DATA *for_user)
109 KDC_REQ_BODY *b = &r->req.req_body;
110 EncTicketPart *ticket = &r->ticket->ticket;
111 hdb_entry *s4u_client = NULL;
113 int flags = HDB_F_FOR_TGS_REQ;
114 krb5_principal s4u_client_name = NULL, s4u_canon_client_name = NULL;
115 krb5_pac s4u_pac = NULL;
116 char *s4ucname = NULL;
122 heim_assert(r->client != NULL, "client must be non-NULL");
124 memset(&self, 0, sizeof(self));
126 if (b->kdc_options.canonicalize)
127 flags |= HDB_F_CANON;
129 ret = decode_PA_S4U2Self(for_user->padata_value.data,
130 for_user->padata_value.length,
133 kdc_audit_addreason((kdc_request_t)r,
134 "Failed to decode PA-S4U2Self");
135 kdc_log(r->context, r->config, 4, "Failed to decode PA-S4U2Self");
139 if (!krb5_checksum_is_keyed(r->context, self.cksum.cksumtype)) {
140 kdc_audit_addreason((kdc_request_t)r,
141 "PA-S4U2Self with unkeyed checksum");
142 kdc_log(r->context, r->config, 4, "Reject PA-S4U2Self with unkeyed checksum");
143 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
147 ret = _krb5_s4u2self_to_checksumdata(r->context, &self, &datack);
151 ret = krb5_crypto_init(r->context, &ticket->key, 0, &crypto);
153 const char *msg = krb5_get_error_message(r->context, ret);
154 krb5_data_free(&datack);
155 kdc_log(r->context, r->config, 4, "krb5_crypto_init failed: %s", msg);
156 krb5_free_error_message(r->context, msg);
160 /* Allow HMAC_MD5 checksum with any key type */
161 if (self.cksum.cksumtype == CKSUMTYPE_HMAC_MD5) {
162 struct krb5_crypto_iov iov;
163 unsigned char csdata[16];
166 cs.checksum.length = sizeof(csdata);
167 cs.checksum.data = &csdata;
169 iov.data.data = datack.data;
170 iov.data.length = datack.length;
171 iov.flags = KRB5_CRYPTO_TYPE_DATA;
173 ret = _krb5_HMAC_MD5_checksum(r->context, NULL, &crypto->key,
174 KRB5_KU_OTHER_CKSUM, &iov, 1,
177 krb5_data_ct_cmp(&cs.checksum, &self.cksum.checksum) != 0)
178 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
180 ret = _kdc_verify_checksum(r->context,
186 krb5_data_free(&datack);
187 krb5_crypto_destroy(r->context, crypto);
189 const char *msg = krb5_get_error_message(r->context, ret);
190 kdc_audit_addreason((kdc_request_t)r,
191 "S4U2Self checksum failed");
192 kdc_log(r->context, r->config, 4,
193 "krb5_verify_checksum failed for S4U2Self: %s", msg);
194 krb5_free_error_message(r->context, msg);
198 ret = _krb5_principalname2krb5_principal(r->context,
205 ret = krb5_unparse_name(r->context, s4u_client_name, &s4ucname);
210 * Note no HDB_F_SYNTHETIC_OK -- impersonating non-existent clients
211 * is probably not desirable!
213 ret = _kdc_db_fetch(r->context, r->config, s4u_client_name,
214 HDB_F_GET_CLIENT | flags, NULL,
215 &s4u_clientdb, &s4u_client);
220 * If the client belongs to the same realm as our krbtgt, it
221 * should exist in the local database.
224 if (ret == HDB_ERR_NOENTRY)
225 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
226 msg = krb5_get_error_message(r->context, ret);
227 kdc_audit_addreason((kdc_request_t)r,
228 "S4U2Self principal to impersonate not found");
229 kdc_log(r->context, r->config, 2,
230 "S4U2Self principal to impersonate %s not found in database: %s",
232 krb5_free_error_message(r->context, msg);
237 * Ignore require_pwchange and pw_end attributes (as Windows does),
238 * since S4U2Self is not password authentication.
240 s4u_client->flags.require_pwchange = FALSE;
241 free(s4u_client->pw_end);
242 s4u_client->pw_end = NULL;
244 ret = kdc_check_flags(r, FALSE, s4u_client, r->server);
246 goto out; /* kdc_check_flags() calls kdc_audit_addreason() */
248 ret = _kdc_pac_generate(r,
252 KRB5_PAC_WAS_GIVEN_IMPLICITLY,
255 kdc_log(r->context, r->config, 4, "PAC generation failed for -- %s", s4ucname);
260 * Check that service doing the impersonating is
261 * requesting a ticket to it-self.
263 ret = _kdc_check_client_matches_target_service(r->context,
270 kdc_log(r->context, r->config, 4, "S4U2Self: %s is not allowed "
271 "to impersonate to service "
272 "(tried for user %s to service %s)",
273 r->cname, s4ucname, r->sname);
277 ret = krb5_copy_principal(r->context, s4u_client->principal,
278 &s4u_canon_client_name);
283 * If the service isn't trusted for authentication to
284 * delegation or if the impersonate client is disallowed
285 * forwardable, remove the forwardable flag.
287 if (r->client->flags.trusted_for_delegation &&
288 s4u_client->flags.forwardable) {
289 str = " [forwardable]";
291 b->kdc_options.forwardable = 0;
294 kdc_log(r->context, r->config, 4, "s4u2self %s impersonating %s to "
295 "service %s%s", r->cname, s4ucname, r->sname, str);
298 * Replace all client information in the request with the
299 * impersonated client. (The audit entry containing the original
300 * client name will have been created before this point.)
302 _kdc_request_set_cname_nocopy((kdc_request_t)r, &s4ucname);
303 _kdc_request_set_client_princ_nocopy(r, &s4u_client_name);
305 _kdc_free_ent(r->context, r->clientdb, r->client);
306 r->client = s4u_client;
308 r->clientdb = s4u_clientdb;
311 _kdc_request_set_canon_client_princ_nocopy(r, &s4u_canon_client_name);
312 _kdc_request_set_pac_nocopy(r, &s4u_pac);
316 _kdc_free_ent(r->context, s4u_clientdb, s4u_client);
317 krb5_free_principal(r->context, s4u_client_name);
318 krb5_xfree(s4ucname);
319 krb5_free_principal(r->context, s4u_canon_client_name);
320 krb5_pac_free(r->context, s4u_pac);
322 free_PA_S4U2Self(&self);
328 * Validate a constrained delegation (S4U2Proxy) request. If
329 * successfully validated then the client in the request structure will
330 * be replaced with the client from the evidence ticket.
334 _kdc_validate_constrained_delegation(astgs_request_t r)
337 KDC_REQ_BODY *b = &r->req.req_body;
338 int flags = HDB_F_FOR_TGS_REQ;
339 krb5_principal s4u_client_name = NULL, s4u_server_name = NULL;
340 krb5_principal s4u_canon_client_name = NULL;
341 krb5_pac s4u_pac = NULL;
342 uint64_t s4u_pac_attributes;
343 char *s4ucname = NULL, *s4usname = NULL;
344 EncTicketPart evidence_tkt;
346 hdb_entry *s4u_client = NULL;
347 HDB *s4u_serverdb = NULL;
348 hdb_entry *s4u_server = NULL;
349 krb5_boolean ad_kdc_issued = FALSE;
352 krb5_const_realm local_realm;
354 memset(&evidence_tkt, 0, sizeof(evidence_tkt));
356 krb5_principal_get_comp_string(r->context, r->krbtgt->principal, 1);
359 * We require that the service's TGT has a PAC; this will have been
360 * validated prior to this function being called.
362 if (r->pac == NULL) {
363 ret = KRB5KDC_ERR_BADOPTION;
364 kdc_audit_addreason((kdc_request_t)r, "Missing PAC");
365 kdc_log(r->context, r->config, 4,
366 "Constrained delegation without PAC, %s/%s",
371 t = &b->additional_tickets->val[0];
373 ret = _krb5_principalname2krb5_principal(r->context,
380 ret = krb5_unparse_name(r->context, s4u_server_name, &s4usname);
385 * Look up the name given in the ticket in the database. We don’t ask for
386 * canonicalisation, so that we get back the same principal that was
387 * specified in the ticket.
389 ret = _kdc_db_fetch(r->context, r->config, s4u_server_name,
390 HDB_F_GET_SERVER | HDB_F_DELAY_NEW_KEYS | flags,
391 NULL, &s4u_serverdb, &s4u_server);
392 if (ret == HDB_ERR_NOENTRY)
393 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
395 kdc_audit_addreason((kdc_request_t)r,
396 "Constrained delegation service principal unknown");
401 * Check that the delegating server (r->client) is the same one as specified
402 * in the ticket. This is to make sure that the server hasn’t forged the
403 * sname, which is in the unencrypted part of the ticket.
405 ret = _kdc_check_client_matches_target_service(r->context,
411 if (ret == KRB5KRB_AP_ERR_BADMATCH)
412 ret = KRB5KDC_ERR_BADOPTION;
416 ret = hdb_enctype2key(r->context, r->client,
417 hdb_kvno2keys(r->context, r->client,
418 t->enc_part.kvno ? * t->enc_part.kvno : 0),
419 t->enc_part.etype, &clientkey);
421 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
425 ret = krb5_decrypt_ticket(r->context, t, &clientkey->key, &evidence_tkt, 0);
427 kdc_audit_addreason((kdc_request_t)r,
428 "Failed to decrypt constrained delegation ticket");
429 kdc_log(r->context, r->config, 4,
430 "failed to decrypt ticket for "
431 "constrained delegation from %s to %s", r->cname, r->sname);
435 ret = _krb5_principalname2krb5_principal(r->context,
438 evidence_tkt.crealm);
442 ret = krb5_unparse_name(r->context, s4u_client_name, &s4ucname);
446 kdc_audit_addkv((kdc_request_t)r, 0, "impersonatee", "%s", s4ucname);
448 /* check that ticket is valid */
449 if (evidence_tkt.flags.forwardable == 0) {
450 kdc_audit_addreason((kdc_request_t)r,
451 "Missing forwardable flag on ticket for constrained delegation");
452 kdc_log(r->context, r->config, 4,
453 "Missing forwardable flag on ticket for "
454 "constrained delegation from %s (%s) as %s to %s ",
455 r->cname, s4usname, s4ucname, r->sname);
456 ret = KRB5KDC_ERR_BADOPTION;
460 ret = check_constrained_delegation(r->context, r->config, r->clientdb,
461 r->client, r->server, r->server_princ);
463 kdc_audit_addreason((kdc_request_t)r,
464 "Constrained delegation not allowed");
465 kdc_log(r->context, r->config, 4,
466 "constrained delegation from %s (%s) as %s to %s not allowed",
467 r->cname, s4usname, s4ucname, r->sname);
471 ret = _kdc_verify_flags(r->context, r->config, &evidence_tkt, s4ucname);
473 kdc_audit_addreason((kdc_request_t)r,
474 "Constrained delegation ticket expired or invalid");
478 /* Try lookup the delegated client in DB */
479 ret = _kdc_db_fetch_client(r->context, r->config, flags,
480 s4u_client_name, s4ucname, local_realm,
481 &s4u_clientdb, &s4u_client);
485 if (s4u_client != NULL) {
486 ret = kdc_check_flags(r, FALSE, s4u_client, r->server);
492 * TODO: pass in t->sname and t->realm and build
493 * a S4U_DELEGATION_INFO blob to the PAC.
495 ret = _kdc_check_pac(r, s4u_client_name, s4u_server_name,
496 s4u_client, r->server, r->krbtgt, r->client,
497 &clientkey->key, &r->ticket_key->key, &evidence_tkt,
498 &ad_kdc_issued, &s4u_pac,
499 &s4u_canon_client_name, &s4u_pac_attributes);
501 const char *msg = krb5_get_error_message(r->context, ret);
502 kdc_audit_addreason((kdc_request_t)r,
503 "Constrained delegation ticket PAC check failed");
504 kdc_log(r->context, r->config, 4,
505 "Verify delegated PAC failed to %s for client "
506 "%s (%s) as %s from %s with %s",
507 r->sname, r->cname, s4usname, s4ucname, r->from, msg);
508 krb5_free_error_message(r->context, msg);
512 if (s4u_pac == NULL || !ad_kdc_issued) {
513 ret = KRB5KDC_ERR_BADOPTION;
514 kdc_log(r->context, r->config, 4,
515 "Ticket not signed with PAC; service %s failed for "
516 "for delegation to %s for client %s (%s) from %s; (%s).",
517 r->sname, s4ucname, s4usname, r->cname, r->from,
518 s4u_pac ? "Ticket unsigned" : "No PAC");
519 kdc_audit_addreason((kdc_request_t)r,
520 "Constrained delegation ticket not signed");
524 heim_assert(s4u_pac != NULL, "ad_kdc_issued implies the PAC is non-NULL");
526 ret = _kdc_pac_update(r, s4u_client_name, s4u_server_name,
527 s4u_client, r->server, r->krbtgt,
529 if (ret == KRB5_PLUGIN_NO_HANDLE) {
533 const char *msg = krb5_get_error_message(r->context, ret);
534 kdc_audit_addreason((kdc_request_t)r,
535 "Constrained delegation ticket PAC update failed");
536 kdc_log(r->context, r->config, 4,
537 "Update delegated PAC failed to %s for client "
538 "%s (%s) as %s from %s with %s",
539 r->sname, r->cname, s4usname, s4ucname, r->from, msg);
540 krb5_free_error_message(r->context, msg);
545 * If the evidence ticket PAC didn't include PAC_UPN_DNS_INFO with
546 * the canonical client name, but the user is local to our KDC, we
547 * can insert the canonical client name ourselves.
549 if (s4u_canon_client_name == NULL && s4u_client != NULL) {
550 ret = krb5_copy_principal(r->context, s4u_client->principal,
551 &s4u_canon_client_name);
556 if (b->enc_authorization_data && r->rk_is_subkey == 0) {
557 krb5_free_keyblock_contents(r->context, &r->enc_ad_key);
558 ret = krb5_copy_keyblock_contents(r->context,
565 kdc_log(r->context, r->config, 4, "constrained delegation for %s "
566 "from %s (%s) to %s", s4ucname, r->cname, s4usname, r->sname);
569 * Replace all client information in the request with the
570 * impersonated client. (The audit entry containing the original
571 * client name will have been created before this point.)
573 _kdc_request_set_cname_nocopy((kdc_request_t)r, &s4ucname);
574 _kdc_request_set_client_princ_nocopy(r, &s4u_client_name);
576 _kdc_free_ent(r->context, r->clientdb, r->client);
577 r->client = s4u_client;
579 r->clientdb = s4u_clientdb;
582 _kdc_request_set_canon_client_princ_nocopy(r, &s4u_canon_client_name);
583 _kdc_request_set_pac_nocopy(r, &s4u_pac);
585 r->pac_attributes = s4u_pac_attributes;
587 r->et.authtime = evidence_tkt.authtime;
591 _kdc_free_ent(r->context, s4u_clientdb, s4u_client);
593 _kdc_free_ent(r->context, s4u_serverdb, s4u_server);
594 krb5_free_principal(r->context, s4u_client_name);
595 krb5_xfree(s4ucname);
596 krb5_free_principal(r->context, s4u_server_name);
597 krb5_xfree(s4usname);
598 krb5_free_principal(r->context, s4u_canon_client_name);
599 krb5_pac_free(r->context, s4u_pac);
601 free_EncTicketPart(&evidence_tkt);