s4:dsdb: Add functions for Group Managed Service Accounts implementation
[cs/samba-autobuild/.git] / source4 / dsdb / gmsa / gkdi.c
1 /*
2    Unix SMB/CIFS implementation.
3    Group Key Distribution Protocol functions
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_errors.h>
24 #include <ldb_module.h>
25 #include "lib/crypto/gkdi.h"
26 #include "lib/util/data_blob.h"
27 #include "lib/util/samba_util.h"
28 #include "lib/util/util_str_hex.h"
29 #include "librpc/ndr/libndr.h"
30 #include "dsdb/gmsa/gkdi.h"
31 #include "dsdb/samdb/ldb_modules/util.h"
32 #include "dsdb/samdb/samdb.h"
33 #include "dsdb/common/proto.h"
34 #include "librpc/gen_ndr/gkdi.h"
35 #include "librpc/gen_ndr/ndr_gkdi.h"
36
37 NTSTATUS gkdi_root_key_from_msg(TALLOC_CTX *mem_ctx,
38                                 const struct GUID root_key_id,
39                                 const struct ldb_message *const msg,
40                                 const struct ProvRootKey **const root_key_out)
41 {
42         NTSTATUS status = NT_STATUS_OK;
43         struct ldb_val root_key_data = {};
44         struct KdfAlgorithm kdf_algorithm = {};
45
46         const int version = ldb_msg_find_attr_as_int(msg, "msKds-Version", 0);
47         const NTTIME create_time = samdb_result_nttime(msg,
48                                                        "msKds-CreateTime",
49                                                        0);
50         const NTTIME use_start_time = samdb_result_nttime(msg,
51                                                           "msKds-UseStartTime",
52                                                           0);
53         const char *domain_id = ldb_msg_find_attr_as_string(msg,
54                                                             "msKds-DomainID",
55                                                             NULL);
56
57         {
58                 const struct ldb_val *root_key_val = ldb_msg_find_ldb_val(
59                         msg, "msKds-RootKeyData");
60                 if (root_key_val != NULL) {
61                         root_key_data = *root_key_val;
62                 }
63         }
64
65         {
66                 const char *algorithm_id = ldb_msg_find_attr_as_string(
67                         msg, "msKds-KDFAlgorithmID", NULL);
68                 const struct ldb_val *kdf_param_val = ldb_msg_find_ldb_val(
69                         msg, "msKds-KDFParam");
70                 status = kdf_algorithm_from_params(algorithm_id,
71                                                    kdf_param_val,
72                                                    &kdf_algorithm);
73                 if (!NT_STATUS_IS_OK(status)) {
74                         goto out;
75                 }
76         }
77
78         status = ProvRootKey(mem_ctx,
79                              root_key_id,
80                              version,
81                              root_key_data,
82                              create_time,
83                              use_start_time,
84                              domain_id,
85                              kdf_algorithm,
86                              root_key_out);
87         if (!NT_STATUS_IS_OK(status)) {
88                 goto out;
89         }
90
91 out:
92         return status;
93 }
94
95 /*
96  * Calculate an appropriate useStartTime for a root key created at
97  * ‘current_time’.
98  *
99  * This function goes unused.
100  */
101 NTTIME gkdi_root_key_use_start_time(const NTTIME current_time)
102 {
103         const NTTIME start_time = gkdi_get_interval_start_time(current_time);
104
105         return start_time + gkdi_key_cycle_duration + gkdi_max_clock_skew;
106 }
107
108 static int gkdi_create_root_key(TALLOC_CTX *mem_ctx,
109                                 struct ldb_context *const ldb,
110                                 const NTTIME current_time,
111                                 const NTTIME use_start_time,
112                                 struct GUID *const root_key_id_out,
113                                 struct ldb_dn **const root_key_dn_out)
114 {
115         TALLOC_CTX *tmp_ctx = NULL;
116         struct GUID root_key_id;
117         struct ldb_dn *server_config_dn = NULL;
118         struct ldb_result *server_config_res = NULL;
119         struct ldb_message *server_config_msg = NULL;
120         uint64_t server_config_version;
121         const struct ldb_val *server_config_version_val = NULL;
122         const char *server_config_KDFAlgorithmID = NULL;
123         const struct ldb_val *server_config_KDFParam = NULL;
124         const char *server_config_SecretAgreementAlgorithmID = NULL;
125         const struct ldb_val *server_config_SecretAgreementParam = NULL;
126         uint64_t server_config_PublicKeyLength;
127         uint64_t server_config_PrivateKeyLength;
128         struct KdfAlgorithm kdf_algorithm;
129         DATA_BLOB kdf_parameters_blob = data_blob_null;
130         struct ldb_message *add_msg = NULL;
131         NTSTATUS status = NT_STATUS_OK;
132         int ret = LDB_SUCCESS;
133
134         static const char *server_config_attrs[] = {
135                 "msKds-Version",
136                 "msKds-KDFAlgorithmID",
137                 "msKds-SecretAgreementAlgorithmID",
138                 "msKds-SecretAgreementParam",
139                 "msKds-PublicKeyLength",
140                 "msKds-PrivateKeyLength",
141                 "msKds-KDFParam",
142                 NULL
143         };
144
145         *root_key_dn_out = NULL;
146
147         tmp_ctx = talloc_new(mem_ctx);
148         if (tmp_ctx == NULL) {
149                 ret = ldb_oom(ldb);
150                 goto out;
151         }
152
153         server_config_dn = samdb_configuration_dn(ldb,
154                                                   mem_ctx,
155                                                   "CN=Group Key Distribution Service Server Configuration,"
156                                                   "CN=Server Configuration,"
157                                                   "CN=Group Key Distribution Service,"
158                                                   "CN=Services");
159         if (server_config_dn == NULL) {
160                 ret = ldb_oom(ldb);
161                 goto out;
162         }
163
164         ret = dsdb_search_dn(ldb,
165                              tmp_ctx,
166                              &server_config_res,
167                              server_config_dn,
168                              server_config_attrs,
169                              DSDB_SEARCH_ONE_ONLY);
170
171         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
172                 ldb_asprintf_errstring(ldb, "Unable to create new GKDI root key as we do not have a GKDI server configuration at %s",
173                                   ldb_dn_get_linearized(server_config_dn));
174                 goto out;
175         }
176
177         if (ret != LDB_SUCCESS) {
178                 goto out;
179         }
180
181         server_config_msg = server_config_res->msgs[0];
182
183         server_config_version_val
184                 = ldb_msg_find_ldb_val(server_config_msg,
185                                        "msKds-Version");
186         server_config_version
187                 = ldb_msg_find_attr_as_uint64(server_config_msg,
188                                               "msKds-Version",
189                                               0);
190
191         /* These values we assert on, so we don't create keys we can't use */
192         if (server_config_version_val == NULL) {
193                 /*
194                  * The systemMustContain msKds-Version attribute
195                  * cannot be read, so if absent we just fail with
196                  * permission denied, as that is all that this can
197                  * mean
198                  */
199                 ldb_asprintf_errstring(ldb,
200                                        "Unwilling to create new GKDI root key as "
201                                        "msKds-Version is not readable on %s\n",
202                                        ldb_dn_get_linearized(server_config_dn));
203                 ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
204                 goto out;
205         } else if (server_config_version != 1) {
206                 ldb_asprintf_errstring(ldb,
207                                        "Unwilling to create new GKDI root key as "
208                                        "%s has msKds-Version = %s "
209                                        "and we only support version 1\n",
210                                        ldb_dn_get_linearized(server_config_dn),
211                                        ldb_msg_find_attr_as_string(server_config_msg, "msKds-Version", "(missing)"));
212                 ret = LDB_ERR_CONSTRAINT_VIOLATION;
213                 goto out;
214         }
215
216         server_config_KDFAlgorithmID
217                 = ldb_msg_find_attr_as_string(server_config_msg,
218                                               "msKds-KDFAlgorithmID",
219                                               SP800_108_CTR_HMAC
220                                               );
221
222         server_config_KDFParam
223                 = ldb_msg_find_ldb_val(server_config_msg,
224                                        "msKds-KDFParam");
225         if (server_config_KDFParam == NULL) {
226                 struct KdfParameters kdf_parameters = {
227                         .hash_algorithm = "SHA512"
228                 };
229                 enum ndr_err_code err;
230
231                 err = ndr_push_struct_blob(&kdf_parameters_blob,
232                                            tmp_ctx,
233                                            &kdf_parameters,
234                                            (ndr_push_flags_fn_t)
235                                            ndr_push_KdfParameters);
236
237                 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
238                         status = ndr_map_error2ntstatus(err);
239                         ldb_asprintf_errstring(ldb,
240                                                "KdfParameters pull failed: %s\n",
241                                                nt_errstr(status));
242                         ret = LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
243                         goto out;
244                 }
245
246                 server_config_KDFParam = &kdf_parameters_blob;
247         }
248
249         status = kdf_algorithm_from_params(server_config_KDFAlgorithmID,
250                                            server_config_KDFParam,
251                                            &kdf_algorithm);
252         if (!NT_STATUS_IS_OK(status)) {
253                 ldb_asprintf_errstring(ldb,
254                                        "Unwilling to create new GKDI root key as "
255                                        "%s has an unsupported msKds-KDFAlgorithmID / msKds-KDFParam combination set: %s\n",
256                                        ldb_dn_get_linearized(server_config_dn),
257                                        nt_errstr(status));
258                 ret = LDB_ERR_CONSTRAINT_VIOLATION;
259                 goto out;
260         }
261
262         server_config_SecretAgreementAlgorithmID
263                 = ldb_msg_find_attr_as_string(server_config_msg,
264                                               "msKds-SecretAgreementAlgorithmID",
265                                               "DH");
266
267         /* Optional in msKds-ProvRootKey */
268         server_config_SecretAgreementParam
269                 = ldb_msg_find_ldb_val(server_config_msg,
270                                        "msKds-SecretAgreementParam");
271         if (server_config_SecretAgreementParam == NULL) {
272                 static const uint8_t ffc_dh_parameters[] = {
273                         12,  2,   0,   0,   68,  72,  80,  77,  0,   1,   0,
274                         0,   135, 168, 230, 29,  180, 182, 102, 60,  255, 187,
275                         209, 156, 101, 25,  89,  153, 140, 238, 246, 8,   102,
276                         13,  208, 242, 93,  44,  238, 212, 67,  94,  59,  0,
277                         224, 13,  248, 241, 214, 25,  87,  212, 250, 247, 223,
278                         69,  97,  178, 170, 48,  22,  195, 217, 17,  52,  9,
279                         111, 170, 59,  244, 41,  109, 131, 14,  154, 124, 32,
280                         158, 12,  100, 151, 81,  122, 189, 90,  138, 157, 48,
281                         107, 207, 103, 237, 145, 249, 230, 114, 91,  71,  88,
282                         192, 34,  224, 177, 239, 66,  117, 191, 123, 108, 91,
283                         252, 17,  212, 95,  144, 136, 185, 65,  245, 78,  177,
284                         229, 155, 184, 188, 57,  160, 191, 18,  48,  127, 92,
285                         79,  219, 112, 197, 129, 178, 63,  118, 182, 58,  202,
286                         225, 202, 166, 183, 144, 45,  82,  82,  103, 53,  72,
287                         138, 14,  241, 60,  109, 154, 81,  191, 164, 171, 58,
288                         216, 52,  119, 150, 82,  77,  142, 246, 161, 103, 181,
289                         164, 24,  37,  217, 103, 225, 68,  229, 20,  5,   100,
290                         37,  28,  202, 203, 131, 230, 180, 134, 246, 179, 202,
291                         63,  121, 113, 80,  96,  38,  192, 184, 87,  246, 137,
292                         150, 40,  86,  222, 212, 1,   10,  189, 11,  230, 33,
293                         195, 163, 150, 10,  84,  231, 16,  195, 117, 242, 99,
294                         117, 215, 1,   65,  3,   164, 181, 67,  48,  193, 152,
295                         175, 18,  97,  22,  210, 39,  110, 17,  113, 95,  105,
296                         56,  119, 250, 215, 239, 9,   202, 219, 9,   74,  233,
297                         30,  26,  21,  151, 63,  179, 44,  155, 115, 19,  77,
298                         11,  46,  119, 80,  102, 96,  237, 189, 72,  76,  167,
299                         177, 143, 33,  239, 32,  84,  7,   244, 121, 58,  26,
300                         11,  161, 37,  16,  219, 193, 80,  119, 190, 70,  63,
301                         255, 79,  237, 74,  172, 11,  181, 85,  190, 58,  108,
302                         27,  12,  107, 71,  177, 188, 55,  115, 191, 126, 140,
303                         111, 98,  144, 18,  40,  248, 194, 140, 187, 24,  165,
304                         90,  227, 19,  65,  0,   10,  101, 1,   150, 249, 49,
305                         199, 122, 87,  242, 221, 244, 99,  229, 233, 236, 20,
306                         75,  119, 125, 230, 42,  170, 184, 168, 98,  138, 195,
307                         118, 210, 130, 214, 237, 56,  100, 230, 121, 130, 66,
308                         142, 188, 131, 29,  20,  52,  143, 111, 47,  145, 147,
309                         181, 4,   90,  242, 118, 113, 100, 225, 223, 201, 103,
310                         193, 251, 63,  46,  85,  164, 189, 27,  255, 232, 59,
311                         156, 128, 208, 82,  185, 133, 209, 130, 234, 10,  219,
312                         42,  59,  115, 19,  211, 254, 20,  200, 72,  75,  30,
313                         5,   37,  136, 185, 183, 210, 187, 210, 223, 1,   97,
314                         153, 236, 208, 110, 21,  87,  205, 9,   21,  179, 53,
315                         59,  187, 100, 224, 236, 55,  127, 208, 40,  55,  13,
316                         249, 43,  82,  199, 137, 20,  40,  205, 198, 126, 182,
317                         24,  75,  82,  61,  29,  178, 70,  195, 47,  99,  7,
318                         132, 144, 240, 14,  248, 214, 71,  209, 72,  212, 121,
319                         84,  81,  94,  35,  39,  207, 239, 152, 197, 130, 102,
320                         75,  76,  15,  108, 196, 22,  89};
321                 static const DATA_BLOB ffc_dh_parameters_blob = {
322                         discard_const_p(uint8_t, ffc_dh_parameters),
323                         sizeof ffc_dh_parameters};
324                 server_config_SecretAgreementParam = &ffc_dh_parameters_blob;
325         }
326
327         server_config_PublicKeyLength
328                 = ldb_msg_find_attr_as_uint64(server_config_msg,
329                                               "msKds-PublicKeyLength",
330                                               2048);
331
332         server_config_PrivateKeyLength
333                 = ldb_msg_find_attr_as_uint64(server_config_msg,
334                                               "msKds-PrivateKeyLength",
335                                               256);
336
337         add_msg = ldb_msg_new(tmp_ctx);
338         if (add_msg == NULL) {
339                 ret = ldb_oom(ldb);
340                 goto out;
341         }
342
343         ret = ldb_msg_append_string(add_msg,
344                                     "objectClass",
345                                     "msKds-ProvRootKey",
346                                     LDB_FLAG_MOD_ADD);
347         if (ret) {
348                 goto out;
349         }
350
351         {
352                 uint8_t root_key_data[GKDI_KEY_LEN];
353                 const DATA_BLOB root_key_data_blob = {
354                         .data = root_key_data, .length = sizeof root_key_data};
355
356                 generate_secret_buffer(root_key_data, sizeof root_key_data);
357
358                 ret = ldb_msg_append_value(add_msg,
359                                            "msKds-RootKeyData",
360                                            &root_key_data_blob,
361                                            LDB_FLAG_MOD_ADD);
362                 if (ret) {
363                         goto out;
364                 }
365         }
366
367         ret = samdb_msg_append_uint64(ldb,
368                                       tmp_ctx,
369                                       add_msg,
370                                       "msKds-CreateTime",
371                                       current_time,
372                                       LDB_FLAG_MOD_ADD);
373         if (ret) {
374                 goto out;
375         }
376
377         ret = samdb_msg_append_uint64(ldb,
378                                       tmp_ctx,
379                                       add_msg,
380                                       "msKds-UseStartTime",
381                                       use_start_time,
382                                       LDB_FLAG_MOD_ADD);
383         if (ret) {
384                 goto out;
385         }
386
387         {
388                 struct ldb_dn *domain_dn = NULL;
389
390                 ret = samdb_server_reference_dn(ldb, tmp_ctx, &domain_dn);
391                 if (ret) {
392                         goto out;
393                 }
394
395                 ret = ldb_msg_append_linearized_dn(add_msg,
396                                                    "msKds-DomainID",
397                                                    domain_dn,
398                                                    LDB_FLAG_MOD_ADD);
399                 if (ret) {
400                         goto out;
401                 }
402         }
403
404         ret = samdb_msg_append_uint64(ldb,
405                                       tmp_ctx,
406                                       add_msg,
407                                       "msKds-Version",
408                                       server_config_version,
409                                       LDB_FLAG_MOD_ADD);
410         if (ret) {
411                 goto out;
412         }
413
414         ret = ldb_msg_append_string(add_msg,
415                                     "msKds-KDFAlgorithmID",
416                                     server_config_KDFAlgorithmID,
417                                     LDB_FLAG_MOD_ADD);
418         if (ret) {
419                 goto out;
420         }
421
422         ret = ldb_msg_append_string(add_msg,
423                                    "msKds-SecretAgreementAlgorithmID",
424                                    server_config_SecretAgreementAlgorithmID,
425                                    LDB_FLAG_MOD_ADD);
426         if (ret) {
427                 goto out;
428         }
429
430         if (server_config_SecretAgreementParam != NULL) {
431                 ret = ldb_msg_append_value(add_msg,
432                                            "msKds-SecretAgreementParam",
433                                            server_config_SecretAgreementParam,
434                                            LDB_FLAG_MOD_ADD);
435                 if (ret) {
436                         goto out;
437                 }
438         }
439
440         ret = samdb_msg_append_uint64(ldb,
441                                       tmp_ctx,
442                                       add_msg,
443                                       "msKds-PublicKeyLength",
444                                       server_config_PublicKeyLength,
445                                       LDB_FLAG_MOD_ADD);
446         if (ret) {
447                 goto out;
448         }
449
450         ret = samdb_msg_append_uint64(ldb,
451                                       tmp_ctx,
452                                       add_msg,
453                                       "msKds-PrivateKeyLength",
454                                       server_config_PrivateKeyLength,
455                                       LDB_FLAG_MOD_ADD);
456
457         ret = ldb_msg_append_value(add_msg,
458                                    "msKds-KDFParam",
459                                    server_config_KDFParam,
460                                    LDB_FLAG_MOD_ADD);
461         if (ret) {
462                 goto out;
463         }
464
465         {
466                 uint8_t guid_buf[sizeof((struct GUID_ndr_buf){}.buf)];
467                 const DATA_BLOB guid_blob = {.data = guid_buf,
468                                              .length = sizeof guid_buf};
469
470                 generate_secret_buffer(guid_buf, sizeof guid_buf);
471
472                 status = GUID_from_ndr_blob(&guid_blob, &root_key_id);
473                 if (!NT_STATUS_IS_OK(status)) {
474                         ret = ldb_operr(ldb);
475                         goto out;
476                 }
477         }
478
479         {
480                 struct ldb_dn *root_key_dn = NULL;
481
482                 root_key_dn = samdb_gkdi_root_key_dn(ldb,
483                                                      tmp_ctx,
484                                                      &root_key_id);
485                 if (root_key_dn == NULL) {
486                         ret = ldb_operr(ldb);
487                         goto out;
488                 }
489
490                 add_msg->dn = root_key_dn;
491         }
492
493         ret = dsdb_add(ldb, add_msg, 0);
494         if (ret) {
495                 goto out;
496         }
497
498         *root_key_id_out = root_key_id;
499         *root_key_dn_out = talloc_steal(mem_ctx, add_msg->dn);
500
501 out:
502         talloc_free(tmp_ctx);
503         return ret;
504 }
505
506 /*
507  * The PrivateKey, PublicKey, and SecretAgreement attributes are related to the
508  * public‐key functionality in GKDI. Samba doesn’t try to implement any of that,
509  * so we don’t bother looking at these attributes.
510  */
511 static const char *const root_key_attrs[] = {
512         "msKds-CreateTime",
513         "msKds-DomainID",
514         "msKds-KDFAlgorithmID",
515         "msKds-KDFParam",
516         /* "msKds-PrivateKeyLength", */
517         /* "msKds-PublicKeyLength", */
518         "msKds-RootKeyData",
519         /* "msKds-SecretAgreementAlgorithmID", */
520         /* "msKds-SecretAgreementParam", */
521         "msKds-UseStartTime",
522         "msKds-Version",
523         NULL,
524 };
525
526 /*
527  * Create and return a new GKDI root key.
528  *
529  * This function goes unused.
530  */
531 int gkdi_new_root_key(TALLOC_CTX *mem_ctx,
532                       struct ldb_context *const ldb,
533                       const NTTIME current_time,
534                       const NTTIME use_start_time,
535                       struct GUID *const root_key_id_out,
536                       const struct ldb_message **const root_key_out)
537 {
538         TALLOC_CTX *tmp_ctx = NULL;
539         struct ldb_dn *root_key_dn = NULL;
540         struct ldb_result *res = NULL;
541         int ret = LDB_SUCCESS;
542
543         *root_key_out = NULL;
544
545         tmp_ctx = talloc_new(mem_ctx);
546         if (tmp_ctx == NULL) {
547                 ret = ldb_oom(ldb);
548                 goto out;
549         }
550
551         ret = gkdi_create_root_key(tmp_ctx,
552                                    ldb,
553                                    current_time,
554                                    use_start_time,
555                                    root_key_id_out,
556                                    &root_key_dn);
557         if (ret) {
558                 goto out;
559         }
560
561         ret = dsdb_search_dn(
562                 ldb, tmp_ctx, &res, root_key_dn, root_key_attrs, 0);
563         if (ret) {
564                 goto out;
565         }
566
567         if (res->count != 1) {
568                 ret = LDB_ERR_NO_SUCH_OBJECT;
569                 goto out;
570         }
571
572         *root_key_out = talloc_steal(mem_ctx, res->msgs[0]);
573
574 out:
575         talloc_free(tmp_ctx);
576         return ret;
577 }
578
579 int gkdi_root_key_from_id(TALLOC_CTX *mem_ctx,
580                           struct ldb_context *const ldb,
581                           const struct GUID *const root_key_id,
582                           const struct ldb_message **const root_key_out)
583 {
584         TALLOC_CTX *tmp_ctx = NULL;
585         struct ldb_dn *root_key_dn = NULL;
586         struct ldb_result *res = NULL;
587         int ret = LDB_SUCCESS;
588
589         *root_key_out = NULL;
590
591         tmp_ctx = talloc_new(mem_ctx);
592         if (tmp_ctx == NULL) {
593                 ret = ldb_oom(ldb);
594                 goto out;
595         }
596
597         root_key_dn = samdb_gkdi_root_key_dn(ldb, tmp_ctx, root_key_id);
598         if (root_key_dn == NULL) {
599                 ret = ldb_operr(ldb);
600                 goto out;
601         }
602
603         ret = dsdb_search_dn(
604                 ldb, tmp_ctx, &res, root_key_dn, root_key_attrs, 0);
605         if (ret) {
606                 goto out;
607         }
608
609         if (res->count != 1) {
610                 ret = dsdb_werror(ldb,
611                                   LDB_ERR_NO_SUCH_OBJECT,
612                                   W_ERROR(HRES_ERROR_V(HRES_NTE_NO_KEY)),
613                                   "failed to find root key");
614                 goto out;
615         }
616
617         *root_key_out = talloc_steal(mem_ctx, res->msgs[0]);
618
619 out:
620         talloc_free(tmp_ctx);
621         return ret;
622 }
623
624 int gkdi_most_recently_created_root_key(
625         TALLOC_CTX *mem_ctx,
626         struct ldb_context *const ldb,
627         _UNUSED_ const NTTIME current_time,
628         const NTTIME not_after,
629         struct GUID *const root_key_id_out,
630         const struct ldb_message **const root_key_out)
631 {
632         TALLOC_CTX *tmp_ctx = NULL;
633         struct ldb_result *res = NULL;
634         int ret = LDB_SUCCESS;
635
636         *root_key_out = NULL;
637
638         tmp_ctx = talloc_new(mem_ctx);
639         if (tmp_ctx == NULL) {
640                 ret = ldb_oom(ldb);
641                 goto out;
642         }
643
644         {
645                 struct ldb_dn *root_key_container_dn = NULL;
646
647                 root_key_container_dn = samdb_gkdi_root_key_container_dn(
648                         ldb, tmp_ctx);
649                 if (root_key_container_dn == NULL) {
650                         ret = ldb_operr(ldb);
651                         goto out;
652                 }
653
654                 ret = dsdb_search(ldb,
655                                   tmp_ctx,
656                                   &res,
657                                   root_key_container_dn,
658                                   LDB_SCOPE_ONELEVEL,
659                                   root_key_attrs,
660                                   0,
661                                   "(msKds-UseStartTime<=%" PRIu64 ")",
662                                   not_after);
663                 if (ret) {
664                         goto out;
665                 }
666         }
667
668         /*
669          * Windows just gives up if there are more than 1000 root keys in the
670          * container.
671          */
672
673         {
674                 struct root_key_candidate {
675                         struct GUID id;
676                         const struct ldb_message *key;
677                         NTTIME create_time;
678                 } most_recent_key = {
679                         .key = NULL,
680                 };
681                 unsigned i;
682
683                 for (i = 0; i < res->count; ++i) {
684                         struct root_key_candidate key = {
685                                 .key = res->msgs[i],
686                         };
687                         const struct ldb_val *rdn_val = NULL;
688                         bool ok;
689
690                         key.create_time = samdb_result_nttime(
691                                 key.key, "msKds-CreateTime", 0);
692                         if (key.create_time < most_recent_key.create_time) {
693                                 /* We already have a more recent key. */
694                                 continue;
695                         }
696
697                         rdn_val = ldb_dn_get_rdn_val(key.key->dn);
698                         if (rdn_val == NULL) {
699                                 continue;
700                         }
701
702                         if (rdn_val->length != 36) {
703                                 /*
704                                  * Check the RDN is the right length — 36 is the
705                                  * length of a UUID.
706                                  */
707                                 continue;
708                         }
709
710                         ok = parse_guid_string((const char *)rdn_val->data,
711                                                &key.id);
712                         if (!ok) {
713                                 /* The RDN is not a correctly formatted GUID. */
714                                 continue;
715                         }
716
717                         /*
718                          * We’ve found a new candidate for the most recent root
719                          * key.
720                          */
721                         most_recent_key = key;
722                 }
723
724                 if (most_recent_key.key == NULL) {
725                         /*
726                          * We were not able to find a suitable root key, but
727                          * there is a possibility that a key we create now will
728                          * do: if gkdi_root_key_use_start_time(current_time) ≤
729                          * not_after, then a newly‐created key will satisfy our
730                          * caller’s requirements.
731                          *
732                          * Unfortunately, with gMSAs this (I believe) will never
733                          * be the case. It’s too late to call
734                          * gkdi_new_root_key() — the new key will be a bit *too*
735                          * new to be usable for a gMSA.
736                          */
737
738                         ret = dsdb_werror(ldb,
739                                           LDB_ERR_NO_SUCH_OBJECT,
740                                           W_ERROR(HRES_ERROR_V(
741                                                   HRES_NTE_NO_KEY)),
742                                           "failed to find a suitable root key");
743                         goto out;
744                 }
745
746                 /* Return the root key that we found. */
747                 *root_key_id_out = most_recent_key.id;
748                 *root_key_out = talloc_steal(mem_ctx, most_recent_key.key);
749         }
750
751 out:
752         talloc_free(tmp_ctx);
753         return ret;
754 }