mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
lib:crypto: Add functions for deriving gMSA passwords
Signed-off-by: Jo Sutton <josutton@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
parent
e062db3225
commit
fe2dc16160
264
lib/crypto/gmsa.c
Normal file
264
lib/crypto/gmsa.c
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Group Managed Service Account functions
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include <gnutls/gnutls.h>
|
||||
#include "lib/crypto/gnutls_helpers.h"
|
||||
#include "lib/crypto/gkdi.h"
|
||||
#include "lib/crypto/gmsa.h"
|
||||
#include "librpc/gen_ndr/ndr_security.h"
|
||||
|
||||
static const uint8_t gmsa_security_descriptor[] = {
|
||||
/* O:SYD:(A;;FRFW;;;S-1-5-9) */
|
||||
0x01, 0x00, 0x04, 0x80, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1c, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x9f, 0x01, 0x12, 0x00,
|
||||
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x09, 0x00, 0x00, 0x00,
|
||||
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x12, 0x00, 0x00, 0x00};
|
||||
|
||||
static const uint8_t gmsa_password_label[] = {
|
||||
/* GMSA PASSWORD as a NULL‐terminated UTF‐16LE string. */
|
||||
'G', 0, 'M', 0, 'S', 0, 'A', 0, ' ', 0, 'P', 0, 'A', 0,
|
||||
'S', 0, 'S', 0, 'W', 0, 'O', 0, 'R', 0, 'D', 0, 0, 0,
|
||||
};
|
||||
|
||||
static NTSTATUS generate_gmsa_password(
|
||||
const uint8_t key[static const GKDI_KEY_LEN],
|
||||
const struct dom_sid *const account_sid,
|
||||
const struct KdfAlgorithm kdf_algorithm,
|
||||
uint8_t password[static const GMSA_PASSWORD_LEN])
|
||||
{
|
||||
NTSTATUS status = NT_STATUS_OK;
|
||||
gnutls_mac_algorithm_t algorithm;
|
||||
|
||||
algorithm = get_sp800_108_mac_algorithm(kdf_algorithm);
|
||||
if (algorithm == GNUTLS_MAC_UNKNOWN) {
|
||||
status = NT_STATUS_NOT_SUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (account_sid == NULL) {
|
||||
status = NT_STATUS_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t encoded_sid[ndr_size_dom_sid(account_sid, 0)];
|
||||
{
|
||||
struct ndr_push ndr = {
|
||||
.data = encoded_sid,
|
||||
.alloc_size = sizeof encoded_sid,
|
||||
.fixed_buf_size = true,
|
||||
};
|
||||
enum ndr_err_code ndr_err;
|
||||
|
||||
ndr_err = ndr_push_dom_sid(&ndr,
|
||||
NDR_SCALARS | NDR_BUFFERS,
|
||||
account_sid);
|
||||
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
||||
status = ndr_map_error2ntstatus(ndr_err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
status = samba_gnutls_sp800_108_derive_key(
|
||||
key,
|
||||
GKDI_KEY_LEN,
|
||||
NULL,
|
||||
0,
|
||||
gmsa_password_label,
|
||||
sizeof gmsa_password_label,
|
||||
encoded_sid,
|
||||
sizeof encoded_sid,
|
||||
algorithm,
|
||||
password,
|
||||
GMSA_PASSWORD_LEN);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void gmsa_post_process_password_buffer(
|
||||
uint8_t password[static const GMSA_PASSWORD_NULL_TERMINATED_LEN])
|
||||
{
|
||||
size_t n;
|
||||
|
||||
for (n = 0; n < GMSA_PASSWORD_LEN; n += 2) {
|
||||
const uint8_t a = password[n];
|
||||
const uint8_t b = password[n + 1];
|
||||
if (!a && !b) {
|
||||
/*
|
||||
* There is a 0.2% chance that the generated password
|
||||
* will contain an embedded null terminator, which will
|
||||
* need to be converted into U+0001.
|
||||
*/
|
||||
password[n] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Null‐terminate the password. */
|
||||
password[GMSA_PASSWORD_LEN] = 0;
|
||||
password[GMSA_PASSWORD_LEN + 1] = 0;
|
||||
}
|
||||
|
||||
NTSTATUS gmsa_password_based_on_key_id(
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct Gkid gkid,
|
||||
const NTTIME current_time,
|
||||
const struct ProvRootKey *const root_key,
|
||||
const struct dom_sid *const account_sid,
|
||||
uint8_t password[static const GMSA_PASSWORD_NULL_TERMINATED_LEN])
|
||||
{
|
||||
NTSTATUS status = NT_STATUS_OK;
|
||||
|
||||
/* Ensure that a specific seed key is being requested. */
|
||||
|
||||
if (!gkid_is_valid(gkid)) {
|
||||
status = NT_STATUS_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (gkid_key_type(gkid) != GKID_L2_SEED_KEY) {
|
||||
status = NT_STATUS_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Require the root key ID for the moment. */
|
||||
if (root_key == NULL) {
|
||||
status = NT_STATUS_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Assert that the root key may be used at this time. */
|
||||
if (current_time < root_key->use_start_time) {
|
||||
status = NT_STATUS_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
{
|
||||
/*
|
||||
* The key being requested must not be from the future. That
|
||||
* said, we allow for a little bit of clock skew so that samdb
|
||||
* can compute the next managed password prior to the expiration
|
||||
* of the current one.
|
||||
*/
|
||||
const struct Gkid current_gkid = gkdi_get_interval_id(
|
||||
current_time + gkdi_max_clock_skew);
|
||||
if (!gkid_less_than_or_equal_to(gkid, current_gkid)) {
|
||||
status = NT_STATUS_INVALID_PARAMETER;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Windows’ GetKey() might return not the specified L2 seed key, but an
|
||||
* earlier L2 seed key, or an L1 seed key, leaving the client to perform
|
||||
* the rest of the derivation. We are able to simplify things by always
|
||||
* deriving the specified L2 seed key, but if we implement a
|
||||
* client‐accessible GetKey(), we must take care that it match the
|
||||
* Windows implementation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Depending on the GKID that was requested, Windows’ GetKey() might
|
||||
* return a different L1 or L2 seed key, leaving the client with some
|
||||
* further derivation to do. Our simpler implementation will return
|
||||
* either the exact key the caller requested, or an error code if the
|
||||
* client is not suitably authorized.
|
||||
*/
|
||||
|
||||
{
|
||||
uint8_t key[GKDI_KEY_LEN];
|
||||
|
||||
status = compute_seed_key(
|
||||
mem_ctx,
|
||||
data_blob_const(gmsa_security_descriptor,
|
||||
sizeof gmsa_security_descriptor),
|
||||
root_key,
|
||||
gkid,
|
||||
key);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = generate_gmsa_password(key,
|
||||
account_sid,
|
||||
root_key->kdf_algorithm,
|
||||
password);
|
||||
ZERO_ARRAY(key);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
gmsa_post_process_password_buffer(password);
|
||||
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS gmsa_talloc_password_based_on_key_id(
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct Gkid gkid,
|
||||
const NTTIME current_time,
|
||||
const struct ProvRootKey *const root_key,
|
||||
const struct dom_sid *const account_sid,
|
||||
struct gmsa_null_terminated_password **password_out)
|
||||
{
|
||||
struct gmsa_null_terminated_password *password = NULL;
|
||||
NTSTATUS status = NT_STATUS_OK;
|
||||
|
||||
if (password_out == NULL) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
password = talloc(mem_ctx, struct gmsa_null_terminated_password);
|
||||
if (password == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = gmsa_password_based_on_key_id(
|
||||
mem_ctx, gkid, current_time, root_key, account_sid, password->buf);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(password);
|
||||
return status;
|
||||
}
|
||||
|
||||
*password_out = password;
|
||||
return status;
|
||||
}
|
||||
|
||||
bool gmsa_current_time(NTTIME *current_time_out)
|
||||
{
|
||||
struct timespec current_timespec;
|
||||
int ret;
|
||||
|
||||
ret = clock_gettime(CLOCK_REALTIME, ¤t_timespec);
|
||||
if (ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*current_time_out = full_timespec_to_nt_time(¤t_timespec);
|
||||
return true;
|
||||
}
|
54
lib/crypto/gmsa.h
Normal file
54
lib/crypto/gmsa.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Group Managed Service Account functions
|
||||
|
||||
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 LIB_CRYPTO_GMSA_H
|
||||
#define LIB_CRYPTO_GMSA_H
|
||||
|
||||
#include "lib/crypto/gkdi.h"
|
||||
|
||||
enum {
|
||||
GMSA_PASSWORD_LEN = 256,
|
||||
GMSA_PASSWORD_NULL_TERMINATED_LEN = GMSA_PASSWORD_LEN + 2,
|
||||
};
|
||||
|
||||
struct gmsa_null_terminated_password {
|
||||
uint8_t buf[GMSA_PASSWORD_NULL_TERMINATED_LEN];
|
||||
};
|
||||
|
||||
struct dom_sid;
|
||||
NTSTATUS gmsa_password_based_on_key_id(
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct Gkid gkid,
|
||||
const NTTIME current_time,
|
||||
const struct ProvRootKey *const root_key,
|
||||
const struct dom_sid *const account_sid,
|
||||
uint8_t password[static const GMSA_PASSWORD_NULL_TERMINATED_LEN]);
|
||||
|
||||
NTSTATUS gmsa_talloc_password_based_on_key_id(
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct Gkid gkid,
|
||||
const NTTIME current_time,
|
||||
const struct ProvRootKey *const root_key,
|
||||
const struct dom_sid *const account_sid,
|
||||
struct gmsa_null_terminated_password **password_out);
|
||||
|
||||
bool gmsa_current_time(NTTIME *current_time_out);
|
||||
|
||||
#endif /* LIB_CRYPTO_GMSA_H */
|
@ -35,6 +35,12 @@ def build(bld):
|
||||
NDR_GKDI
|
||||
''')
|
||||
|
||||
bld.SAMBA_SUBSYSTEM('gmsa',
|
||||
source='gmsa.c',
|
||||
deps='''
|
||||
gkdi
|
||||
''')
|
||||
|
||||
bld.SAMBA_PYTHON('python_crypto',
|
||||
source='py_crypto.c',
|
||||
deps='gnutls talloc LIBCLI_AUTH',
|
||||
|
Loading…
Reference in New Issue
Block a user