Line data Source code
1 : /*
2 : * Copyright (c) 1997-2008 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions
8 : * are met:
9 : *
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : *
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.
16 : *
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.
20 : *
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
31 : * SUCH DAMAGE.
32 : */
33 :
34 : #include "kdc_locl.h"
35 :
36 : /*
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/
40 : */
41 :
42 : /*
43 : * Determine if constrained delegation is allowed from this client to this server
44 : */
45 :
46 : static krb5_error_code
47 146 : check_constrained_delegation(krb5_context context,
48 : krb5_kdc_configuration *config,
49 : HDB *clientdb,
50 : hdb_entry *client,
51 : hdb_entry *server,
52 : krb5_const_principal target)
53 : {
54 0 : const HDB_Ext_Constrained_delegation_acl *acl;
55 0 : krb5_error_code ret;
56 0 : size_t i;
57 :
58 : /*
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.
63 : */
64 146 : if (!krb5_realm_compare(context, client->principal, server->principal)) {
65 0 : ret = KRB5KDC_ERR_BADOPTION;
66 0 : kdc_log(context, config, 4,
67 : "Bad request for constrained delegation");
68 0 : return ret;
69 : }
70 :
71 146 : if (clientdb->hdb_check_constrained_delegation) {
72 146 : ret = clientdb->hdb_check_constrained_delegation(context, clientdb, client, target);
73 146 : if (ret == 0)
74 116 : return 0;
75 : } else {
76 : /* if client delegates to itself, that ok */
77 0 : if (krb5_principal_compare(context, client->principal, server->principal) == TRUE)
78 0 : return 0;
79 :
80 0 : ret = hdb_entry_get_ConstrainedDelegACL(client, &acl);
81 0 : if (ret) {
82 0 : krb5_clear_error_message(context);
83 0 : return ret;
84 : }
85 :
86 0 : if (acl) {
87 0 : for (i = 0; i < acl->len; i++) {
88 0 : if (krb5_principal_compare(context, target, &acl->val[i]) == TRUE)
89 0 : return 0;
90 : }
91 : }
92 0 : ret = KRB5KDC_ERR_BADOPTION;
93 : }
94 30 : kdc_log(context, config, 4,
95 : "Bad request for constrained delegation");
96 30 : return ret;
97 : }
98 :
99 : /*
100 : * Determine if resource-based constrained delegation is allowed from this
101 : * client to this server
102 : */
103 :
104 : static krb5_error_code
105 136 : check_rbcd(krb5_context context,
106 : krb5_kdc_configuration *config,
107 : HDB *clientdb,
108 : krb5_const_principal s4u_principal,
109 : const hdb_entry *client_krbtgt,
110 : const hdb_entry *client,
111 : const hdb_entry *device_krbtgt,
112 : const hdb_entry *device,
113 : krb5_const_pac client_pac,
114 : krb5_const_pac device_pac,
115 : const hdb_entry *target)
116 : {
117 136 : krb5_error_code ret = KRB5KDC_ERR_BADOPTION;
118 :
119 136 : if (clientdb->hdb_check_rbcd) {
120 136 : ret = clientdb->hdb_check_rbcd(context,
121 : clientdb,
122 : client_krbtgt,
123 : client,
124 : device_krbtgt,
125 : device,
126 : s4u_principal,
127 : client_pac,
128 : device_pac,
129 : target);
130 136 : if (ret == 0)
131 109 : return 0;
132 : }
133 :
134 27 : kdc_log(context, config, 4,
135 : "Bad request for resource-based constrained delegation");
136 27 : return ret;
137 : }
138 :
139 : /*
140 : * Validate a protocol transition (S4U2Self) request. If successfully
141 : * validated then the client in the request structure will be replaced
142 : * with the impersonated client.
143 : */
144 :
145 : krb5_error_code
146 658 : _kdc_validate_protocol_transition(astgs_request_t r, const PA_DATA *for_user)
147 : {
148 0 : krb5_error_code ret;
149 658 : KDC_REQ_BODY *b = &r->req.req_body;
150 658 : EncTicketPart *ticket = &r->ticket->ticket;
151 658 : hdb_entry *s4u_client = NULL;
152 0 : HDB *s4u_clientdb;
153 658 : int flags = HDB_F_FOR_TGS_REQ;
154 658 : krb5_principal s4u_client_name = NULL, s4u_canon_client_name = NULL;
155 658 : krb5_pac s4u_pac = NULL;
156 658 : char *s4ucname = NULL;
157 0 : krb5_crypto crypto;
158 0 : krb5_data datack;
159 0 : PA_S4U2Self self;
160 0 : const char *str;
161 :
162 658 : heim_assert(r->client != NULL, "client must be non-NULL");
163 :
164 658 : memset(&self, 0, sizeof(self));
165 :
166 658 : if (b->kdc_options.canonicalize)
167 576 : flags |= HDB_F_CANON;
168 :
169 658 : ret = decode_PA_S4U2Self(for_user->padata_value.data,
170 658 : for_user->padata_value.length,
171 : &self, NULL);
172 658 : if (ret) {
173 0 : kdc_audit_addreason((kdc_request_t)r,
174 : "Failed to decode PA-S4U2Self");
175 0 : kdc_log(r->context, r->config, 4, "Failed to decode PA-S4U2Self");
176 0 : goto out;
177 : }
178 :
179 658 : if (!krb5_checksum_is_keyed(r->context, self.cksum.cksumtype)) {
180 4 : kdc_audit_addreason((kdc_request_t)r,
181 : "PA-S4U2Self with unkeyed checksum");
182 4 : kdc_log(r->context, r->config, 4, "Reject PA-S4U2Self with unkeyed checksum");
183 4 : ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
184 4 : goto out;
185 : }
186 :
187 654 : ret = _krb5_s4u2self_to_checksumdata(r->context, &self, &datack);
188 654 : if (ret)
189 0 : goto out;
190 :
191 654 : ret = krb5_crypto_init(r->context, &ticket->key, 0, &crypto);
192 654 : if (ret) {
193 0 : const char *msg = krb5_get_error_message(r->context, ret);
194 0 : krb5_data_free(&datack);
195 0 : kdc_log(r->context, r->config, 4, "krb5_crypto_init failed: %s", msg);
196 0 : krb5_free_error_message(r->context, msg);
197 0 : goto out;
198 : }
199 :
200 : /* Allow HMAC_MD5 checksum with any key type */
201 654 : if (self.cksum.cksumtype == CKSUMTYPE_HMAC_MD5) {
202 0 : struct krb5_crypto_iov iov;
203 0 : unsigned char csdata[16];
204 0 : Checksum cs;
205 :
206 123 : cs.checksum.length = sizeof(csdata);
207 123 : cs.checksum.data = &csdata;
208 :
209 123 : iov.data.data = datack.data;
210 123 : iov.data.length = datack.length;
211 123 : iov.flags = KRB5_CRYPTO_TYPE_DATA;
212 :
213 123 : ret = _krb5_HMAC_MD5_checksum(r->context, NULL, &crypto->key,
214 : KRB5_KU_OTHER_CKSUM, &iov, 1,
215 : &cs);
216 246 : if (ret == 0 &&
217 123 : krb5_data_ct_cmp(&cs.checksum, &self.cksum.checksum) != 0)
218 0 : ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
219 : } else {
220 531 : ret = _kdc_verify_checksum(r->context,
221 : crypto,
222 : KRB5_KU_OTHER_CKSUM,
223 : &datack,
224 : &self.cksum);
225 : }
226 654 : krb5_data_free(&datack);
227 654 : krb5_crypto_destroy(r->context, crypto);
228 654 : if (ret) {
229 2 : const char *msg = krb5_get_error_message(r->context, ret);
230 2 : kdc_audit_addreason((kdc_request_t)r,
231 : "S4U2Self checksum failed");
232 2 : kdc_log(r->context, r->config, 4,
233 : "krb5_verify_checksum failed for S4U2Self: %s", msg);
234 2 : krb5_free_error_message(r->context, msg);
235 2 : goto out;
236 : }
237 :
238 652 : ret = _krb5_principalname2krb5_principal(r->context,
239 : &s4u_client_name,
240 : self.name,
241 : self.realm);
242 652 : if (ret)
243 0 : goto out;
244 :
245 652 : ret = krb5_unparse_name(r->context, s4u_client_name, &s4ucname);
246 652 : if (ret)
247 0 : goto out;
248 :
249 : /*
250 : * Note no HDB_F_SYNTHETIC_OK -- impersonating non-existent clients
251 : * is probably not desirable!
252 : */
253 652 : ret = _kdc_db_fetch(r->context, r->config, s4u_client_name,
254 652 : HDB_F_GET_CLIENT | flags, NULL,
255 : &s4u_clientdb, &s4u_client);
256 652 : if (ret) {
257 0 : const char *msg;
258 :
259 : /*
260 : * If the client belongs to the same realm as our krbtgt, it
261 : * should exist in the local database.
262 : *
263 : */
264 0 : if (ret == HDB_ERR_NOENTRY)
265 0 : ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
266 0 : msg = krb5_get_error_message(r->context, ret);
267 0 : kdc_audit_addreason((kdc_request_t)r,
268 : "S4U2Self principal to impersonate not found");
269 0 : kdc_log(r->context, r->config, 2,
270 : "S4U2Self principal to impersonate %s not found in database: %s",
271 : s4ucname, msg);
272 0 : krb5_free_error_message(r->context, msg);
273 0 : goto out;
274 : }
275 :
276 : /*
277 : * Ignore require_pwchange and pw_end attributes (as Windows does),
278 : * since S4U2Self is not password authentication.
279 : */
280 652 : s4u_client->flags.require_pwchange = FALSE;
281 652 : free(s4u_client->pw_end);
282 652 : s4u_client->pw_end = NULL;
283 :
284 652 : ret = kdc_check_flags(r, FALSE, s4u_client, r->server);
285 652 : if (ret)
286 0 : goto out; /* kdc_check_flags() calls kdc_audit_addreason() */
287 :
288 652 : ret = _kdc_pac_generate(r,
289 : s4u_client,
290 : r->server,
291 : NULL,
292 : KRB5_PAC_WAS_GIVEN_IMPLICITLY,
293 : &s4u_pac);
294 652 : if (ret) {
295 0 : kdc_log(r->context, r->config, 4, "PAC generation failed for -- %s", s4ucname);
296 0 : goto out;
297 : }
298 :
299 : /*
300 : * Check that service doing the impersonating is
301 : * requesting a ticket to it-self.
302 : */
303 652 : ret = _kdc_check_client_matches_target_service(r->context,
304 : r->config,
305 : r->clientdb,
306 : r->client,
307 : r->server,
308 652 : r->server_princ);
309 652 : if (ret) {
310 2 : kdc_log(r->context, r->config, 4, "S4U2Self: %s is not allowed "
311 : "to impersonate to service "
312 : "(tried for user %s to service %s)",
313 : r->cname, s4ucname, r->sname);
314 2 : goto out;
315 : }
316 :
317 650 : ret = krb5_copy_principal(r->context, s4u_client->principal,
318 : &s4u_canon_client_name);
319 650 : if (ret)
320 0 : goto out;
321 :
322 : /*
323 : * If the service isn't trusted for authentication to
324 : * delegation or if the impersonate client is disallowed
325 : * forwardable, remove the forwardable flag.
326 : */
327 650 : if (r->client->flags.trusted_for_delegation &&
328 125 : s4u_client->flags.forwardable) {
329 124 : str = " [forwardable]";
330 : } else {
331 526 : b->kdc_options.forwardable = 0;
332 526 : str = "";
333 : }
334 650 : kdc_log(r->context, r->config, 4, "s4u2self %s impersonating %s to "
335 : "service %s%s", r->cname, s4ucname, r->sname, str);
336 :
337 : /*
338 : * Replace all client information in the request with the
339 : * impersonated client. (The audit entry containing the original
340 : * client name will have been created before this point.)
341 : */
342 650 : _kdc_request_set_cname_nocopy((kdc_request_t)r, &s4ucname);
343 650 : _kdc_request_set_client_princ_nocopy(r, &s4u_client_name);
344 :
345 650 : _kdc_free_ent(r->context, r->clientdb, r->client);
346 650 : r->client = s4u_client;
347 650 : s4u_client = NULL;
348 650 : r->clientdb = s4u_clientdb;
349 650 : s4u_clientdb = NULL;
350 :
351 650 : _kdc_request_set_canon_client_princ_nocopy(r, &s4u_canon_client_name);
352 650 : _kdc_request_set_pac_nocopy(r, &s4u_pac);
353 :
354 658 : out:
355 658 : if (s4u_client)
356 2 : _kdc_free_ent(r->context, s4u_clientdb, s4u_client);
357 658 : krb5_free_principal(r->context, s4u_client_name);
358 658 : krb5_xfree(s4ucname);
359 658 : krb5_free_principal(r->context, s4u_canon_client_name);
360 658 : krb5_pac_free(r->context, s4u_pac);
361 :
362 658 : free_PA_S4U2Self(&self);
363 :
364 658 : return ret;
365 : }
366 :
367 : /*
368 : * Validate a constrained delegation (S4U2Proxy) request. If
369 : * successfully validated then the client in the request structure will
370 : * be replaced with the client from the evidence ticket.
371 : */
372 :
373 : krb5_error_code
374 264 : _kdc_validate_constrained_delegation(astgs_request_t r)
375 : {
376 0 : krb5_error_code ret;
377 264 : KDC_REQ_BODY *b = &r->req.req_body;
378 264 : int flags = HDB_F_FOR_TGS_REQ;
379 264 : krb5_principal s4u_client_name = NULL, s4u_server_name = NULL;
380 264 : krb5_principal s4u_canon_client_name = NULL;
381 264 : krb5_pac s4u_pac = NULL;
382 0 : uint64_t s4u_pac_attributes;
383 264 : char *s4ucname = NULL, *s4usname = NULL;
384 0 : EncTicketPart evidence_tkt;
385 0 : HDB *s4u_clientdb;
386 264 : hdb_entry *s4u_client = NULL;
387 264 : HDB *s4u_serverdb = NULL;
388 264 : hdb_entry *s4u_server = NULL;
389 264 : krb5_boolean ad_kdc_issued = FALSE;
390 0 : Key *clientkey;
391 0 : Ticket *t;
392 0 : krb5_const_realm local_realm;
393 264 : const PA_DATA *pac_options_data = NULL;
394 264 : int pac_options_data_idx = 0;
395 264 : krb5_boolean rbcd_support = FALSE;
396 :
397 264 : memset(&evidence_tkt, 0, sizeof(evidence_tkt));
398 0 : local_realm =
399 264 : krb5_principal_get_comp_string(r->context, r->krbtgt->principal, 1);
400 :
401 : /*
402 : * We require that the service's TGT has a PAC; this will have been
403 : * validated prior to this function being called.
404 : */
405 264 : if (r->pac == NULL) {
406 0 : ret = KRB5KDC_ERR_BADOPTION;
407 0 : kdc_audit_addreason((kdc_request_t)r, "Missing PAC");
408 0 : kdc_log(r->context, r->config, 4,
409 : "Constrained delegation without PAC, %s/%s",
410 : r->cname, r->sname);
411 0 : goto out;
412 : }
413 :
414 264 : t = &b->additional_tickets->val[0];
415 :
416 264 : ret = _krb5_principalname2krb5_principal(r->context,
417 : &s4u_server_name,
418 : t->sname,
419 : t->realm);
420 264 : if (ret)
421 0 : goto out;
422 :
423 264 : ret = krb5_unparse_name(r->context, s4u_server_name, &s4usname);
424 264 : if (ret)
425 0 : goto out;
426 :
427 : /*
428 : * Look up the name given in the ticket in the database. We don’t ask for
429 : * canonicalisation, so that we get back the same principal that was
430 : * specified in the ticket.
431 : */
432 264 : ret = _kdc_db_fetch(r->context, r->config, s4u_server_name,
433 264 : HDB_F_GET_SERVER | HDB_F_DELAY_NEW_KEYS | flags,
434 : NULL, &s4u_serverdb, &s4u_server);
435 264 : if (ret == HDB_ERR_NOENTRY)
436 0 : ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
437 264 : if (ret) {
438 0 : kdc_audit_addreason((kdc_request_t)r,
439 : "Constrained delegation service principal unknown");
440 0 : goto out;
441 : }
442 :
443 : /*
444 : * Check that the delegating server (r->client) is the same one as specified
445 : * in the ticket. This is to make sure that the server hasn’t forged the
446 : * sname, which is in the unencrypted part of the ticket.
447 : */
448 264 : ret = _kdc_check_client_matches_target_service(r->context,
449 : r->config,
450 : s4u_serverdb,
451 : s4u_server,
452 : r->client,
453 264 : r->client_princ);
454 264 : if (ret == KRB5KRB_AP_ERR_BADMATCH)
455 2 : ret = KRB5KDC_ERR_BADOPTION;
456 264 : if (ret)
457 2 : goto out;
458 :
459 262 : ret = hdb_enctype2key(r->context, r->client,
460 262 : hdb_kvno2keys(r->context, r->client,
461 262 : t->enc_part.kvno ? * t->enc_part.kvno : 0),
462 : t->enc_part.etype, &clientkey);
463 262 : if (ret) {
464 0 : ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
465 0 : goto out;
466 : }
467 :
468 262 : ret = krb5_decrypt_ticket(r->context, t, &clientkey->key, &evidence_tkt, 0);
469 262 : if (ret) {
470 0 : kdc_audit_addreason((kdc_request_t)r,
471 : "Failed to decrypt constrained delegation ticket");
472 0 : kdc_log(r->context, r->config, 4,
473 : "failed to decrypt ticket for "
474 : "constrained delegation from %s to %s", r->cname, r->sname);
475 0 : goto out;
476 : }
477 :
478 262 : ret = _krb5_principalname2krb5_principal(r->context,
479 : &s4u_client_name,
480 : evidence_tkt.cname,
481 : evidence_tkt.crealm);
482 262 : if (ret)
483 0 : goto out;
484 :
485 262 : ret = krb5_unparse_name(r->context, s4u_client_name, &s4ucname);
486 262 : if (ret)
487 0 : goto out;
488 :
489 262 : kdc_audit_addkv((kdc_request_t)r, 0, "impersonatee", "%s", s4ucname);
490 :
491 : /* check that ticket is valid */
492 262 : if (evidence_tkt.flags.forwardable == 0) {
493 7 : kdc_audit_addreason((kdc_request_t)r,
494 : "Missing forwardable flag on ticket for constrained delegation");
495 7 : kdc_log(r->context, r->config, 4,
496 : "Missing forwardable flag on ticket for "
497 : "constrained delegation from %s (%s) as %s to %s ",
498 : r->cname, s4usname, s4ucname, r->sname);
499 7 : ret = KRB5KDC_ERR_BADOPTION;
500 7 : goto out;
501 : }
502 :
503 255 : pac_options_data = _kdc_find_padata(&r->req,
504 : &pac_options_data_idx,
505 : KRB5_PADATA_PAC_OPTIONS);
506 255 : if (pac_options_data != NULL) {
507 0 : PA_PAC_OPTIONS pac_options;
508 145 : size_t size = 0;
509 :
510 145 : ret = decode_PA_PAC_OPTIONS(pac_options_data->padata_value.data,
511 145 : pac_options_data->padata_value.length,
512 : &pac_options,
513 : &size);
514 145 : if (ret) {
515 0 : goto out;
516 : }
517 :
518 145 : if (size != pac_options_data->padata_value.length) {
519 0 : free_PA_PAC_OPTIONS(&pac_options);
520 0 : ret = KRB5KDC_ERR_BADOPTION;
521 0 : goto out;
522 : }
523 :
524 145 : rbcd_support = pac_options.flags.resource_based_constrained_delegation != 0;
525 :
526 145 : free_PA_PAC_OPTIONS(&pac_options);
527 : }
528 :
529 255 : if (rbcd_support) {
530 136 : ret = check_rbcd(r->context, r->config, r->clientdb,
531 : s4u_client_name,
532 136 : r->krbtgt, r->client,
533 136 : r->armor_server, r->armor_client,
534 136 : r->pac, r->armor_pac,
535 136 : r->server);
536 : } else {
537 119 : ret = KRB5KDC_ERR_BADOPTION;
538 : }
539 255 : if (ret == KRB5KDC_ERR_BADOPTION) {
540 : /* RBCD was denied or not supported; try constrained delegation. */
541 146 : ret = check_constrained_delegation(r->context, r->config, r->clientdb,
542 146 : r->client, r->server, r->server_princ);
543 146 : if (ret) {
544 30 : kdc_audit_addreason((kdc_request_t)r,
545 : "Constrained delegation not allowed");
546 30 : kdc_log(r->context, r->config, 4,
547 : "constrained delegation from %s (%s) as %s to %s not allowed",
548 : r->cname, s4usname, s4ucname, r->sname);
549 30 : goto out;
550 : }
551 109 : } else if (ret) {
552 0 : kdc_audit_addreason((kdc_request_t)r,
553 : "Resource-based constrained delegation not allowed");
554 0 : kdc_log(r->context, r->config, 4,
555 : "resource-based constrained delegation from %s (%s) as %s to %s not allowed",
556 : r->cname, s4usname, s4ucname, r->sname);
557 0 : goto out;
558 : }
559 :
560 225 : ret = _kdc_verify_flags(r->context, r->config, &evidence_tkt, s4ucname);
561 225 : if (ret) {
562 0 : kdc_audit_addreason((kdc_request_t)r,
563 : "Constrained delegation ticket expired or invalid");
564 0 : goto out;
565 : }
566 :
567 : /* Try lookup the delegated client in DB */
568 225 : ret = _kdc_db_fetch_client(r->context, r->config, flags,
569 : s4u_client_name, s4ucname, local_realm,
570 : &s4u_clientdb, &s4u_client);
571 225 : if (ret)
572 0 : goto out;
573 :
574 225 : if (s4u_client != NULL) {
575 224 : ret = kdc_check_flags(r, FALSE, s4u_client, r->server);
576 224 : if (ret)
577 0 : goto out;
578 : }
579 :
580 : /*
581 : * TODO: pass in t->sname and t->realm and build
582 : * a S4U_DELEGATION_INFO blob to the PAC.
583 : */
584 225 : ret = _kdc_check_pac(r, s4u_client_name, s4u_server,
585 : s4u_client, r->server, r->krbtgt, r->client,
586 225 : &clientkey->key, &r->ticket_key->key, &evidence_tkt,
587 : &ad_kdc_issued, &s4u_pac,
588 : &s4u_canon_client_name, &s4u_pac_attributes);
589 225 : if (ret) {
590 70 : const char *msg = krb5_get_error_message(r->context, ret);
591 70 : kdc_audit_addreason((kdc_request_t)r,
592 : "Constrained delegation ticket PAC check failed");
593 70 : kdc_log(r->context, r->config, 4,
594 : "Verify delegated PAC failed to %s for client "
595 : "%s (%s) as %s from %s with %s",
596 : r->sname, r->cname, s4usname, s4ucname, r->from, msg);
597 70 : krb5_free_error_message(r->context, msg);
598 70 : goto out;
599 : }
600 :
601 155 : if (s4u_pac == NULL || !ad_kdc_issued) {
602 3 : ret = KRB5KDC_ERR_BADOPTION;
603 3 : kdc_log(r->context, r->config, 4,
604 : "Ticket not signed with PAC; service %s failed for "
605 : "for delegation to %s for client %s (%s) from %s; (%s).",
606 : r->sname, s4ucname, s4usname, r->cname, r->from,
607 3 : s4u_pac ? "Ticket unsigned" : "No PAC");
608 3 : kdc_audit_addreason((kdc_request_t)r,
609 : "Constrained delegation ticket not signed");
610 3 : goto out;
611 : }
612 :
613 152 : heim_assert(s4u_pac != NULL, "ad_kdc_issued implies the PAC is non-NULL");
614 :
615 152 : ret = _kdc_pac_update(r, s4u_client_name, s4u_server, r->pac,
616 : s4u_client, r->server, r->krbtgt,
617 : &s4u_pac);
618 152 : if (ret == KRB5_PLUGIN_NO_HANDLE) {
619 0 : ret = 0;
620 : }
621 152 : if (ret) {
622 12 : const char *msg = krb5_get_error_message(r->context, ret);
623 12 : kdc_audit_addreason((kdc_request_t)r,
624 : "Constrained delegation ticket PAC update failed");
625 12 : kdc_log(r->context, r->config, 4,
626 : "Update delegated PAC failed to %s for client "
627 : "%s (%s) as %s from %s with %s",
628 : r->sname, r->cname, s4usname, s4ucname, r->from, msg);
629 12 : krb5_free_error_message(r->context, msg);
630 12 : goto out;
631 : }
632 :
633 : /*
634 : * If the evidence ticket PAC didn't include PAC_UPN_DNS_INFO with
635 : * the canonical client name, but the user is local to our KDC, we
636 : * can insert the canonical client name ourselves.
637 : */
638 140 : if (s4u_canon_client_name == NULL && s4u_client != NULL) {
639 0 : ret = krb5_copy_principal(r->context, s4u_client->principal,
640 : &s4u_canon_client_name);
641 0 : if (ret)
642 0 : goto out;
643 : }
644 :
645 140 : if (b->enc_authorization_data && r->rk_is_subkey == 0) {
646 2 : krb5_free_keyblock_contents(r->context, &r->enc_ad_key);
647 2 : ret = krb5_copy_keyblock_contents(r->context,
648 : &evidence_tkt.key,
649 : &r->enc_ad_key);
650 2 : if (ret)
651 0 : goto out;
652 : }
653 :
654 140 : kdc_log(r->context, r->config, 4, "constrained delegation for %s "
655 : "from %s (%s) to %s", s4ucname, r->cname, s4usname, r->sname);
656 :
657 : /*
658 : * Replace all client information in the request with the
659 : * impersonated client. (The audit entry containing the original
660 : * client name will have been created before this point.)
661 : */
662 140 : _kdc_request_set_cname_nocopy((kdc_request_t)r, &s4ucname);
663 140 : _kdc_request_set_client_princ_nocopy(r, &s4u_client_name);
664 :
665 140 : _kdc_free_ent(r->context, r->clientdb, r->client);
666 140 : r->client = s4u_client;
667 140 : s4u_client = NULL;
668 140 : r->clientdb = s4u_clientdb;
669 140 : s4u_clientdb = NULL;
670 :
671 140 : _kdc_request_set_canon_client_princ_nocopy(r, &s4u_canon_client_name);
672 140 : _kdc_request_set_pac_nocopy(r, &s4u_pac);
673 :
674 140 : r->pac_attributes = s4u_pac_attributes;
675 :
676 140 : r->et.authtime = evidence_tkt.authtime;
677 :
678 264 : out:
679 264 : if (s4u_client)
680 85 : _kdc_free_ent(r->context, s4u_clientdb, s4u_client);
681 264 : if (s4u_server)
682 264 : _kdc_free_ent(r->context, s4u_serverdb, s4u_server);
683 264 : krb5_free_principal(r->context, s4u_client_name);
684 264 : krb5_xfree(s4ucname);
685 264 : krb5_free_principal(r->context, s4u_server_name);
686 264 : krb5_xfree(s4usname);
687 264 : krb5_free_principal(r->context, s4u_canon_client_name);
688 264 : krb5_pac_free(r->context, s4u_pac);
689 :
690 264 : free_EncTicketPart(&evidence_tkt);
691 :
692 264 : return ret;
693 : }
|