2 Unix SMB/CIFS implementation.
3 Group Key Distribution Protocol functions
5 Copyright (C) Catalyst.Net Ltd 2024
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.
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.
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/>.
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"
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)
42 NTSTATUS status = NT_STATUS_OK;
43 struct ldb_val root_key_data = {};
44 struct KdfAlgorithm kdf_algorithm = {};
46 const int version = ldb_msg_find_attr_as_int(msg, "msKds-Version", 0);
47 const NTTIME create_time = samdb_result_nttime(msg,
50 const NTTIME use_start_time = samdb_result_nttime(msg,
53 const char *domain_id = ldb_msg_find_attr_as_string(msg,
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;
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,
73 if (!NT_STATUS_IS_OK(status)) {
78 status = ProvRootKey(mem_ctx,
87 if (!NT_STATUS_IS_OK(status)) {
96 * Calculate an appropriate useStartTime for a root key created at
99 * This function goes unused.
101 NTTIME gkdi_root_key_use_start_time(const NTTIME current_time)
103 const NTTIME start_time = gkdi_get_interval_start_time(current_time);
105 return start_time + gkdi_key_cycle_duration + gkdi_max_clock_skew;
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)
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;
134 static const char *server_config_attrs[] = {
136 "msKds-KDFAlgorithmID",
137 "msKds-SecretAgreementAlgorithmID",
138 "msKds-SecretAgreementParam",
139 "msKds-PublicKeyLength",
140 "msKds-PrivateKeyLength",
145 *root_key_dn_out = NULL;
147 tmp_ctx = talloc_new(mem_ctx);
148 if (tmp_ctx == NULL) {
153 server_config_dn = samdb_configuration_dn(ldb,
155 "CN=Group Key Distribution Service Server Configuration,"
156 "CN=Server Configuration,"
157 "CN=Group Key Distribution Service,"
159 if (server_config_dn == NULL) {
164 ret = dsdb_search_dn(ldb,
169 DSDB_SEARCH_ONE_ONLY);
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));
177 if (ret != LDB_SUCCESS) {
181 server_config_msg = server_config_res->msgs[0];
183 server_config_version_val
184 = ldb_msg_find_ldb_val(server_config_msg,
186 server_config_version
187 = ldb_msg_find_attr_as_uint64(server_config_msg,
191 /* These values we assert on, so we don't create keys we can't use */
192 if (server_config_version_val == NULL) {
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
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;
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;
216 server_config_KDFAlgorithmID
217 = ldb_msg_find_attr_as_string(server_config_msg,
218 "msKds-KDFAlgorithmID",
222 server_config_KDFParam
223 = ldb_msg_find_ldb_val(server_config_msg,
225 if (server_config_KDFParam == NULL) {
226 struct KdfParameters kdf_parameters = {
227 .hash_algorithm = "SHA512"
229 enum ndr_err_code err;
231 err = ndr_push_struct_blob(&kdf_parameters_blob,
234 (ndr_push_flags_fn_t)
235 ndr_push_KdfParameters);
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",
242 ret = LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
246 server_config_KDFParam = &kdf_parameters_blob;
249 status = kdf_algorithm_from_params(server_config_KDFAlgorithmID,
250 server_config_KDFParam,
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),
258 ret = LDB_ERR_CONSTRAINT_VIOLATION;
262 server_config_SecretAgreementAlgorithmID
263 = ldb_msg_find_attr_as_string(server_config_msg,
264 "msKds-SecretAgreementAlgorithmID",
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;
327 server_config_PublicKeyLength
328 = ldb_msg_find_attr_as_uint64(server_config_msg,
329 "msKds-PublicKeyLength",
332 server_config_PrivateKeyLength
333 = ldb_msg_find_attr_as_uint64(server_config_msg,
334 "msKds-PrivateKeyLength",
337 add_msg = ldb_msg_new(tmp_ctx);
338 if (add_msg == NULL) {
343 ret = ldb_msg_append_string(add_msg,
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};
356 generate_secret_buffer(root_key_data, sizeof root_key_data);
358 ret = ldb_msg_append_value(add_msg,
367 ret = samdb_msg_append_uint64(ldb,
377 ret = samdb_msg_append_uint64(ldb,
380 "msKds-UseStartTime",
388 struct ldb_dn *domain_dn = NULL;
390 ret = samdb_server_reference_dn(ldb, tmp_ctx, &domain_dn);
395 ret = ldb_msg_append_linearized_dn(add_msg,
404 ret = samdb_msg_append_uint64(ldb,
408 server_config_version,
414 ret = ldb_msg_append_string(add_msg,
415 "msKds-KDFAlgorithmID",
416 server_config_KDFAlgorithmID,
422 ret = ldb_msg_append_string(add_msg,
423 "msKds-SecretAgreementAlgorithmID",
424 server_config_SecretAgreementAlgorithmID,
430 if (server_config_SecretAgreementParam != NULL) {
431 ret = ldb_msg_append_value(add_msg,
432 "msKds-SecretAgreementParam",
433 server_config_SecretAgreementParam,
440 ret = samdb_msg_append_uint64(ldb,
443 "msKds-PublicKeyLength",
444 server_config_PublicKeyLength,
450 ret = samdb_msg_append_uint64(ldb,
453 "msKds-PrivateKeyLength",
454 server_config_PrivateKeyLength,
457 ret = ldb_msg_append_value(add_msg,
459 server_config_KDFParam,
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};
470 generate_secret_buffer(guid_buf, sizeof guid_buf);
472 status = GUID_from_ndr_blob(&guid_blob, &root_key_id);
473 if (!NT_STATUS_IS_OK(status)) {
474 ret = ldb_operr(ldb);
480 struct ldb_dn *root_key_dn = NULL;
482 root_key_dn = samdb_gkdi_root_key_dn(ldb,
485 if (root_key_dn == NULL) {
486 ret = ldb_operr(ldb);
490 add_msg->dn = root_key_dn;
493 ret = dsdb_add(ldb, add_msg, 0);
498 *root_key_id_out = root_key_id;
499 *root_key_dn_out = talloc_steal(mem_ctx, add_msg->dn);
502 talloc_free(tmp_ctx);
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.
511 static const char *const root_key_attrs[] = {
514 "msKds-KDFAlgorithmID",
516 /* "msKds-PrivateKeyLength", */
517 /* "msKds-PublicKeyLength", */
519 /* "msKds-SecretAgreementAlgorithmID", */
520 /* "msKds-SecretAgreementParam", */
521 "msKds-UseStartTime",
527 * Create and return a new GKDI root key.
529 * This function goes unused.
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)
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;
543 *root_key_out = NULL;
545 tmp_ctx = talloc_new(mem_ctx);
546 if (tmp_ctx == NULL) {
551 ret = gkdi_create_root_key(tmp_ctx,
561 ret = dsdb_search_dn(
562 ldb, tmp_ctx, &res, root_key_dn, root_key_attrs, 0);
567 if (res->count != 1) {
568 ret = LDB_ERR_NO_SUCH_OBJECT;
572 *root_key_out = talloc_steal(mem_ctx, res->msgs[0]);
575 talloc_free(tmp_ctx);
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)
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;
589 *root_key_out = NULL;
591 tmp_ctx = talloc_new(mem_ctx);
592 if (tmp_ctx == NULL) {
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);
603 ret = dsdb_search_dn(
604 ldb, tmp_ctx, &res, root_key_dn, root_key_attrs, 0);
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");
617 *root_key_out = talloc_steal(mem_ctx, res->msgs[0]);
620 talloc_free(tmp_ctx);
624 int gkdi_most_recently_created_root_key(
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)
632 TALLOC_CTX *tmp_ctx = NULL;
633 struct ldb_result *res = NULL;
634 int ret = LDB_SUCCESS;
636 *root_key_out = NULL;
638 tmp_ctx = talloc_new(mem_ctx);
639 if (tmp_ctx == NULL) {
645 struct ldb_dn *root_key_container_dn = NULL;
647 root_key_container_dn = samdb_gkdi_root_key_container_dn(
649 if (root_key_container_dn == NULL) {
650 ret = ldb_operr(ldb);
654 ret = dsdb_search(ldb,
657 root_key_container_dn,
661 "(msKds-UseStartTime<=%" PRIu64 ")",
669 * Windows just gives up if there are more than 1000 root keys in the
674 struct root_key_candidate {
676 const struct ldb_message *key;
678 } most_recent_key = {
683 for (i = 0; i < res->count; ++i) {
684 struct root_key_candidate key = {
687 const struct ldb_val *rdn_val = NULL;
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. */
697 rdn_val = ldb_dn_get_rdn_val(key.key->dn);
698 if (rdn_val == NULL) {
702 if (rdn_val->length != 36) {
704 * Check the RDN is the right length — 36 is the
710 ok = parse_guid_string((const char *)rdn_val->data,
713 /* The RDN is not a correctly formatted GUID. */
718 * We’ve found a new candidate for the most recent root
721 most_recent_key = key;
724 if (most_recent_key.key == NULL) {
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.
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.
738 ret = dsdb_werror(ldb,
739 LDB_ERR_NO_SUCH_OBJECT,
740 W_ERROR(HRES_ERROR_V(
742 "failed to find a suitable root key");
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);
752 talloc_free(tmp_ctx);