mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
s4:dsdb: Add functions for Group Managed Service Accounts implementation
Signed-off-by: Jo Sutton <josutton@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
parent
85d34934e1
commit
85fbdcd048
@ -34,6 +34,77 @@
|
|||||||
#include "librpc/gen_ndr/gkdi.h"
|
#include "librpc/gen_ndr/gkdi.h"
|
||||||
#include "librpc/gen_ndr/ndr_gkdi.h"
|
#include "librpc/gen_ndr/ndr_gkdi.h"
|
||||||
|
|
||||||
|
NTSTATUS gkdi_root_key_from_msg(TALLOC_CTX *mem_ctx,
|
||||||
|
const struct GUID root_key_id,
|
||||||
|
const struct ldb_message *const msg,
|
||||||
|
const struct ProvRootKey **const root_key_out)
|
||||||
|
{
|
||||||
|
NTSTATUS status = NT_STATUS_OK;
|
||||||
|
struct ldb_val root_key_data = {};
|
||||||
|
struct KdfAlgorithm kdf_algorithm = {};
|
||||||
|
|
||||||
|
const int version = ldb_msg_find_attr_as_int(msg, "msKds-Version", 0);
|
||||||
|
const NTTIME create_time = samdb_result_nttime(msg,
|
||||||
|
"msKds-CreateTime",
|
||||||
|
0);
|
||||||
|
const NTTIME use_start_time = samdb_result_nttime(msg,
|
||||||
|
"msKds-UseStartTime",
|
||||||
|
0);
|
||||||
|
const char *domain_id = ldb_msg_find_attr_as_string(msg,
|
||||||
|
"msKds-DomainID",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
{
|
||||||
|
const struct ldb_val *root_key_val = ldb_msg_find_ldb_val(
|
||||||
|
msg, "msKds-RootKeyData");
|
||||||
|
if (root_key_val != NULL) {
|
||||||
|
root_key_data = *root_key_val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const char *algorithm_id = ldb_msg_find_attr_as_string(
|
||||||
|
msg, "msKds-KDFAlgorithmID", NULL);
|
||||||
|
const struct ldb_val *kdf_param_val = ldb_msg_find_ldb_val(
|
||||||
|
msg, "msKds-KDFParam");
|
||||||
|
status = kdf_algorithm_from_params(algorithm_id,
|
||||||
|
kdf_param_val,
|
||||||
|
&kdf_algorithm);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status = ProvRootKey(mem_ctx,
|
||||||
|
root_key_id,
|
||||||
|
version,
|
||||||
|
root_key_data,
|
||||||
|
create_time,
|
||||||
|
use_start_time,
|
||||||
|
domain_id,
|
||||||
|
kdf_algorithm,
|
||||||
|
root_key_out);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate an appropriate useStartTime for a root key created at
|
||||||
|
* ‘current_time’.
|
||||||
|
*
|
||||||
|
* This function goes unused.
|
||||||
|
*/
|
||||||
|
NTTIME gkdi_root_key_use_start_time(const NTTIME current_time)
|
||||||
|
{
|
||||||
|
const NTTIME start_time = gkdi_get_interval_start_time(current_time);
|
||||||
|
|
||||||
|
return start_time + gkdi_key_cycle_duration + gkdi_max_clock_skew;
|
||||||
|
}
|
||||||
|
|
||||||
static int gkdi_create_root_key(TALLOC_CTX *mem_ctx,
|
static int gkdi_create_root_key(TALLOC_CTX *mem_ctx,
|
||||||
struct ldb_context *const ldb,
|
struct ldb_context *const ldb,
|
||||||
const NTTIME current_time,
|
const NTTIME current_time,
|
||||||
@ -504,3 +575,180 @@ out:
|
|||||||
talloc_free(tmp_ctx);
|
talloc_free(tmp_ctx);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int gkdi_root_key_from_id(TALLOC_CTX *mem_ctx,
|
||||||
|
struct ldb_context *const ldb,
|
||||||
|
const struct GUID *const root_key_id,
|
||||||
|
const struct ldb_message **const root_key_out)
|
||||||
|
{
|
||||||
|
TALLOC_CTX *tmp_ctx = NULL;
|
||||||
|
struct ldb_dn *root_key_dn = NULL;
|
||||||
|
struct ldb_result *res = NULL;
|
||||||
|
int ret = LDB_SUCCESS;
|
||||||
|
|
||||||
|
*root_key_out = NULL;
|
||||||
|
|
||||||
|
tmp_ctx = talloc_new(mem_ctx);
|
||||||
|
if (tmp_ctx == NULL) {
|
||||||
|
ret = ldb_oom(ldb);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
root_key_dn = samdb_gkdi_root_key_dn(ldb, tmp_ctx, root_key_id);
|
||||||
|
if (root_key_dn == NULL) {
|
||||||
|
ret = ldb_operr(ldb);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dsdb_search_dn(
|
||||||
|
ldb, tmp_ctx, &res, root_key_dn, root_key_attrs, 0);
|
||||||
|
if (ret) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res->count != 1) {
|
||||||
|
ret = dsdb_werror(ldb,
|
||||||
|
LDB_ERR_NO_SUCH_OBJECT,
|
||||||
|
W_ERROR(HRES_ERROR_V(HRES_NTE_NO_KEY)),
|
||||||
|
"failed to find root key");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
*root_key_out = talloc_steal(mem_ctx, res->msgs[0]);
|
||||||
|
|
||||||
|
out:
|
||||||
|
talloc_free(tmp_ctx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gkdi_most_recently_created_root_key(
|
||||||
|
TALLOC_CTX *mem_ctx,
|
||||||
|
struct ldb_context *const ldb,
|
||||||
|
_UNUSED_ const NTTIME current_time,
|
||||||
|
const NTTIME not_after,
|
||||||
|
struct GUID *const root_key_id_out,
|
||||||
|
const struct ldb_message **const root_key_out)
|
||||||
|
{
|
||||||
|
TALLOC_CTX *tmp_ctx = NULL;
|
||||||
|
struct ldb_result *res = NULL;
|
||||||
|
int ret = LDB_SUCCESS;
|
||||||
|
|
||||||
|
*root_key_out = NULL;
|
||||||
|
|
||||||
|
tmp_ctx = talloc_new(mem_ctx);
|
||||||
|
if (tmp_ctx == NULL) {
|
||||||
|
ret = ldb_oom(ldb);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
struct ldb_dn *root_key_container_dn = NULL;
|
||||||
|
|
||||||
|
root_key_container_dn = samdb_gkdi_root_key_container_dn(
|
||||||
|
ldb, tmp_ctx);
|
||||||
|
if (root_key_container_dn == NULL) {
|
||||||
|
ret = ldb_operr(ldb);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dsdb_search(ldb,
|
||||||
|
tmp_ctx,
|
||||||
|
&res,
|
||||||
|
root_key_container_dn,
|
||||||
|
LDB_SCOPE_ONELEVEL,
|
||||||
|
root_key_attrs,
|
||||||
|
0,
|
||||||
|
"(msKds-UseStartTime<=%" PRIu64 ")",
|
||||||
|
not_after);
|
||||||
|
if (ret) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Windows just gives up if there are more than 1000 root keys in the
|
||||||
|
* container.
|
||||||
|
*/
|
||||||
|
|
||||||
|
{
|
||||||
|
struct root_key_candidate {
|
||||||
|
struct GUID id;
|
||||||
|
const struct ldb_message *key;
|
||||||
|
NTTIME create_time;
|
||||||
|
} most_recent_key = {
|
||||||
|
.key = NULL,
|
||||||
|
};
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < res->count; ++i) {
|
||||||
|
struct root_key_candidate key = {
|
||||||
|
.key = res->msgs[i],
|
||||||
|
};
|
||||||
|
const struct ldb_val *rdn_val = NULL;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
key.create_time = samdb_result_nttime(
|
||||||
|
key.key, "msKds-CreateTime", 0);
|
||||||
|
if (key.create_time < most_recent_key.create_time) {
|
||||||
|
/* We already have a more recent key. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rdn_val = ldb_dn_get_rdn_val(key.key->dn);
|
||||||
|
if (rdn_val == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rdn_val->length != 36) {
|
||||||
|
/*
|
||||||
|
* Check the RDN is the right length — 36 is the
|
||||||
|
* length of a UUID.
|
||||||
|
*/
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = parse_guid_string((const char *)rdn_val->data,
|
||||||
|
&key.id);
|
||||||
|
if (!ok) {
|
||||||
|
/* The RDN is not a correctly formatted GUID. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We’ve found a new candidate for the most recent root
|
||||||
|
* key.
|
||||||
|
*/
|
||||||
|
most_recent_key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (most_recent_key.key == NULL) {
|
||||||
|
/*
|
||||||
|
* We were not able to find a suitable root key, but
|
||||||
|
* there is a possibility that a key we create now will
|
||||||
|
* do: if gkdi_root_key_use_start_time(current_time) ≤
|
||||||
|
* not_after, then a newly‐created key will satisfy our
|
||||||
|
* caller’s requirements.
|
||||||
|
*
|
||||||
|
* Unfortunately, with gMSAs this (I believe) will never
|
||||||
|
* be the case. It’s too late to call
|
||||||
|
* gkdi_new_root_key() — the new key will be a bit *too*
|
||||||
|
* new to be usable for a gMSA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ret = dsdb_werror(ldb,
|
||||||
|
LDB_ERR_NO_SUCH_OBJECT,
|
||||||
|
W_ERROR(HRES_ERROR_V(
|
||||||
|
HRES_NTE_NO_KEY)),
|
||||||
|
"failed to find a suitable root key");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the root key that we found. */
|
||||||
|
*root_key_id_out = most_recent_key.id;
|
||||||
|
*root_key_out = talloc_steal(mem_ctx, most_recent_key.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
talloc_free(tmp_ctx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -28,6 +28,19 @@
|
|||||||
#include "librpc/gen_ndr/misc.h"
|
#include "librpc/gen_ndr/misc.h"
|
||||||
|
|
||||||
struct ldb_message;
|
struct ldb_message;
|
||||||
|
struct ProvRootKey;
|
||||||
|
NTSTATUS gkdi_root_key_from_msg(TALLOC_CTX *mem_ctx,
|
||||||
|
const struct GUID root_key_id,
|
||||||
|
const struct ldb_message *const msg,
|
||||||
|
const struct ProvRootKey **const root_key_out);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate an appropriate useStartTime for a root key created at
|
||||||
|
* ‘current_time’.
|
||||||
|
*
|
||||||
|
* This function goes unused.
|
||||||
|
*/
|
||||||
|
NTTIME gkdi_root_key_use_start_time(const NTTIME current_time);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create and return a new GKDI root key.
|
* Create and return a new GKDI root key.
|
||||||
@ -42,5 +55,17 @@ int gkdi_new_root_key(TALLOC_CTX *mem_ctx,
|
|||||||
struct GUID *const root_key_id_out,
|
struct GUID *const root_key_id_out,
|
||||||
const struct ldb_message **const root_key_out);
|
const struct ldb_message **const root_key_out);
|
||||||
|
|
||||||
|
int gkdi_root_key_from_id(TALLOC_CTX *mem_ctx,
|
||||||
|
struct ldb_context *const ldb,
|
||||||
|
const struct GUID *const root_key_id,
|
||||||
|
const struct ldb_message **const root_key_out);
|
||||||
|
|
||||||
|
int gkdi_most_recently_created_root_key(
|
||||||
|
TALLOC_CTX *mem_ctx,
|
||||||
|
struct ldb_context *const ldb,
|
||||||
|
const NTTIME current_time,
|
||||||
|
const NTTIME not_after,
|
||||||
|
struct GUID *const root_key_id_out,
|
||||||
|
const struct ldb_message **const root_key_out);
|
||||||
|
|
||||||
#endif /* DSDB_GMSA_GKDI_H */
|
#endif /* DSDB_GMSA_GKDI_H */
|
||||||
|
1414
source4/dsdb/gmsa/util.c
Normal file
1414
source4/dsdb/gmsa/util.c
Normal file
File diff suppressed because it is too large
Load Diff
106
source4/dsdb/gmsa/util.h
Normal file
106
source4/dsdb/gmsa/util.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
Unix SMB/CIFS implementation.
|
||||||
|
msDS-ManagedPassword attribute for Group Managed Service Accounts
|
||||||
|
|
||||||
|
Copyright (C) Catalyst.Net Ltd 2024
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DSDB_GMSA_UTIL_H
|
||||||
|
#define DSDB_GMSA_UTIL_H
|
||||||
|
|
||||||
|
#include "ldb.h"
|
||||||
|
#include "ldb_module.h"
|
||||||
|
#include <talloc.h>
|
||||||
|
|
||||||
|
#include "lib/crypto/gkdi.h"
|
||||||
|
#include "lib/crypto/gmsa.h"
|
||||||
|
#include "lib/util/data_blob.h"
|
||||||
|
#include "lib/util/time.h"
|
||||||
|
|
||||||
|
struct gmsa_update {
|
||||||
|
/* An optional request to set the previous password. */
|
||||||
|
struct ldb_request *old_pw_req;
|
||||||
|
/* A request to set the current password. */
|
||||||
|
struct ldb_request *new_pw_req;
|
||||||
|
/* An request to set the managed password ID. */
|
||||||
|
struct ldb_request *pwd_id_req;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gmsa_update_pwd_part {
|
||||||
|
const struct ProvRootKey *root_key;
|
||||||
|
struct Gkid gkid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gmsa_update_pwd {
|
||||||
|
struct gmsa_update_pwd_part prev_id;
|
||||||
|
struct gmsa_update_pwd_part new_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dom_sid;
|
||||||
|
int gmsa_allowed_to_view_managed_password(TALLOC_CTX *mem_ctx,
|
||||||
|
struct ldb_context *ldb,
|
||||||
|
const struct ldb_message *msg,
|
||||||
|
const struct dom_sid *account_sid,
|
||||||
|
bool *allowed_out);
|
||||||
|
|
||||||
|
struct KeyEnvelope;
|
||||||
|
void gmsa_update_managed_pwd_id(struct KeyEnvelope *pwd_id,
|
||||||
|
const struct gmsa_update_pwd_part *new_pwd);
|
||||||
|
|
||||||
|
NTSTATUS gmsa_pack_managed_pwd_id(TALLOC_CTX *mem_ctx,
|
||||||
|
const struct KeyEnvelope *pwd_id,
|
||||||
|
DATA_BLOB *pwd_id_out);
|
||||||
|
|
||||||
|
int gmsa_generate_blobs(struct ldb_context *ldb,
|
||||||
|
TALLOC_CTX *mem_ctx,
|
||||||
|
const NTTIME current_time,
|
||||||
|
const struct dom_sid *const account_sid,
|
||||||
|
DATA_BLOB *pwd_id_blob_out,
|
||||||
|
struct gmsa_null_terminated_password **password_out);
|
||||||
|
|
||||||
|
NTSTATUS gmsa_pack_managed_pwd(TALLOC_CTX *mem_ctx,
|
||||||
|
const uint8_t *new_password,
|
||||||
|
const uint8_t *old_password,
|
||||||
|
uint64_t query_interval,
|
||||||
|
uint64_t unchanged_interval,
|
||||||
|
DATA_BLOB *managed_pwd_out);
|
||||||
|
|
||||||
|
bool dsdb_account_is_gmsa(struct ldb_context *ldb,
|
||||||
|
const struct ldb_message *msg);
|
||||||
|
|
||||||
|
const struct KeyEnvelopeId *gmsa_get_managed_pwd_id(
|
||||||
|
const struct ldb_message *msg,
|
||||||
|
struct KeyEnvelopeId *key_env_out);
|
||||||
|
|
||||||
|
struct gmsa_return_pwd {
|
||||||
|
struct gmsa_null_terminated_password *prev_pwd;
|
||||||
|
struct gmsa_null_terminated_password *new_pwd;
|
||||||
|
NTTIME query_interval;
|
||||||
|
NTTIME unchanged_interval;
|
||||||
|
};
|
||||||
|
|
||||||
|
int gmsa_recalculate_managed_pwd(TALLOC_CTX *mem_ctx,
|
||||||
|
struct ldb_context *ldb,
|
||||||
|
const struct ldb_message *msg,
|
||||||
|
const NTTIME current_time,
|
||||||
|
struct gmsa_update **update_out,
|
||||||
|
struct gmsa_return_pwd *return_out);
|
||||||
|
|
||||||
|
#define DSDB_GMSA_TIME_OPAQUE ("dsdb_gmsa_time_opaque")
|
||||||
|
|
||||||
|
bool dsdb_gmsa_current_time(struct ldb_context *ldb, NTTIME *current_time_out);
|
||||||
|
|
||||||
|
#endif /* DSDB_GMSA_UTIL_H */
|
@ -13,7 +13,7 @@ bld.SAMBA_LIBRARY('samdb',
|
|||||||
)
|
)
|
||||||
|
|
||||||
bld.SAMBA_LIBRARY('samdb-common',
|
bld.SAMBA_LIBRARY('samdb-common',
|
||||||
source='common/util.c common/util_trusts.c common/util_groups.c common/util_samr.c common/dsdb_dn.c common/dsdb_access.c common/util_links.c common/rodc_helper.c gmsa/gkdi.c',
|
source='common/util.c common/util_trusts.c common/util_groups.c common/util_samr.c common/dsdb_dn.c common/dsdb_access.c common/util_links.c common/rodc_helper.c gmsa/gkdi.c gmsa/util.c',
|
||||||
autoproto='common/proto.h',
|
autoproto='common/proto.h',
|
||||||
private_library=True,
|
private_library=True,
|
||||||
deps='ldb NDR_DRSBLOBS util_ldb LIBCLI_AUTH samba-hostconfig samba_socket cli-ldap-common flag_mapping UTIL_RUNCMD SAMBA_VERSION samba-security gkdi gmsa'
|
deps='ldb NDR_DRSBLOBS util_ldb LIBCLI_AUTH samba-hostconfig samba_socket cli-ldap-common flag_mapping UTIL_RUNCMD SAMBA_VERSION samba-security gkdi gmsa'
|
||||||
|
Loading…
Reference in New Issue
Block a user