Line data Source code
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 101 : 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 101 : NTSTATUS status = NT_STATUS_OK;
43 101 : struct ldb_val root_key_data = {};
44 101 : struct KdfAlgorithm kdf_algorithm = {};
45 :
46 101 : const int version = ldb_msg_find_attr_as_int(msg, "msKds-Version", 0);
47 101 : const NTTIME create_time = samdb_result_nttime(msg,
48 : "msKds-CreateTime",
49 : 0);
50 101 : const NTTIME use_start_time = samdb_result_nttime(msg,
51 : "msKds-UseStartTime",
52 : 0);
53 101 : const char *domain_id = ldb_msg_find_attr_as_string(msg,
54 : "msKds-DomainID",
55 : NULL);
56 :
57 : {
58 101 : const struct ldb_val *root_key_val = ldb_msg_find_ldb_val(
59 : msg, "msKds-RootKeyData");
60 101 : if (root_key_val != NULL) {
61 101 : root_key_data = *root_key_val;
62 : }
63 : }
64 :
65 : {
66 101 : const char *algorithm_id = ldb_msg_find_attr_as_string(
67 : msg, "msKds-KDFAlgorithmID", NULL);
68 101 : const struct ldb_val *kdf_param_val = ldb_msg_find_ldb_val(
69 : msg, "msKds-KDFParam");
70 101 : status = kdf_algorithm_from_params(algorithm_id,
71 : kdf_param_val,
72 : &kdf_algorithm);
73 101 : if (!NT_STATUS_IS_OK(status)) {
74 0 : goto out;
75 : }
76 : }
77 :
78 101 : 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 101 : if (!NT_STATUS_IS_OK(status)) {
88 0 : goto out;
89 : }
90 :
91 101 : out:
92 101 : 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 0 : NTTIME gkdi_root_key_use_start_time(const NTTIME current_time)
102 : {
103 0 : const NTTIME start_time = gkdi_get_interval_start_time(current_time);
104 :
105 0 : return start_time + gkdi_key_cycle_duration + gkdi_max_clock_skew;
106 : }
107 :
108 100 : 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 100 : TALLOC_CTX *tmp_ctx = NULL;
116 22 : struct GUID root_key_id;
117 100 : struct ldb_dn *server_config_dn = NULL;
118 100 : struct ldb_result *server_config_res = NULL;
119 100 : struct ldb_message *server_config_msg = NULL;
120 22 : uint64_t server_config_version;
121 100 : const struct ldb_val *server_config_version_val = NULL;
122 100 : const char *server_config_KDFAlgorithmID = NULL;
123 100 : const struct ldb_val *server_config_KDFParam = NULL;
124 100 : const char *server_config_SecretAgreementAlgorithmID = NULL;
125 100 : const struct ldb_val *server_config_SecretAgreementParam = NULL;
126 22 : uint64_t server_config_PublicKeyLength;
127 22 : uint64_t server_config_PrivateKeyLength;
128 22 : struct KdfAlgorithm kdf_algorithm;
129 100 : DATA_BLOB kdf_parameters_blob = data_blob_null;
130 100 : struct ldb_message *add_msg = NULL;
131 100 : NTSTATUS status = NT_STATUS_OK;
132 100 : int ret = LDB_SUCCESS;
133 :
134 22 : 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 100 : *root_key_dn_out = NULL;
146 :
147 100 : tmp_ctx = talloc_new(mem_ctx);
148 100 : if (tmp_ctx == NULL) {
149 0 : ret = ldb_oom(ldb);
150 0 : goto out;
151 : }
152 :
153 100 : 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 100 : if (server_config_dn == NULL) {
160 0 : ret = ldb_oom(ldb);
161 0 : goto out;
162 : }
163 :
164 100 : 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 100 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
172 0 : 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 0 : goto out;
175 : }
176 :
177 100 : if (ret != LDB_SUCCESS) {
178 0 : goto out;
179 : }
180 :
181 100 : server_config_msg = server_config_res->msgs[0];
182 :
183 22 : server_config_version_val
184 100 : = ldb_msg_find_ldb_val(server_config_msg,
185 : "msKds-Version");
186 22 : server_config_version
187 100 : = 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 100 : 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 1 : 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 1 : ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
204 1 : goto out;
205 99 : } else if (server_config_version != 1) {
206 1 : 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 1 : ret = LDB_ERR_CONSTRAINT_VIOLATION;
213 1 : goto out;
214 : }
215 :
216 22 : server_config_KDFAlgorithmID
217 98 : = ldb_msg_find_attr_as_string(server_config_msg,
218 : "msKds-KDFAlgorithmID",
219 : SP800_108_CTR_HMAC
220 : );
221 :
222 22 : server_config_KDFParam
223 98 : = ldb_msg_find_ldb_val(server_config_msg,
224 : "msKds-KDFParam");
225 98 : if (server_config_KDFParam == NULL) {
226 98 : struct KdfParameters kdf_parameters = {
227 : .hash_algorithm = "SHA512"
228 : };
229 22 : enum ndr_err_code err;
230 :
231 98 : 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 98 : if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
238 0 : status = ndr_map_error2ntstatus(err);
239 0 : ldb_asprintf_errstring(ldb,
240 : "KdfParameters pull failed: %s\n",
241 : nt_errstr(status));
242 0 : ret = LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
243 0 : goto out;
244 : }
245 :
246 98 : server_config_KDFParam = &kdf_parameters_blob;
247 : }
248 :
249 98 : status = kdf_algorithm_from_params(server_config_KDFAlgorithmID,
250 : server_config_KDFParam,
251 : &kdf_algorithm);
252 98 : if (!NT_STATUS_IS_OK(status)) {
253 1 : 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 1 : ret = LDB_ERR_CONSTRAINT_VIOLATION;
259 1 : goto out;
260 : }
261 :
262 22 : server_config_SecretAgreementAlgorithmID
263 97 : = ldb_msg_find_attr_as_string(server_config_msg,
264 : "msKds-SecretAgreementAlgorithmID",
265 : "DH");
266 :
267 : /* Optional in msKds-ProvRootKey */
268 22 : server_config_SecretAgreementParam
269 97 : = ldb_msg_find_ldb_val(server_config_msg,
270 : "msKds-SecretAgreementParam");
271 97 : if (server_config_SecretAgreementParam == NULL) {
272 22 : 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 22 : static const DATA_BLOB ffc_dh_parameters_blob = {
322 : discard_const_p(uint8_t, ffc_dh_parameters),
323 : sizeof ffc_dh_parameters};
324 97 : server_config_SecretAgreementParam = &ffc_dh_parameters_blob;
325 : }
326 :
327 22 : server_config_PublicKeyLength
328 97 : = ldb_msg_find_attr_as_uint64(server_config_msg,
329 : "msKds-PublicKeyLength",
330 : 2048);
331 :
332 22 : server_config_PrivateKeyLength
333 97 : = ldb_msg_find_attr_as_uint64(server_config_msg,
334 : "msKds-PrivateKeyLength",
335 : 256);
336 :
337 97 : add_msg = ldb_msg_new(tmp_ctx);
338 97 : if (add_msg == NULL) {
339 0 : ret = ldb_oom(ldb);
340 0 : goto out;
341 : }
342 :
343 97 : ret = ldb_msg_append_string(add_msg,
344 : "objectClass",
345 : "msKds-ProvRootKey",
346 : LDB_FLAG_MOD_ADD);
347 97 : if (ret) {
348 0 : goto out;
349 : }
350 :
351 : {
352 22 : uint8_t root_key_data[GKDI_KEY_LEN];
353 97 : const DATA_BLOB root_key_data_blob = {
354 : .data = root_key_data, .length = sizeof root_key_data};
355 :
356 97 : generate_secret_buffer(root_key_data, sizeof root_key_data);
357 :
358 97 : ret = ldb_msg_append_value(add_msg,
359 : "msKds-RootKeyData",
360 : &root_key_data_blob,
361 : LDB_FLAG_MOD_ADD);
362 97 : if (ret) {
363 0 : goto out;
364 : }
365 : }
366 :
367 97 : ret = samdb_msg_append_uint64(ldb,
368 : tmp_ctx,
369 : add_msg,
370 : "msKds-CreateTime",
371 : current_time,
372 : LDB_FLAG_MOD_ADD);
373 97 : if (ret) {
374 0 : goto out;
375 : }
376 :
377 97 : 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 97 : if (ret) {
384 0 : goto out;
385 : }
386 :
387 : {
388 97 : struct ldb_dn *domain_dn = NULL;
389 :
390 97 : ret = samdb_server_reference_dn(ldb, tmp_ctx, &domain_dn);
391 97 : if (ret) {
392 0 : goto out;
393 : }
394 :
395 97 : ret = ldb_msg_append_linearized_dn(add_msg,
396 : "msKds-DomainID",
397 : domain_dn,
398 : LDB_FLAG_MOD_ADD);
399 97 : if (ret) {
400 0 : goto out;
401 : }
402 : }
403 :
404 97 : 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 97 : if (ret) {
411 0 : goto out;
412 : }
413 :
414 97 : ret = ldb_msg_append_string(add_msg,
415 : "msKds-KDFAlgorithmID",
416 : server_config_KDFAlgorithmID,
417 : LDB_FLAG_MOD_ADD);
418 97 : if (ret) {
419 0 : goto out;
420 : }
421 :
422 97 : ret = ldb_msg_append_string(add_msg,
423 : "msKds-SecretAgreementAlgorithmID",
424 : server_config_SecretAgreementAlgorithmID,
425 : LDB_FLAG_MOD_ADD);
426 97 : if (ret) {
427 0 : goto out;
428 : }
429 :
430 97 : if (server_config_SecretAgreementParam != NULL) {
431 97 : ret = ldb_msg_append_value(add_msg,
432 : "msKds-SecretAgreementParam",
433 : server_config_SecretAgreementParam,
434 : LDB_FLAG_MOD_ADD);
435 97 : if (ret) {
436 0 : goto out;
437 : }
438 : }
439 :
440 97 : 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 97 : if (ret) {
447 0 : goto out;
448 : }
449 :
450 97 : 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 97 : ret = ldb_msg_append_value(add_msg,
458 : "msKds-KDFParam",
459 : server_config_KDFParam,
460 : LDB_FLAG_MOD_ADD);
461 97 : if (ret) {
462 0 : goto out;
463 : }
464 :
465 : {
466 22 : uint8_t guid_buf[sizeof((struct GUID_ndr_buf){}.buf)];
467 97 : const DATA_BLOB guid_blob = {.data = guid_buf,
468 : .length = sizeof guid_buf};
469 :
470 97 : generate_secret_buffer(guid_buf, sizeof guid_buf);
471 :
472 97 : status = GUID_from_ndr_blob(&guid_blob, &root_key_id);
473 97 : if (!NT_STATUS_IS_OK(status)) {
474 0 : ret = ldb_operr(ldb);
475 0 : goto out;
476 : }
477 : }
478 :
479 : {
480 97 : struct ldb_dn *root_key_dn = NULL;
481 :
482 97 : root_key_dn = samdb_gkdi_root_key_dn(ldb,
483 : tmp_ctx,
484 : &root_key_id);
485 97 : if (root_key_dn == NULL) {
486 0 : ret = ldb_operr(ldb);
487 0 : goto out;
488 : }
489 :
490 97 : add_msg->dn = root_key_dn;
491 : }
492 :
493 97 : ret = dsdb_add(ldb, add_msg, 0);
494 97 : if (ret) {
495 0 : goto out;
496 : }
497 :
498 97 : *root_key_id_out = root_key_id;
499 97 : *root_key_dn_out = talloc_steal(mem_ctx, add_msg->dn);
500 :
501 100 : out:
502 100 : talloc_free(tmp_ctx);
503 100 : 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 100 : 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 100 : TALLOC_CTX *tmp_ctx = NULL;
539 100 : struct ldb_dn *root_key_dn = NULL;
540 100 : struct ldb_result *res = NULL;
541 100 : int ret = LDB_SUCCESS;
542 :
543 100 : *root_key_out = NULL;
544 :
545 100 : tmp_ctx = talloc_new(mem_ctx);
546 100 : if (tmp_ctx == NULL) {
547 0 : ret = ldb_oom(ldb);
548 0 : goto out;
549 : }
550 :
551 100 : 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 100 : if (ret) {
558 3 : goto out;
559 : }
560 :
561 97 : ret = dsdb_search_dn(
562 : ldb, tmp_ctx, &res, root_key_dn, root_key_attrs, 0);
563 97 : if (ret) {
564 0 : goto out;
565 : }
566 :
567 97 : if (res->count != 1) {
568 0 : ret = LDB_ERR_NO_SUCH_OBJECT;
569 0 : goto out;
570 : }
571 :
572 97 : *root_key_out = talloc_steal(mem_ctx, res->msgs[0]);
573 :
574 100 : out:
575 100 : talloc_free(tmp_ctx);
576 100 : return ret;
577 : }
578 :
579 27 : 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 27 : TALLOC_CTX *tmp_ctx = NULL;
585 27 : struct ldb_dn *root_key_dn = NULL;
586 27 : struct ldb_result *res = NULL;
587 27 : int ret = LDB_SUCCESS;
588 :
589 27 : *root_key_out = NULL;
590 :
591 27 : tmp_ctx = talloc_new(mem_ctx);
592 27 : if (tmp_ctx == NULL) {
593 0 : ret = ldb_oom(ldb);
594 0 : goto out;
595 : }
596 :
597 27 : root_key_dn = samdb_gkdi_root_key_dn(ldb, tmp_ctx, root_key_id);
598 27 : if (root_key_dn == NULL) {
599 0 : ret = ldb_operr(ldb);
600 0 : goto out;
601 : }
602 :
603 27 : ret = dsdb_search_dn(
604 : ldb, tmp_ctx, &res, root_key_dn, root_key_attrs, 0);
605 27 : if (ret) {
606 0 : goto out;
607 : }
608 :
609 27 : if (res->count != 1) {
610 0 : 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 0 : goto out;
615 : }
616 :
617 27 : *root_key_out = talloc_steal(mem_ctx, res->msgs[0]);
618 :
619 27 : out:
620 27 : talloc_free(tmp_ctx);
621 27 : return ret;
622 : }
623 :
624 74 : 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 74 : TALLOC_CTX *tmp_ctx = NULL;
633 74 : struct ldb_result *res = NULL;
634 74 : int ret = LDB_SUCCESS;
635 :
636 74 : *root_key_out = NULL;
637 :
638 74 : tmp_ctx = talloc_new(mem_ctx);
639 74 : if (tmp_ctx == NULL) {
640 0 : ret = ldb_oom(ldb);
641 0 : goto out;
642 : }
643 :
644 : {
645 74 : struct ldb_dn *root_key_container_dn = NULL;
646 :
647 74 : root_key_container_dn = samdb_gkdi_root_key_container_dn(
648 : ldb, tmp_ctx);
649 74 : if (root_key_container_dn == NULL) {
650 0 : ret = ldb_operr(ldb);
651 0 : goto out;
652 : }
653 :
654 74 : 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 74 : if (ret) {
664 0 : 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 0 : struct root_key_candidate {
675 : struct GUID id;
676 : const struct ldb_message *key;
677 : NTTIME create_time;
678 74 : } most_recent_key = {
679 : .key = NULL,
680 : };
681 0 : unsigned i;
682 :
683 680 : for (i = 0; i < res->count; ++i) {
684 606 : struct root_key_candidate key = {
685 606 : .key = res->msgs[i],
686 : };
687 606 : const struct ldb_val *rdn_val = NULL;
688 0 : bool ok;
689 :
690 606 : key.create_time = samdb_result_nttime(
691 : key.key, "msKds-CreateTime", 0);
692 606 : if (key.create_time < most_recent_key.create_time) {
693 : /* We already have a more recent key. */
694 368 : continue;
695 : }
696 :
697 238 : rdn_val = ldb_dn_get_rdn_val(key.key->dn);
698 238 : if (rdn_val == NULL) {
699 0 : continue;
700 : }
701 :
702 238 : if (rdn_val->length != 36) {
703 : /*
704 : * Check the RDN is the right length — 36 is the
705 : * length of a UUID.
706 : */
707 0 : continue;
708 : }
709 :
710 238 : ok = parse_guid_string((const char *)rdn_val->data,
711 : &key.id);
712 238 : if (!ok) {
713 : /* The RDN is not a correctly formatted GUID. */
714 0 : continue;
715 : }
716 :
717 : /*
718 : * We’ve found a new candidate for the most recent root
719 : * key.
720 : */
721 238 : most_recent_key = key;
722 : }
723 :
724 74 : 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 0 : 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 0 : goto out;
744 : }
745 :
746 : /* Return the root key that we found. */
747 74 : *root_key_id_out = most_recent_key.id;
748 74 : *root_key_out = talloc_steal(mem_ctx, most_recent_key.key);
749 : }
750 :
751 74 : out:
752 74 : talloc_free(tmp_ctx);
753 74 : return ret;
754 : }
|