Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : msDS-ManagedPassword attribute for Group Managed Service Accounts
4 :
5 : Copyright (C) Catalyst.Net Ltd 2024
6 :
7 : This program is free software: you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation, either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <https://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "includes.h"
22 : #include "ldb.h"
23 : #include "ldb_module.h"
24 : #include "ldb_errors.h"
25 : #include "ldb_private.h"
26 : #include "lib/crypto/gkdi.h"
27 : #include "lib/crypto/gmsa.h"
28 : #include "lib/util/data_blob.h"
29 : #include "lib/util/fault.h"
30 : #include "lib/util/time.h"
31 : #include "libcli/security/access_check.h"
32 : #include "librpc/gen_ndr/auth.h"
33 : #include "librpc/gen_ndr/ndr_gkdi.h"
34 : #include "librpc/gen_ndr/ndr_gmsa.h"
35 : #include "librpc/gen_ndr/ndr_security.h"
36 : #include "dsdb/common/util.h"
37 : #include "dsdb/gmsa/gkdi.h"
38 : #include "dsdb/gmsa/util.h"
39 : #include "dsdb/samdb/samdb.h"
40 :
41 : #undef strcasecmp
42 :
43 : enum RootKeyType {
44 : ROOT_KEY_NONE,
45 : ROOT_KEY_SPECIFIC,
46 : ROOT_KEY_NONSPECIFIC,
47 : ROOT_KEY_OBTAINED,
48 : };
49 :
50 : struct RootKey {
51 : TALLOC_CTX *mem_ctx;
52 : enum RootKeyType type;
53 : union {
54 : struct KeyEnvelopeId specific;
55 : struct {
56 : NTTIME key_start_time;
57 : } nonspecific;
58 : struct {
59 : struct gmsa_update_pwd_part key;
60 : struct gmsa_null_terminated_password *password;
61 : } obtained;
62 : } u;
63 : };
64 :
65 : static const struct RootKey empty_root_key = {.type = ROOT_KEY_NONE};
66 :
67 42 : int gmsa_allowed_to_view_managed_password(TALLOC_CTX *mem_ctx,
68 : struct ldb_context *ldb,
69 : const struct ldb_message *msg,
70 : const struct dom_sid *account_sid,
71 : bool *allowed_out)
72 : {
73 42 : TALLOC_CTX *tmp_ctx = NULL;
74 42 : struct security_descriptor group_msa_membership_sd = {};
75 42 : const struct security_token *user_token = NULL;
76 42 : NTSTATUS status = NT_STATUS_OK;
77 42 : int ret = LDB_SUCCESS;
78 :
79 42 : if (allowed_out == NULL) {
80 0 : ret = ldb_operr(ldb);
81 0 : goto out;
82 : }
83 42 : *allowed_out = false;
84 :
85 : {
86 42 : const struct auth_session_info *session_info = ldb_get_opaque(
87 : ldb, DSDB_SESSION_INFO);
88 0 : const enum security_user_level level =
89 42 : security_session_user_level(session_info, NULL);
90 :
91 42 : if (level == SECURITY_SYSTEM) {
92 16 : *allowed_out = true;
93 16 : ret = LDB_SUCCESS;
94 16 : goto out;
95 : }
96 :
97 26 : if (session_info == NULL) {
98 0 : ret = dsdb_werror(ldb,
99 : LDB_ERR_OPERATIONS_ERROR,
100 : WERR_DS_CANT_RETRIEVE_ATTS,
101 : "no right to view attribute");
102 0 : goto out;
103 : }
104 :
105 26 : user_token = session_info->security_token;
106 : }
107 :
108 26 : tmp_ctx = talloc_new(msg);
109 26 : if (tmp_ctx == NULL) {
110 0 : ret = ldb_oom(ldb);
111 0 : goto out;
112 : }
113 :
114 : {
115 26 : const struct ldb_val *group_msa_membership = NULL;
116 0 : enum ndr_err_code err;
117 :
118 : /* [MS-ADTS] 3.1.1.4.4: Extended Access Checks. */
119 26 : group_msa_membership = ldb_msg_find_ldb_val(
120 : msg, "msDS-GroupMSAMembership");
121 26 : if (group_msa_membership == NULL) {
122 0 : ret = dsdb_werror(ldb,
123 : LDB_ERR_OPERATIONS_ERROR,
124 : WERR_DS_CANT_RETRIEVE_ATTS,
125 : "no right to view attribute");
126 0 : goto out;
127 : }
128 :
129 26 : err = ndr_pull_struct_blob_all(
130 : group_msa_membership,
131 : tmp_ctx,
132 : &group_msa_membership_sd,
133 : (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
134 26 : if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
135 0 : status = ndr_map_error2ntstatus(err);
136 0 : DBG_WARNING("msDS-GroupMSAMembership pull failed: %s\n",
137 : nt_errstr(status));
138 0 : ret = ldb_operr(ldb);
139 0 : goto out;
140 : }
141 : }
142 :
143 : {
144 26 : const uint32_t access_desired = SEC_ADS_READ_PROP;
145 26 : uint32_t access_granted = 0;
146 :
147 26 : status = sec_access_check_ds(&group_msa_membership_sd,
148 : user_token,
149 : access_desired,
150 : &access_granted,
151 : NULL,
152 : account_sid);
153 26 : if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
154 : /*
155 : * The principal is not allowed to view the managed
156 : * password.
157 : */
158 19 : } else if (!NT_STATUS_IS_OK(status)) {
159 0 : DBG_WARNING("msDS-GroupMSAMembership: "
160 : "sec_access_check_ds(access_desired=%#08x, "
161 : "access_granted:%#08x) failed with: %s\n",
162 : access_desired,
163 : access_granted,
164 : nt_errstr(status));
165 :
166 0 : ret = dsdb_werror(
167 : ldb,
168 : LDB_ERR_OPERATIONS_ERROR,
169 : WERR_DS_CANT_RETRIEVE_ATTS,
170 : "access check to view managed password failed");
171 0 : goto out;
172 : } else {
173 : /* Cool, the principal may view the password. */
174 19 : *allowed_out = true;
175 : }
176 : }
177 :
178 42 : out:
179 42 : TALLOC_FREE(tmp_ctx);
180 42 : return ret;
181 : }
182 :
183 60 : static NTSTATUS gmsa_managed_pwd_id(struct ldb_context *ldb,
184 : TALLOC_CTX *mem_ctx,
185 : const struct ldb_val *pwd_id_blob,
186 : const struct ProvRootKey *root_key,
187 : struct KeyEnvelope *pwd_id_out)
188 : {
189 60 : if (root_key == NULL) {
190 0 : return NT_STATUS_INVALID_PARAMETER;
191 : }
192 :
193 60 : if (pwd_id_blob != NULL) {
194 8 : return gkdi_pull_KeyEnvelope(mem_ctx, pwd_id_blob, pwd_id_out);
195 : }
196 :
197 : {
198 52 : const char *domain_name = NULL;
199 52 : const char *forest_name = NULL;
200 :
201 52 : domain_name = samdb_default_domain_name(ldb, mem_ctx);
202 52 : if (domain_name == NULL) {
203 0 : return NT_STATUS_NO_MEMORY;
204 : }
205 :
206 52 : forest_name = samdb_forest_name(ldb, mem_ctx);
207 52 : if (forest_name == NULL) {
208 : /* We leak ‘domain_name’, but that can’t be helped. */
209 0 : return NT_STATUS_NO_MEMORY;
210 : }
211 :
212 52 : *pwd_id_out = (struct KeyEnvelope){
213 52 : .version = root_key->version,
214 : .flags = ENVELOPE_FLAG_KEY_MAY_ENCRYPT_NEW_DATA,
215 : .domain_name = domain_name,
216 : .forest_name = forest_name,
217 : };
218 : }
219 :
220 52 : return NT_STATUS_OK;
221 : }
222 :
223 66 : void gmsa_update_managed_pwd_id(struct KeyEnvelope *pwd_id,
224 : const struct gmsa_update_pwd_part *new_pwd)
225 : {
226 66 : pwd_id->l0_index = new_pwd->gkid.l0_idx;
227 66 : pwd_id->l1_index = new_pwd->gkid.l1_idx;
228 66 : pwd_id->l2_index = new_pwd->gkid.l2_idx;
229 66 : pwd_id->root_key_id = new_pwd->root_key->id;
230 66 : }
231 :
232 66 : NTSTATUS gmsa_pack_managed_pwd_id(TALLOC_CTX *mem_ctx,
233 : const struct KeyEnvelope *pwd_id,
234 : DATA_BLOB *pwd_id_out)
235 : {
236 66 : NTSTATUS status = NT_STATUS_OK;
237 0 : enum ndr_err_code err;
238 :
239 66 : err = ndr_push_struct_blob(pwd_id_out,
240 : mem_ctx,
241 : pwd_id,
242 : (ndr_push_flags_fn_t)ndr_push_KeyEnvelope);
243 66 : status = ndr_map_error2ntstatus(err);
244 66 : if (!NT_STATUS_IS_OK(status)) {
245 0 : DBG_WARNING("KeyEnvelope push failed: %s\n", nt_errstr(status));
246 : }
247 :
248 66 : return status;
249 : }
250 :
251 27 : static int gmsa_specific_password(TALLOC_CTX *mem_ctx,
252 : struct ldb_context *ldb,
253 : const struct KeyEnvelopeId pwd_id,
254 : struct gmsa_update_pwd_part *new_pwd_out)
255 : {
256 27 : TALLOC_CTX *tmp_ctx = NULL;
257 27 : NTSTATUS status = NT_STATUS_OK;
258 27 : int ret = LDB_SUCCESS;
259 :
260 27 : tmp_ctx = talloc_new(mem_ctx);
261 27 : if (tmp_ctx == NULL) {
262 0 : ret = ldb_oom(ldb);
263 0 : goto out;
264 : }
265 :
266 : {
267 27 : const struct ldb_message *root_key_msg = NULL;
268 :
269 27 : ret = gkdi_root_key_from_id(tmp_ctx,
270 : ldb,
271 : &pwd_id.root_key_id,
272 : &root_key_msg);
273 27 : if (ret) {
274 0 : goto out;
275 : }
276 :
277 27 : status = gkdi_root_key_from_msg(mem_ctx,
278 : pwd_id.root_key_id,
279 : root_key_msg,
280 : &new_pwd_out->root_key);
281 27 : if (!NT_STATUS_IS_OK(status)) {
282 0 : ret = ldb_operr(ldb);
283 0 : goto out;
284 : }
285 : }
286 :
287 27 : new_pwd_out->gkid = pwd_id.gkid;
288 :
289 27 : out:
290 27 : talloc_free(tmp_ctx);
291 27 : return ret;
292 : }
293 :
294 22 : static int gmsa_nonspecific_password(TALLOC_CTX *mem_ctx,
295 : struct ldb_context *ldb,
296 : const NTTIME key_start_time,
297 : const NTTIME current_time,
298 : struct gmsa_update_pwd_part *new_pwd_out)
299 : {
300 22 : TALLOC_CTX *tmp_ctx = NULL;
301 22 : int ret = LDB_SUCCESS;
302 :
303 22 : tmp_ctx = talloc_new(mem_ctx);
304 22 : if (tmp_ctx == NULL) {
305 0 : ret = ldb_oom(ldb);
306 0 : goto out;
307 : }
308 :
309 : {
310 22 : const struct ldb_message *root_key_msg = NULL;
311 0 : struct GUID root_key_id;
312 22 : NTSTATUS status = NT_STATUS_OK;
313 :
314 22 : ret = gkdi_most_recently_created_root_key(tmp_ctx,
315 : ldb,
316 : current_time,
317 : key_start_time,
318 : &root_key_id,
319 : &root_key_msg);
320 22 : if (ret) {
321 0 : goto out;
322 : }
323 :
324 22 : status = gkdi_root_key_from_msg(mem_ctx,
325 : root_key_id,
326 : root_key_msg,
327 : &new_pwd_out->root_key);
328 22 : if (!NT_STATUS_IS_OK(status)) {
329 0 : ret = ldb_operr(ldb);
330 0 : goto out;
331 : }
332 : }
333 :
334 22 : new_pwd_out->gkid = gkdi_get_interval_id(key_start_time);
335 :
336 22 : out:
337 22 : talloc_free(tmp_ctx);
338 22 : return ret;
339 : }
340 :
341 27 : static int gmsa_specifc_root_key(TALLOC_CTX *mem_ctx,
342 : const struct KeyEnvelopeId pwd_id,
343 : struct RootKey *root_key_out)
344 : {
345 27 : if (root_key_out == NULL) {
346 0 : return LDB_ERR_OPERATIONS_ERROR;
347 : }
348 :
349 27 : *root_key_out = (struct RootKey){.mem_ctx = mem_ctx,
350 : .type = ROOT_KEY_SPECIFIC,
351 : .u.specific = pwd_id};
352 27 : return LDB_SUCCESS;
353 : }
354 :
355 26 : static int gmsa_nonspecifc_root_key(TALLOC_CTX *mem_ctx,
356 : const NTTIME key_start_time,
357 : struct RootKey *root_key_out)
358 : {
359 26 : if (root_key_out == NULL) {
360 0 : return LDB_ERR_OPERATIONS_ERROR;
361 : }
362 :
363 26 : *root_key_out = (struct RootKey){
364 : .mem_ctx = mem_ctx,
365 : .type = ROOT_KEY_NONSPECIFIC,
366 : .u.nonspecific.key_start_time = key_start_time};
367 26 : return LDB_SUCCESS;
368 : }
369 :
370 49 : static int gmsa_obtained_root_key_steal(
371 : TALLOC_CTX *mem_ctx,
372 : const struct gmsa_update_pwd_part key,
373 : struct gmsa_null_terminated_password *password,
374 : struct RootKey *root_key_out)
375 : {
376 49 : if (root_key_out == NULL) {
377 0 : return LDB_ERR_OPERATIONS_ERROR;
378 : }
379 :
380 : /* Steal the data on to the appropriate memory context. */
381 49 : talloc_steal(mem_ctx, key.root_key);
382 49 : talloc_steal(mem_ctx, password);
383 :
384 49 : *root_key_out = (struct RootKey){.mem_ctx = mem_ctx,
385 : .type = ROOT_KEY_OBTAINED,
386 : .u.obtained = {.key = key,
387 : .password = password}};
388 49 : return LDB_SUCCESS;
389 : }
390 :
391 84 : static int gmsa_fetch_root_key(struct ldb_context *ldb,
392 : const NTTIME current_time,
393 : struct RootKey *root_key,
394 : const struct dom_sid *const account_sid)
395 : {
396 84 : TALLOC_CTX *tmp_ctx = NULL;
397 84 : NTSTATUS status = NT_STATUS_OK;
398 84 : int ret = LDB_SUCCESS;
399 :
400 84 : if (root_key == NULL) {
401 0 : ret = ldb_operr(ldb);
402 0 : goto out;
403 : }
404 :
405 84 : switch (root_key->type) {
406 49 : case ROOT_KEY_SPECIFIC:
407 : case ROOT_KEY_NONSPECIFIC: {
408 49 : struct gmsa_null_terminated_password *password = NULL;
409 0 : struct gmsa_update_pwd_part key;
410 :
411 49 : tmp_ctx = talloc_new(NULL);
412 49 : if (tmp_ctx == NULL) {
413 0 : ret = ldb_oom(ldb);
414 0 : goto out;
415 : }
416 :
417 49 : if (root_key->type == ROOT_KEY_SPECIFIC) {
418 : /* Search for a specific root key. */
419 27 : ret = gmsa_specific_password(tmp_ctx,
420 : ldb,
421 : root_key->u.specific,
422 : &key);
423 27 : if (ret) {
424 : /*
425 : * We couldn’t find a specific key — treat this
426 : * as an error.
427 : */
428 0 : goto out;
429 : }
430 : } else {
431 : /*
432 : * Search for the most recent root key meeting the start
433 : * time requirement.
434 : */
435 22 : ret = gmsa_nonspecific_password(
436 : tmp_ctx,
437 : ldb,
438 : root_key->u.nonspecific.key_start_time,
439 : current_time,
440 : &key);
441 : /* Handle errors below. */
442 : }
443 49 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
444 : /*
445 : * We couldn’t find a key meeting the requirements —
446 : * that’s OK, presumably. It’s not critical if we can’t
447 : * find a key for deriving a previous gMSA password, for
448 : * example.
449 : */
450 0 : ret = LDB_SUCCESS;
451 0 : *root_key = empty_root_key;
452 49 : } else if (ret) {
453 0 : goto out;
454 : } else {
455 : /* Derive the password. */
456 49 : status = gmsa_talloc_password_based_on_key_id(
457 : tmp_ctx,
458 : key.gkid,
459 : current_time,
460 : key.root_key,
461 : account_sid,
462 : &password);
463 49 : if (!NT_STATUS_IS_OK(status)) {
464 0 : ret = ldb_operr(ldb);
465 0 : goto out;
466 : }
467 :
468 : /*
469 : * Initialize the obtained structure, and give it the
470 : * appropriate memory context.
471 : */
472 49 : ret = gmsa_obtained_root_key_steal(root_key->mem_ctx,
473 : key,
474 : password,
475 : root_key);
476 49 : if (ret) {
477 0 : goto out;
478 : }
479 : }
480 49 : } break;
481 23 : case ROOT_KEY_NONE:
482 : /* No key is available. */
483 23 : break;
484 12 : case ROOT_KEY_OBTAINED:
485 : /* The key has already been obtained. */
486 12 : break;
487 0 : default:
488 0 : ret = ldb_operr(ldb);
489 0 : goto out;
490 : }
491 :
492 84 : out:
493 84 : TALLOC_FREE(tmp_ctx);
494 84 : return ret;
495 : }
496 :
497 : /*
498 : * Get the password and update information associated with a root key. The
499 : * caller *does not* own these structures; the root key object retains
500 : * ownership.
501 : */
502 84 : static int gmsa_get_root_key(
503 : struct ldb_context *ldb,
504 : const NTTIME current_time,
505 : const struct dom_sid *const account_sid,
506 : struct RootKey *root_key,
507 : struct gmsa_null_terminated_password **password_out,
508 : struct gmsa_update_pwd_part *update_out)
509 : {
510 84 : int ret = LDB_SUCCESS;
511 :
512 84 : if (password_out == NULL) {
513 0 : ret = ldb_operr(ldb);
514 0 : goto out;
515 : }
516 84 : *password_out = NULL;
517 :
518 84 : if (update_out != NULL) {
519 14 : *update_out = (struct gmsa_update_pwd_part){};
520 : }
521 :
522 : /* Fetch the root key from the database and obtain the password. */
523 84 : ret = gmsa_fetch_root_key(ldb, current_time, root_key, account_sid);
524 84 : if (ret) {
525 0 : goto out;
526 : }
527 :
528 84 : switch (root_key->type) {
529 23 : case ROOT_KEY_NONE:
530 : /* No key is available. */
531 23 : break;
532 61 : case ROOT_KEY_OBTAINED:
533 61 : *password_out = root_key->u.obtained.password;
534 61 : if (update_out != NULL) {
535 14 : *update_out = root_key->u.obtained.key;
536 : }
537 61 : break;
538 0 : default:
539 : /* Unexpected. */
540 0 : ret = ldb_operr(ldb);
541 0 : goto out;
542 : }
543 :
544 84 : out:
545 84 : return ret;
546 : }
547 :
548 8 : static int gmsa_system_update_password_id_req(
549 : struct ldb_context *ldb,
550 : TALLOC_CTX *mem_ctx,
551 : const struct ldb_message *msg,
552 : const struct gmsa_update_pwd *new_pwd,
553 : struct ldb_request **req_out)
554 : {
555 8 : TALLOC_CTX *tmp_ctx = NULL;
556 8 : const struct ldb_val *pwd_id_blob = ldb_msg_find_ldb_val(
557 : msg, "msDS-ManagedPasswordId");
558 0 : struct KeyEnvelope pwd_id;
559 8 : struct ldb_message *mod_msg = NULL;
560 8 : NTSTATUS status = NT_STATUS_OK;
561 8 : int ret = LDB_SUCCESS;
562 :
563 8 : tmp_ctx = talloc_new(mem_ctx);
564 8 : if (tmp_ctx == NULL) {
565 0 : ret = ldb_oom(ldb);
566 0 : goto out;
567 : }
568 :
569 : /* Create a new ldb message. */
570 8 : mod_msg = ldb_msg_new(tmp_ctx);
571 8 : if (mod_msg == NULL) {
572 0 : ret = ldb_oom(ldb);
573 0 : goto out;
574 : }
575 : {
576 8 : struct ldb_dn *dn = ldb_dn_copy(mod_msg, msg->dn);
577 8 : if (dn == NULL) {
578 0 : ret = ldb_oom(ldb);
579 0 : goto out;
580 : }
581 8 : mod_msg->dn = dn;
582 : }
583 :
584 : /* Get the Managed Password ID. */
585 8 : status = gmsa_managed_pwd_id(
586 8 : ldb, tmp_ctx, pwd_id_blob, new_pwd->new_id.root_key, &pwd_id);
587 8 : if (!NT_STATUS_IS_OK(status)) {
588 0 : ret = ldb_operr(ldb);
589 0 : goto out;
590 : }
591 :
592 : /* Update the password ID to contain the new GKID and root key ID. */
593 8 : gmsa_update_managed_pwd_id(&pwd_id, &new_pwd->new_id);
594 :
595 : {
596 8 : DATA_BLOB new_pwd_id_blob = {};
597 :
598 : /* Pack the current password ID. */
599 8 : status = gmsa_pack_managed_pwd_id(tmp_ctx,
600 : &pwd_id,
601 : &new_pwd_id_blob);
602 8 : if (!NT_STATUS_IS_OK(status)) {
603 0 : ret = ldb_operr(ldb);
604 0 : goto out;
605 : }
606 :
607 : /* Update the msDS-ManagedPasswordId attribute. */
608 8 : ret = ldb_msg_append_steal_value(mod_msg,
609 : "msDS-ManagedPasswordId",
610 : &new_pwd_id_blob,
611 : LDB_FLAG_MOD_REPLACE);
612 8 : if (ret) {
613 0 : goto out;
614 : }
615 : }
616 :
617 : {
618 8 : DATA_BLOB *prev_pwd_id_blob = NULL;
619 0 : DATA_BLOB _prev_pwd_id_blob;
620 8 : DATA_BLOB prev_pwd_id = {};
621 :
622 8 : if (new_pwd->prev_id.root_key != NULL) {
623 : /*
624 : * Update the password ID to contain the previous GKID
625 : * and root key ID.
626 : */
627 6 : gmsa_update_managed_pwd_id(&pwd_id, &new_pwd->prev_id);
628 :
629 : /* Pack the previous password ID. */
630 6 : status = gmsa_pack_managed_pwd_id(tmp_ctx,
631 : &pwd_id,
632 : &prev_pwd_id);
633 6 : if (!NT_STATUS_IS_OK(status)) {
634 0 : ret = ldb_operr(ldb);
635 0 : goto out;
636 : }
637 :
638 6 : prev_pwd_id_blob = &prev_pwd_id;
639 2 : } else if (pwd_id_blob != NULL) {
640 : /* Copy the current password ID to the previous ID. */
641 2 : _prev_pwd_id_blob = ldb_val_dup(tmp_ctx, pwd_id_blob);
642 2 : if (_prev_pwd_id_blob.length != pwd_id_blob->length) {
643 0 : ret = ldb_oom(ldb);
644 0 : goto out;
645 : }
646 :
647 2 : prev_pwd_id_blob = &_prev_pwd_id_blob;
648 : }
649 :
650 8 : if (prev_pwd_id_blob != NULL) {
651 : /*
652 : * Update the msDS-ManagedPasswordPreviousId attribute.
653 : */
654 8 : ret = ldb_msg_append_steal_value(
655 : mod_msg,
656 : "msDS-ManagedPasswordPreviousId",
657 : prev_pwd_id_blob,
658 : LDB_FLAG_MOD_REPLACE);
659 8 : if (ret) {
660 0 : goto out;
661 : }
662 : }
663 : }
664 :
665 : {
666 8 : struct ldb_request *req = NULL;
667 :
668 : /* Build the ldb request to return. */
669 8 : ret = ldb_build_mod_req(&req,
670 : ldb,
671 : tmp_ctx,
672 : mod_msg,
673 : NULL,
674 : NULL,
675 : ldb_op_default_callback,
676 : NULL);
677 8 : if (ret) {
678 0 : goto out;
679 : }
680 :
681 : /* Tie the lifetime of the message to that of the request. */
682 8 : talloc_steal(req, mod_msg);
683 :
684 : /* Make sure the password ID update happens as System. */
685 8 : ret = dsdb_request_add_controls(req, DSDB_FLAG_AS_SYSTEM);
686 8 : if (ret) {
687 0 : goto out;
688 : }
689 :
690 8 : *req_out = talloc_steal(mem_ctx, req);
691 : }
692 :
693 8 : out:
694 8 : talloc_free(tmp_ctx);
695 8 : return ret;
696 : }
697 :
698 52 : int gmsa_generate_blobs(struct ldb_context *ldb,
699 : TALLOC_CTX *mem_ctx,
700 : const NTTIME current_time,
701 : const struct dom_sid *const account_sid,
702 : DATA_BLOB *pwd_id_blob_out,
703 : struct gmsa_null_terminated_password **password_out)
704 : {
705 52 : TALLOC_CTX *tmp_ctx = NULL;
706 0 : struct KeyEnvelope pwd_id;
707 52 : const struct ProvRootKey *root_key = NULL;
708 52 : NTSTATUS status = NT_STATUS_OK;
709 52 : int ret = LDB_SUCCESS;
710 :
711 52 : tmp_ctx = talloc_new(mem_ctx);
712 52 : if (tmp_ctx == NULL) {
713 0 : ret = ldb_oom(ldb);
714 0 : goto out;
715 : }
716 :
717 : {
718 52 : const struct ldb_message *root_key_msg = NULL;
719 0 : struct GUID root_key_id;
720 52 : const NTTIME one_interval = gkdi_key_cycle_duration +
721 : gkdi_max_clock_skew;
722 52 : const NTTIME one_interval_ago = current_time -
723 52 : MIN(one_interval, current_time);
724 :
725 52 : ret = gkdi_most_recently_created_root_key(tmp_ctx,
726 : ldb,
727 : current_time,
728 : one_interval_ago,
729 : &root_key_id,
730 : &root_key_msg);
731 52 : if (ret) {
732 0 : goto out;
733 : }
734 :
735 52 : status = gkdi_root_key_from_msg(tmp_ctx,
736 : root_key_id,
737 : root_key_msg,
738 : &root_key);
739 52 : if (!NT_STATUS_IS_OK(status)) {
740 0 : ret = ldb_operr(ldb);
741 0 : goto out;
742 : }
743 : }
744 :
745 : /* Get the Managed Password ID. */
746 52 : status = gmsa_managed_pwd_id(ldb, tmp_ctx, NULL, root_key, &pwd_id);
747 52 : if (!NT_STATUS_IS_OK(status)) {
748 0 : ret = ldb_operr(ldb);
749 0 : goto out;
750 : }
751 :
752 : {
753 52 : const struct Gkid current_gkid = gkdi_get_interval_id(
754 : current_time);
755 :
756 : /* Derive the password. */
757 52 : status = gmsa_talloc_password_based_on_key_id(tmp_ctx,
758 : current_gkid,
759 : current_time,
760 : root_key,
761 : account_sid,
762 : password_out);
763 52 : if (!NT_STATUS_IS_OK(status)) {
764 0 : ret = ldb_operr(ldb);
765 0 : goto out;
766 : }
767 :
768 : {
769 52 : const struct gmsa_update_pwd_part new_id = {
770 : .root_key = root_key,
771 : .gkid = current_gkid,
772 : };
773 :
774 : /*
775 : * Update the password ID to contain the new GKID and
776 : * root key ID.
777 : */
778 52 : gmsa_update_managed_pwd_id(&pwd_id, &new_id);
779 : }
780 : }
781 :
782 : /* Pack the current password ID. */
783 52 : status = gmsa_pack_managed_pwd_id(mem_ctx, &pwd_id, pwd_id_blob_out);
784 52 : if (!NT_STATUS_IS_OK(status)) {
785 0 : ret = ldb_operr(ldb);
786 0 : goto out;
787 : }
788 :
789 : /* Transfer ownership of the password to the caller’s memory context. */
790 52 : talloc_steal(mem_ctx, *password_out);
791 :
792 52 : out:
793 52 : talloc_free(tmp_ctx);
794 52 : return ret;
795 : }
796 :
797 8 : static int gmsa_create_update(TALLOC_CTX *mem_ctx,
798 : struct ldb_context *ldb,
799 : const struct ldb_message *msg,
800 : const NTTIME current_time,
801 : const struct dom_sid *account_sid,
802 : const bool current_key_becomes_previous,
803 : struct RootKey *current_key,
804 : struct RootKey *previous_key,
805 : struct gmsa_update **update_out)
806 : {
807 8 : TALLOC_CTX *tmp_ctx = NULL;
808 8 : struct ldb_request *old_pw_req = NULL;
809 8 : struct ldb_request *new_pw_req = NULL;
810 8 : struct ldb_request *pwd_id_req = NULL;
811 8 : struct gmsa_update_pwd new_pwd = {};
812 8 : struct gmsa_update *update = NULL;
813 8 : NTSTATUS status = NT_STATUS_OK;
814 8 : int ret = LDB_SUCCESS;
815 :
816 8 : if (update_out == NULL) {
817 0 : ret = ldb_operr(ldb);
818 0 : goto out;
819 : }
820 8 : *update_out = NULL;
821 :
822 8 : if (current_key == NULL) {
823 0 : ret = ldb_operr(ldb);
824 0 : goto out;
825 : }
826 :
827 8 : tmp_ctx = talloc_new(mem_ctx);
828 8 : if (tmp_ctx == NULL) {
829 0 : ret = ldb_oom(ldb);
830 0 : goto out;
831 : }
832 :
833 : {
834 : /*
835 : * The password_hash module expects these passwords to be
836 : * null‐terminated.
837 : */
838 8 : struct gmsa_null_terminated_password *new_password = NULL;
839 :
840 8 : ret = gmsa_get_root_key(ldb,
841 : current_time,
842 : account_sid,
843 : current_key,
844 : &new_password,
845 : &new_pwd.new_id);
846 8 : if (ret) {
847 0 : goto out;
848 : }
849 :
850 8 : if (new_password == NULL) {
851 0 : ret = ldb_operr(ldb);
852 0 : goto out;
853 : }
854 :
855 8 : status = gmsa_system_password_update_request(
856 8 : ldb, tmp_ctx, msg->dn, new_password->buf, &new_pw_req);
857 8 : if (!NT_STATUS_IS_OK(status)) {
858 0 : ret = ldb_operr(ldb);
859 0 : goto out;
860 : }
861 : }
862 :
863 : /* Does the previous password need to be updated? */
864 8 : if (current_key_becomes_previous) {
865 : /*
866 : * When we perform the password set, the now‐current password
867 : * will become the previous password automatically. We don’t
868 : * have to manage that ourselves.
869 : */
870 : } else {
871 6 : struct gmsa_null_terminated_password *old_password = NULL;
872 :
873 : /* The current key cannot be reused as the previous key. */
874 6 : ret = gmsa_get_root_key(ldb,
875 : current_time,
876 : account_sid,
877 : previous_key,
878 : &old_password,
879 : &new_pwd.prev_id);
880 6 : if (ret) {
881 0 : goto out;
882 : }
883 :
884 6 : if (old_password != NULL) {
885 6 : status = gmsa_system_password_update_request(
886 : ldb,
887 : tmp_ctx,
888 6 : msg->dn,
889 6 : old_password->buf,
890 : &old_pw_req);
891 6 : if (!NT_STATUS_IS_OK(status)) {
892 0 : ret = ldb_operr(ldb);
893 0 : goto out;
894 : }
895 : }
896 : }
897 :
898 : /* Ready the update of the msDS-ManagedPasswordId attribute. */
899 8 : ret = gmsa_system_update_password_id_req(
900 : ldb, tmp_ctx, msg, &new_pwd, &pwd_id_req);
901 8 : if (ret) {
902 0 : goto out;
903 : }
904 :
905 8 : update = talloc(tmp_ctx, struct gmsa_update);
906 8 : if (update == NULL) {
907 0 : ret = ldb_oom(ldb);
908 0 : goto out;
909 : }
910 :
911 8 : *update = (struct gmsa_update){
912 8 : .old_pw_req = talloc_steal(update, old_pw_req),
913 8 : .new_pw_req = talloc_steal(update, new_pw_req),
914 8 : .pwd_id_req = talloc_steal(update, pwd_id_req)};
915 :
916 8 : *update_out = talloc_steal(mem_ctx, update);
917 :
918 8 : out:
919 8 : TALLOC_FREE(tmp_ctx);
920 8 : return ret;
921 : }
922 :
923 35 : NTSTATUS gmsa_pack_managed_pwd(TALLOC_CTX *mem_ctx,
924 : const uint8_t *new_password,
925 : const uint8_t *old_password,
926 : uint64_t query_interval,
927 : uint64_t unchanged_interval,
928 : DATA_BLOB *managed_pwd_out)
929 : {
930 35 : const struct MANAGEDPASSWORD_BLOB managed_pwd = {
931 : .passwords = {.current = new_password,
932 : .previous = old_password,
933 : .query_interval = &query_interval,
934 : .unchanged_interval = &unchanged_interval}};
935 35 : NTSTATUS status = NT_STATUS_OK;
936 0 : enum ndr_err_code err;
937 :
938 35 : err = ndr_push_struct_blob(managed_pwd_out,
939 : mem_ctx,
940 : &managed_pwd,
941 : (ndr_push_flags_fn_t)
942 : ndr_push_MANAGEDPASSWORD_BLOB);
943 35 : status = ndr_map_error2ntstatus(err);
944 35 : if (!NT_STATUS_IS_OK(status)) {
945 0 : DBG_WARNING("MANAGEDPASSWORD_BLOB push failed: %s\n",
946 : nt_errstr(status));
947 : }
948 :
949 35 : return status;
950 : }
951 :
952 35156 : bool dsdb_account_is_gmsa(struct ldb_context *ldb,
953 : const struct ldb_message *msg)
954 : {
955 : /*
956 : * Check if the account has objectClass
957 : * ‘msDS-GroupManagedServiceAccount’.
958 : */
959 35156 : return samdb_find_attribute(ldb,
960 : msg,
961 : "objectclass",
962 35156 : "msDS-GroupManagedServiceAccount") != NULL;
963 : }
964 :
965 : static struct new_key {
966 : NTTIME start_time;
967 : bool immediately_follows_previous;
968 8 : } calculate_new_key(const NTTIME current_time,
969 : const NTTIME current_key_expiration_time,
970 : const NTTIME rollover_interval)
971 : {
972 8 : NTTIME new_key_start_time = current_key_expiration_time;
973 8 : bool immediately_follows_previous = false;
974 :
975 8 : if (new_key_start_time < current_time && rollover_interval) {
976 : /*
977 : * Advance the key start time by the rollover interval until it
978 : * would be greater than the current time.
979 : */
980 6 : const NTTIME time_to_advance_by = current_time + 1 -
981 : new_key_start_time;
982 6 : const uint64_t stale_count = time_to_advance_by /
983 : rollover_interval;
984 6 : new_key_start_time += stale_count * rollover_interval;
985 :
986 6 : SMB_ASSERT(new_key_start_time <= current_time);
987 :
988 6 : immediately_follows_previous = stale_count == 0;
989 : } else {
990 : /*
991 : * It is possible that new_key_start_time ≥ current_time;
992 : * specifically, if there is no password ID, and the creation
993 : * time of the gMSA is in the future (perhaps due to replication
994 : * weirdness).
995 : */
996 0 : }
997 :
998 8 : return (struct new_key){
999 : .start_time = new_key_start_time,
1000 : .immediately_follows_previous = immediately_follows_previous};
1001 : }
1002 :
1003 35 : static bool gmsa_creation_time(const struct ldb_message *msg,
1004 : const NTTIME current_time,
1005 : NTTIME *creation_time_out)
1006 : {
1007 35 : const struct ldb_val *when_created = NULL;
1008 0 : time_t creation_unix_time;
1009 0 : int ret;
1010 :
1011 35 : when_created = ldb_msg_find_ldb_val(msg, "whenCreated");
1012 35 : ret = ldb_val_to_time(when_created, &creation_unix_time);
1013 35 : if (ret) {
1014 : /* Fail if we can’t read the attribute or it isn’t present. */
1015 0 : return false;
1016 : }
1017 :
1018 35 : unix_to_nt_time(creation_time_out, creation_unix_time);
1019 35 : return true;
1020 : }
1021 :
1022 39 : static const struct KeyEnvelopeId *gmsa_get_managed_pwd_id_attr_name(
1023 : const struct ldb_message *msg,
1024 : const char *attr_name,
1025 : struct KeyEnvelopeId *key_env_out)
1026 : {
1027 39 : const struct ldb_val *pwd_id_blob = ldb_msg_find_ldb_val(msg,
1028 : attr_name);
1029 39 : if (pwd_id_blob == NULL) {
1030 4 : return NULL;
1031 : }
1032 :
1033 35 : return gkdi_pull_KeyEnvelopeId(*pwd_id_blob, key_env_out);
1034 : }
1035 :
1036 35 : const struct KeyEnvelopeId *gmsa_get_managed_pwd_id(
1037 : const struct ldb_message *msg,
1038 : struct KeyEnvelopeId *key_env_out)
1039 : {
1040 35 : return gmsa_get_managed_pwd_id_attr_name(msg,
1041 : "msDS-ManagedPasswordId",
1042 : key_env_out);
1043 : }
1044 :
1045 4 : static const struct KeyEnvelopeId *gmsa_get_managed_pwd_prev_id(
1046 : const struct ldb_message *msg,
1047 : struct KeyEnvelopeId *key_env_out)
1048 : {
1049 4 : return gmsa_get_managed_pwd_id_attr_name(
1050 : msg, "msDS-ManagedPasswordPreviousId", key_env_out);
1051 : }
1052 :
1053 35 : static bool samdb_result_gkdi_rollover_interval(const struct ldb_message *msg,
1054 : NTTIME *rollover_interval_out)
1055 : {
1056 0 : int64_t managed_password_interval;
1057 :
1058 35 : managed_password_interval = ldb_msg_find_attr_as_int64(
1059 : msg, "msDS-ManagedPasswordInterval", 30);
1060 35 : return gkdi_rollover_interval(managed_password_interval,
1061 : rollover_interval_out);
1062 : }
1063 :
1064 35 : int gmsa_recalculate_managed_pwd(TALLOC_CTX *mem_ctx,
1065 : struct ldb_context *ldb,
1066 : const struct ldb_message *msg,
1067 : const NTTIME current_time,
1068 : struct gmsa_update **update_out,
1069 : struct gmsa_return_pwd *return_out)
1070 : {
1071 35 : TALLOC_CTX *tmp_ctx = NULL;
1072 35 : int ret = LDB_SUCCESS;
1073 0 : NTTIME rollover_interval;
1074 0 : NTTIME current_key_expiration_time;
1075 0 : NTTIME key_expiration_time;
1076 0 : struct dom_sid account_sid;
1077 0 : struct KeyEnvelopeId pwd_id_buf;
1078 35 : const struct KeyEnvelopeId *pwd_id = NULL;
1079 35 : struct RootKey previous_key = empty_root_key;
1080 35 : struct RootKey current_key = empty_root_key;
1081 35 : struct gmsa_update *update = NULL;
1082 35 : struct gmsa_null_terminated_password *previous_password = NULL;
1083 35 : struct gmsa_null_terminated_password *current_password = NULL;
1084 35 : NTTIME query_interval = 0;
1085 35 : NTTIME unchanged_interval = 0;
1086 35 : NTTIME creation_time = 0;
1087 35 : NTTIME account_age = 0;
1088 35 : NTTIME key_start_time = 0;
1089 35 : bool have_key_start_time = false;
1090 35 : bool ok = true;
1091 35 : bool current_key_is_valid = false;
1092 :
1093 35 : if (update_out == NULL) {
1094 0 : ret = ldb_operr(ldb);
1095 0 : goto out;
1096 : }
1097 35 : *update_out = NULL;
1098 :
1099 : {
1100 : /* Is the account a Group Managed Service Account? */
1101 35 : const bool is_gmsa = dsdb_account_is_gmsa(ldb, msg);
1102 35 : if (!is_gmsa) {
1103 : /* It’s not a GMSA — we’re done here. */
1104 0 : *update_out = NULL;
1105 0 : if (return_out != NULL) {
1106 0 : *return_out = (struct gmsa_return_pwd){};
1107 : }
1108 0 : ret = LDB_SUCCESS;
1109 0 : goto out;
1110 : }
1111 : }
1112 :
1113 : /* Calculate the rollover interval. */
1114 35 : ok = samdb_result_gkdi_rollover_interval(msg, &rollover_interval);
1115 35 : if (!ok || rollover_interval == 0) {
1116 : /* We can’t do anything if the rollover interval is zero. */
1117 0 : ret = ldb_operr(ldb);
1118 0 : goto out;
1119 : }
1120 :
1121 35 : ok = gmsa_creation_time(msg, current_time, &creation_time);
1122 35 : if (!ok) {
1123 0 : return ldb_error(ldb,
1124 : LDB_ERR_OPERATIONS_ERROR,
1125 : "unable to determine creation time of Group "
1126 : "Managed Service Account");
1127 : }
1128 35 : account_age = current_time - MIN(creation_time, current_time);
1129 :
1130 : /* Calculate the expiration time of the current key. */
1131 35 : pwd_id = gmsa_get_managed_pwd_id(msg, &pwd_id_buf);
1132 70 : if (pwd_id != NULL &&
1133 35 : gkdi_get_key_start_time(pwd_id->gkid, &key_start_time))
1134 : {
1135 35 : have_key_start_time = true;
1136 :
1137 : /* Check for overflow. */
1138 35 : if (key_start_time > UINT64_MAX - rollover_interval) {
1139 0 : ret = ldb_operr(ldb);
1140 0 : goto out;
1141 : }
1142 35 : current_key_expiration_time = key_start_time +
1143 : rollover_interval;
1144 : } else {
1145 : /*
1146 : * [MS-ADTS] does not say to use gkdi_get_interval_start_time(),
1147 : * but surely it makes no sense to have keys starting or ending
1148 : * at random times.
1149 : */
1150 0 : current_key_expiration_time = gkdi_get_interval_start_time(
1151 : creation_time);
1152 : }
1153 :
1154 : /* Fetch the account’s SID, necessary for deriving passwords. */
1155 35 : ret = samdb_result_dom_sid_buf(msg, "objectSid", &account_sid);
1156 35 : if (ret) {
1157 0 : goto out;
1158 : }
1159 :
1160 35 : tmp_ctx = talloc_new(mem_ctx);
1161 35 : if (tmp_ctx == NULL) {
1162 0 : ret = ldb_oom(ldb);
1163 0 : goto out;
1164 : }
1165 :
1166 : /*
1167 : * In determining whether the account’s passwords should be updated, we
1168 : * do not validate that the unicodePwd attribute is up‐to‐date, or even
1169 : * that it exists. We rely entirely on the fact that the managed
1170 : * password ID should be updated *only* as part of a successful gMSA
1171 : * password update. In any case, unicodePwd is optional in Samba — save
1172 : * for machine accounts (which gMSAs are :)) — and we can’t always rely
1173 : * on its presence.
1174 : *
1175 : * All this means that an admin (or a DC that doesn’t support gMSAs)
1176 : * could reset a gMSA’s password outside of the normal procedure, and
1177 : * the account would then have the wrong password until the key was due
1178 : * to roll over again. There’s nothing much we can do about this if we
1179 : * don’t want to re‐derive and verify the password every time we look up
1180 : * the keys.
1181 : */
1182 :
1183 35 : current_key_is_valid = pwd_id != NULL &&
1184 0 : current_time < current_key_expiration_time;
1185 35 : if (current_key_is_valid) {
1186 27 : key_expiration_time = current_key_expiration_time;
1187 :
1188 27 : if (return_out != NULL) {
1189 0 : struct KeyEnvelopeId prev_pwd_id_buf;
1190 27 : const struct KeyEnvelopeId *prev_pwd_id = NULL;
1191 :
1192 27 : ret = gmsa_specifc_root_key(tmp_ctx,
1193 : *pwd_id,
1194 : ¤t_key);
1195 27 : if (ret) {
1196 0 : goto out;
1197 : }
1198 :
1199 27 : if (account_age >= rollover_interval) {
1200 4 : prev_pwd_id = gmsa_get_managed_pwd_prev_id(
1201 : msg, &prev_pwd_id_buf);
1202 4 : if (prev_pwd_id != NULL) {
1203 0 : ret = gmsa_specifc_root_key(
1204 : tmp_ctx,
1205 : *prev_pwd_id,
1206 : &previous_key);
1207 0 : if (ret) {
1208 0 : goto out;
1209 : }
1210 4 : } else if (have_key_start_time &&
1211 4 : key_start_time >= rollover_interval)
1212 : {
1213 : /*
1214 : * The account’s old enough to have a
1215 : * previous password, but it doesn’t
1216 : * have a previous password ID for some
1217 : * reason. This can happen in our tests
1218 : * (python/samba/krb5/gmsa_tests.py)
1219 : * when we’re mucking about with times.
1220 : * Just produce what would have been the
1221 : * previous key.
1222 : */
1223 4 : ret = gmsa_nonspecifc_root_key(
1224 : tmp_ctx,
1225 : key_start_time -
1226 : rollover_interval,
1227 : &previous_key);
1228 4 : if (ret) {
1229 0 : goto out;
1230 : }
1231 : }
1232 : } else {
1233 : /*
1234 : * The account is not old enough to have a
1235 : * previous password. The old password will not
1236 : * be returned.
1237 : */
1238 0 : }
1239 : }
1240 : } else {
1241 : /* Calculate the start time of the new key. */
1242 8 : const struct new_key new_key = calculate_new_key(
1243 : current_time,
1244 : current_key_expiration_time,
1245 : rollover_interval);
1246 8 : const bool current_key_becomes_previous =
1247 8 : pwd_id != NULL && new_key.immediately_follows_previous;
1248 :
1249 : /* Check for overflow. */
1250 8 : if (new_key.start_time > UINT64_MAX - rollover_interval) {
1251 0 : ret = ldb_operr(ldb);
1252 0 : goto out;
1253 : }
1254 8 : key_expiration_time = new_key.start_time + rollover_interval;
1255 :
1256 8 : ret = gmsa_nonspecifc_root_key(tmp_ctx,
1257 8 : new_key.start_time,
1258 : ¤t_key);
1259 8 : if (ret) {
1260 0 : goto out;
1261 : }
1262 :
1263 8 : if (account_age >= rollover_interval) {
1264 : /* Check for underflow. */
1265 8 : if (new_key.start_time < rollover_interval) {
1266 0 : ret = ldb_operr(ldb);
1267 0 : goto out;
1268 : }
1269 8 : ret = gmsa_nonspecifc_root_key(
1270 : tmp_ctx,
1271 8 : new_key.start_time - rollover_interval,
1272 : &previous_key);
1273 8 : if (ret) {
1274 0 : goto out;
1275 : }
1276 : } else {
1277 : /*
1278 : * The account is not old enough to have a previous
1279 : * password. The old password will not be returned.
1280 : */
1281 0 : }
1282 :
1283 : /*
1284 : * The current GMSA key, according to the Managed Password ID,
1285 : * is no longer valid. We should update the account’s Managed
1286 : * Password ID and keys in anticipation of their being needed in
1287 : * the near future.
1288 : */
1289 :
1290 8 : ret = gmsa_create_update(tmp_ctx,
1291 : ldb,
1292 : msg,
1293 : current_time,
1294 : &account_sid,
1295 : current_key_becomes_previous,
1296 : ¤t_key,
1297 : &previous_key,
1298 : &update);
1299 8 : if (ret) {
1300 0 : goto out;
1301 : }
1302 : }
1303 :
1304 35 : if (return_out != NULL) {
1305 0 : bool return_future_key;
1306 :
1307 35 : unchanged_interval = query_interval = key_expiration_time -
1308 35 : MIN(current_time,
1309 : key_expiration_time);
1310 :
1311 : /* Derive the current and previous passwords. */
1312 35 : return_future_key = query_interval <= gkdi_max_clock_skew;
1313 35 : if (return_future_key) {
1314 6 : struct RootKey future_key = empty_root_key;
1315 :
1316 : /*
1317 : * The current key hasn’t expired yet, but it
1318 : * soon will. Return a new key that will be valid in the
1319 : * next epoch.
1320 : */
1321 :
1322 6 : ret = gmsa_nonspecifc_root_key(tmp_ctx,
1323 : key_expiration_time,
1324 : &future_key);
1325 6 : if (ret) {
1326 0 : goto out;
1327 : }
1328 :
1329 6 : ret = gmsa_get_root_key(ldb,
1330 : current_time,
1331 : &account_sid,
1332 : &future_key,
1333 : ¤t_password,
1334 : NULL);
1335 6 : if (ret) {
1336 0 : goto out;
1337 : }
1338 :
1339 6 : ret = gmsa_get_root_key(ldb,
1340 : current_time,
1341 : &account_sid,
1342 : ¤t_key,
1343 : &previous_password,
1344 : NULL);
1345 6 : if (ret) {
1346 0 : goto out;
1347 : }
1348 :
1349 : /* Check for overflow. */
1350 6 : if (unchanged_interval > UINT64_MAX - rollover_interval)
1351 : {
1352 0 : ret = ldb_operr(ldb);
1353 0 : goto out;
1354 : }
1355 6 : unchanged_interval += rollover_interval;
1356 : } else {
1357 : /*
1358 : * Note that a gMSA will become unusable (at least until
1359 : * the next rollover) if its associated root key is ever
1360 : * deleted.
1361 : */
1362 :
1363 29 : ret = gmsa_get_root_key(ldb,
1364 : current_time,
1365 : &account_sid,
1366 : ¤t_key,
1367 : ¤t_password,
1368 : NULL);
1369 29 : if (ret) {
1370 0 : goto out;
1371 : }
1372 :
1373 29 : ret = gmsa_get_root_key(ldb,
1374 : current_time,
1375 : &account_sid,
1376 : &previous_key,
1377 : &previous_password,
1378 : NULL);
1379 29 : if (ret) {
1380 0 : goto out;
1381 : }
1382 : }
1383 :
1384 35 : unchanged_interval -= MIN(gkdi_max_clock_skew,
1385 : unchanged_interval);
1386 : }
1387 :
1388 35 : *update_out = talloc_steal(mem_ctx, update);
1389 35 : if (return_out != NULL) {
1390 35 : *return_out = (struct gmsa_return_pwd){
1391 35 : .prev_pwd = talloc_steal(mem_ctx, previous_password),
1392 35 : .new_pwd = talloc_steal(mem_ctx, current_password),
1393 : .query_interval = query_interval,
1394 : .unchanged_interval = unchanged_interval,
1395 : };
1396 : }
1397 :
1398 0 : out:
1399 35 : TALLOC_FREE(tmp_ctx);
1400 35 : return ret;
1401 : }
1402 :
1403 87 : bool dsdb_gmsa_current_time(struct ldb_context *ldb, NTTIME *current_time_out)
1404 : {
1405 87 : const unsigned long long *gmsa_time = talloc_get_type(
1406 : ldb_get_opaque(ldb, DSDB_GMSA_TIME_OPAQUE), unsigned long long);
1407 :
1408 87 : if (gmsa_time != NULL) {
1409 24 : *current_time_out = *gmsa_time;
1410 24 : return true;
1411 : }
1412 :
1413 63 : return gmsa_current_time(current_time_out);
1414 : }
|