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 190 : 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 190 : NTSTATUS status = NT_STATUS_OK;
43 190 : struct ldb_val root_key_data = {};
44 190 : struct KdfAlgorithm kdf_algorithm = {};
45 :
46 190 : const int version = ldb_msg_find_attr_as_int(msg, "msKds-Version", 0);
47 190 : const NTTIME create_time = samdb_result_nttime(msg,
48 : "msKds-CreateTime",
49 : 0);
50 190 : const NTTIME use_start_time = samdb_result_nttime(msg,
51 : "msKds-UseStartTime",
52 : 0);
53 190 : const char *domain_id = ldb_msg_find_attr_as_string(msg,
54 : "msKds-DomainID",
55 : NULL);
56 :
57 : {
58 190 : const struct ldb_val *root_key_val = ldb_msg_find_ldb_val(
59 : msg, "msKds-RootKeyData");
60 190 : if (root_key_val != NULL) {
61 190 : root_key_data = *root_key_val;
62 : }
63 : }
64 :
65 : {
66 190 : const char *algorithm_id = ldb_msg_find_attr_as_string(
67 : msg, "msKds-KDFAlgorithmID", NULL);
68 190 : const struct ldb_val *kdf_param_val = ldb_msg_find_ldb_val(
69 : msg, "msKds-KDFParam");
70 190 : status = kdf_algorithm_from_params(algorithm_id,
71 : kdf_param_val,
72 : &kdf_algorithm);
73 190 : if (!NT_STATUS_IS_OK(status)) {
74 0 : goto out;
75 : }
76 : }
77 :
78 190 : 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 190 : if (!NT_STATUS_IS_OK(status)) {
88 0 : goto out;
89 : }
90 :
91 190 : out:
92 190 : 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 22 : uint8_t root_key_data[GKDI_KEY_LEN];
132 100 : NTSTATUS status = NT_STATUS_OK;
133 100 : int ret = LDB_SUCCESS;
134 :
135 22 : static const char *server_config_attrs[] = {
136 : "msKds-Version",
137 : "msKds-KDFAlgorithmID",
138 : "msKds-SecretAgreementAlgorithmID",
139 : "msKds-SecretAgreementParam",
140 : "msKds-PublicKeyLength",
141 : "msKds-PrivateKeyLength",
142 : "msKds-KDFParam",
143 : NULL
144 : };
145 :
146 100 : *root_key_dn_out = NULL;
147 :
148 100 : tmp_ctx = talloc_new(mem_ctx);
149 100 : if (tmp_ctx == NULL) {
150 0 : ret = ldb_oom(ldb);
151 0 : goto out;
152 : }
153 :
154 100 : server_config_dn = samdb_configuration_dn(ldb,
155 : mem_ctx,
156 : "CN=Group Key Distribution Service Server Configuration,"
157 : "CN=Server Configuration,"
158 : "CN=Group Key Distribution Service,"
159 : "CN=Services");
160 100 : if (server_config_dn == NULL) {
161 0 : ret = ldb_oom(ldb);
162 0 : goto out;
163 : }
164 :
165 100 : ret = dsdb_search_dn(ldb,
166 : tmp_ctx,
167 : &server_config_res,
168 : server_config_dn,
169 : server_config_attrs,
170 : 0);
171 :
172 100 : if (ret == LDB_ERR_NO_SUCH_OBJECT) {
173 0 : ldb_asprintf_errstring(ldb, "Unable to create new GKDI root key as we do not have a GKDI server configuration at %s",
174 : ldb_dn_get_linearized(server_config_dn));
175 0 : goto out;
176 : }
177 :
178 100 : if (ret != LDB_SUCCESS) {
179 0 : goto out;
180 : }
181 :
182 100 : server_config_msg = server_config_res->msgs[0];
183 :
184 22 : server_config_version_val
185 100 : = ldb_msg_find_ldb_val(server_config_msg,
186 : "msKds-Version");
187 22 : server_config_version
188 100 : = ldb_msg_find_attr_as_uint64(server_config_msg,
189 : "msKds-Version",
190 : 0);
191 :
192 : /* These values we assert on, so we don't create keys we can't use */
193 100 : if (server_config_version_val == NULL) {
194 : /*
195 : * The systemMustContain msKds-Version attribute
196 : * cannot be read, so if absent we just fail with
197 : * permission denied, as that is all that this can
198 : * mean
199 : */
200 1 : ldb_asprintf_errstring(ldb,
201 : "Unwilling to create new GKDI root key as "
202 : "msKds-Version is not readable on %s\n",
203 : ldb_dn_get_linearized(server_config_dn));
204 1 : ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
205 1 : goto out;
206 99 : } else if (server_config_version != 1) {
207 1 : ldb_asprintf_errstring(ldb,
208 : "Unwilling to create new GKDI root key as "
209 : "%s has msKds-Version = %s "
210 : "and we only support version 1\n",
211 : ldb_dn_get_linearized(server_config_dn),
212 : ldb_msg_find_attr_as_string(server_config_msg, "msKds-Version", "(missing)"));
213 1 : ret = LDB_ERR_CONSTRAINT_VIOLATION;
214 1 : goto out;
215 : }
216 :
217 22 : server_config_KDFAlgorithmID
218 98 : = ldb_msg_find_attr_as_string(server_config_msg,
219 : "msKds-KDFAlgorithmID",
220 : SP800_108_CTR_HMAC
221 : );
222 :
223 22 : server_config_KDFParam
224 98 : = ldb_msg_find_ldb_val(server_config_msg,
225 : "msKds-KDFParam");
226 98 : if (server_config_KDFParam == NULL) {
227 98 : struct KdfParameters kdf_parameters = {
228 : .hash_algorithm = "SHA512"
229 : };
230 22 : enum ndr_err_code err;
231 :
232 98 : err = ndr_push_struct_blob(&kdf_parameters_blob,
233 : tmp_ctx,
234 : &kdf_parameters,
235 : (ndr_push_flags_fn_t)
236 : ndr_push_KdfParameters);
237 :
238 98 : if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
239 0 : status = ndr_map_error2ntstatus(err);
240 0 : ldb_asprintf_errstring(ldb,
241 : "KdfParameters pull failed: %s\n",
242 : nt_errstr(status));
243 0 : ret = LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
244 0 : goto out;
245 : }
246 :
247 98 : server_config_KDFParam = &kdf_parameters_blob;
248 : }
249 :
250 98 : status = kdf_algorithm_from_params(server_config_KDFAlgorithmID,
251 : server_config_KDFParam,
252 : &kdf_algorithm);
253 98 : if (!NT_STATUS_IS_OK(status)) {
254 1 : ldb_asprintf_errstring(ldb,
255 : "Unwilling to create new GKDI root key as "
256 : "%s has an unsupported msKds-KDFAlgorithmID / msKds-KDFParam combination set: %s\n",
257 : ldb_dn_get_linearized(server_config_dn),
258 : nt_errstr(status));
259 1 : ret = LDB_ERR_CONSTRAINT_VIOLATION;
260 1 : goto out;
261 : }
262 :
263 22 : server_config_SecretAgreementAlgorithmID
264 97 : = ldb_msg_find_attr_as_string(server_config_msg,
265 : "msKds-SecretAgreementAlgorithmID",
266 : "DH");
267 :
268 : /* Optional in msKds-ProvRootKey */
269 22 : server_config_SecretAgreementParam
270 97 : = ldb_msg_find_ldb_val(server_config_msg,
271 : "msKds-SecretAgreementParam");
272 97 : if (server_config_SecretAgreementParam == NULL) {
273 22 : static const uint8_t ffc_dh_parameters[] = {
274 : 12, 2, 0, 0, 68, 72, 80, 77, 0, 1, 0,
275 : 0, 135, 168, 230, 29, 180, 182, 102, 60, 255, 187,
276 : 209, 156, 101, 25, 89, 153, 140, 238, 246, 8, 102,
277 : 13, 208, 242, 93, 44, 238, 212, 67, 94, 59, 0,
278 : 224, 13, 248, 241, 214, 25, 87, 212, 250, 247, 223,
279 : 69, 97, 178, 170, 48, 22, 195, 217, 17, 52, 9,
280 : 111, 170, 59, 244, 41, 109, 131, 14, 154, 124, 32,
281 : 158, 12, 100, 151, 81, 122, 189, 90, 138, 157, 48,
282 : 107, 207, 103, 237, 145, 249, 230, 114, 91, 71, 88,
283 : 192, 34, 224, 177, 239, 66, 117, 191, 123, 108, 91,
284 : 252, 17, 212, 95, 144, 136, 185, 65, 245, 78, 177,
285 : 229, 155, 184, 188, 57, 160, 191, 18, 48, 127, 92,
286 : 79, 219, 112, 197, 129, 178, 63, 118, 182, 58, 202,
287 : 225, 202, 166, 183, 144, 45, 82, 82, 103, 53, 72,
288 : 138, 14, 241, 60, 109, 154, 81, 191, 164, 171, 58,
289 : 216, 52, 119, 150, 82, 77, 142, 246, 161, 103, 181,
290 : 164, 24, 37, 217, 103, 225, 68, 229, 20, 5, 100,
291 : 37, 28, 202, 203, 131, 230, 180, 134, 246, 179, 202,
292 : 63, 121, 113, 80, 96, 38, 192, 184, 87, 246, 137,
293 : 150, 40, 86, 222, 212, 1, 10, 189, 11, 230, 33,
294 : 195, 163, 150, 10, 84, 231, 16, 195, 117, 242, 99,
295 : 117, 215, 1, 65, 3, 164, 181, 67, 48, 193, 152,
296 : 175, 18, 97, 22, 210, 39, 110, 17, 113, 95, 105,
297 : 56, 119, 250, 215, 239, 9, 202, 219, 9, 74, 233,
298 : 30, 26, 21, 151, 63, 179, 44, 155, 115, 19, 77,
299 : 11, 46, 119, 80, 102, 96, 237, 189, 72, 76, 167,
300 : 177, 143, 33, 239, 32, 84, 7, 244, 121, 58, 26,
301 : 11, 161, 37, 16, 219, 193, 80, 119, 190, 70, 63,
302 : 255, 79, 237, 74, 172, 11, 181, 85, 190, 58, 108,
303 : 27, 12, 107, 71, 177, 188, 55, 115, 191, 126, 140,
304 : 111, 98, 144, 18, 40, 248, 194, 140, 187, 24, 165,
305 : 90, 227, 19, 65, 0, 10, 101, 1, 150, 249, 49,
306 : 199, 122, 87, 242, 221, 244, 99, 229, 233, 236, 20,
307 : 75, 119, 125, 230, 42, 170, 184, 168, 98, 138, 195,
308 : 118, 210, 130, 214, 237, 56, 100, 230, 121, 130, 66,
309 : 142, 188, 131, 29, 20, 52, 143, 111, 47, 145, 147,
310 : 181, 4, 90, 242, 118, 113, 100, 225, 223, 201, 103,
311 : 193, 251, 63, 46, 85, 164, 189, 27, 255, 232, 59,
312 : 156, 128, 208, 82, 185, 133, 209, 130, 234, 10, 219,
313 : 42, 59, 115, 19, 211, 254, 20, 200, 72, 75, 30,
314 : 5, 37, 136, 185, 183, 210, 187, 210, 223, 1, 97,
315 : 153, 236, 208, 110, 21, 87, 205, 9, 21, 179, 53,
316 : 59, 187, 100, 224, 236, 55, 127, 208, 40, 55, 13,
317 : 249, 43, 82, 199, 137, 20, 40, 205, 198, 126, 182,
318 : 24, 75, 82, 61, 29, 178, 70, 195, 47, 99, 7,
319 : 132, 144, 240, 14, 248, 214, 71, 209, 72, 212, 121,
320 : 84, 81, 94, 35, 39, 207, 239, 152, 197, 130, 102,
321 : 75, 76, 15, 108, 196, 22, 89};
322 22 : static const DATA_BLOB ffc_dh_parameters_blob = {
323 : discard_const_p(uint8_t, ffc_dh_parameters),
324 : sizeof ffc_dh_parameters};
325 97 : server_config_SecretAgreementParam = &ffc_dh_parameters_blob;
326 : }
327 :
328 22 : server_config_PublicKeyLength
329 97 : = ldb_msg_find_attr_as_uint64(server_config_msg,
330 : "msKds-PublicKeyLength",
331 : 2048);
332 :
333 22 : server_config_PrivateKeyLength
334 97 : = ldb_msg_find_attr_as_uint64(server_config_msg,
335 : "msKds-PrivateKeyLength",
336 : 256);
337 :
338 97 : add_msg = ldb_msg_new(tmp_ctx);
339 97 : if (add_msg == NULL) {
340 0 : ret = ldb_oom(ldb);
341 0 : goto out;
342 : }
343 :
344 97 : ret = ldb_msg_append_string(add_msg,
345 : "objectClass",
346 : "msKds-ProvRootKey",
347 : LDB_FLAG_MOD_ADD);
348 97 : if (ret) {
349 0 : goto out;
350 : }
351 :
352 : {
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 39 : 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 39 : TALLOC_CTX *tmp_ctx = NULL;
585 39 : struct ldb_dn *root_key_dn = NULL;
586 39 : struct ldb_result *res = NULL;
587 39 : int ret = LDB_SUCCESS;
588 :
589 39 : *root_key_out = NULL;
590 :
591 39 : tmp_ctx = talloc_new(mem_ctx);
592 39 : if (tmp_ctx == NULL) {
593 0 : ret = ldb_oom(ldb);
594 0 : goto out;
595 : }
596 :
597 39 : root_key_dn = samdb_gkdi_root_key_dn(ldb, tmp_ctx, root_key_id);
598 39 : if (root_key_dn == NULL) {
599 0 : ret = ldb_operr(ldb);
600 0 : goto out;
601 : }
602 :
603 39 : ret = dsdb_search_dn(
604 : ldb, tmp_ctx, &res, root_key_dn, root_key_attrs, 0);
605 39 : if (ret) {
606 0 : goto out;
607 : }
608 :
609 39 : 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 39 : *root_key_out = talloc_steal(mem_ctx, res->msgs[0]);
618 :
619 39 : out:
620 39 : talloc_free(tmp_ctx);
621 39 : return ret;
622 : }
623 :
624 151 : 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 151 : TALLOC_CTX *tmp_ctx = NULL;
633 151 : struct ldb_result *res = NULL;
634 151 : int ret = LDB_SUCCESS;
635 :
636 151 : *root_key_out = NULL;
637 :
638 151 : tmp_ctx = talloc_new(mem_ctx);
639 151 : if (tmp_ctx == NULL) {
640 0 : ret = ldb_oom(ldb);
641 0 : goto out;
642 : }
643 :
644 : {
645 151 : struct ldb_dn *root_key_container_dn = NULL;
646 :
647 151 : root_key_container_dn = samdb_gkdi_root_key_container_dn(
648 : ldb, tmp_ctx);
649 151 : if (root_key_container_dn == NULL) {
650 0 : ret = ldb_operr(ldb);
651 0 : goto out;
652 : }
653 :
654 151 : 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 151 : 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 151 : } most_recent_key = {
679 : .key = NULL,
680 : };
681 0 : unsigned i;
682 :
683 1767 : for (i = 0; i < res->count; ++i) {
684 1616 : struct root_key_candidate key = {
685 1616 : .key = res->msgs[i],
686 : };
687 1616 : const struct ldb_val *rdn_val = NULL;
688 0 : bool ok;
689 :
690 1616 : key.create_time = samdb_result_nttime(
691 : key.key, "msKds-CreateTime", 0);
692 1616 : if (key.create_time < most_recent_key.create_time) {
693 : /* We already have a more recent key. */
694 1104 : continue;
695 : }
696 :
697 512 : rdn_val = ldb_dn_get_rdn_val(key.key->dn);
698 512 : if (rdn_val == NULL) {
699 0 : continue;
700 : }
701 :
702 512 : 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 512 : ok = parse_guid_string((const char *)rdn_val->data,
711 : &key.id);
712 512 : 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 512 : most_recent_key = key;
722 : }
723 :
724 151 : 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 151 : *root_key_id_out = most_recent_key.id;
748 151 : *root_key_out = talloc_steal(mem_ctx, most_recent_key.key);
749 : }
750 :
751 151 : out:
752 151 : talloc_free(tmp_ctx);
753 151 : return ret;
754 : }
|