mirror of
https://github.com/samba-team/samba.git
synced 2025-03-01 04:58:35 +03:00
password_hash: generate and store Primary:userPassword
Generate sha256 and sha512 password hashes and store them in supplementalCredentials Signed-off-by: Gary Lockyer <gary@catalyst.net.nz> Reviewed-by: Garming Sam <garming@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
parent
de5299d155
commit
4b49e18c14
@ -330,12 +330,3 @@
|
||||
# We currently don't send referrals for LDAP modify of non-replicated attrs
|
||||
^samba4.ldap.rodc.python\(rodc\).__main__.RodcTests.test_modify_nonreplicated.*
|
||||
^samba4.ldap.rodc_rwdc.python.*.__main__.RodcRwdcTests.test_change_password_reveal_on_demand_kerberos
|
||||
# Tests for password hash supplemental credentials, userPassword hashes
|
||||
# Will fail as the implementation has not been written
|
||||
#
|
||||
^samba.tests.password_hash_gpgme.samba.tests.password_hash_gpgme.PassWordHashGpgmeTests.test_userPassword_multiple_hashes\(ad_dc:local\)
|
||||
^samba.tests.password_hash_gpgme.samba.tests.password_hash_gpgme.PassWordHashGpgmeTests.test_userPassword_multiple_hashes_rounds_specified\(ad_dc:local\)
|
||||
^samba.tests.password_hash_fl2008.samba.tests.password_hash_fl2008.PassWordHashFl2008Tests.test_userPassword_cleartext_sha256\(ad_dc_ntvfs:local\)
|
||||
^samba.tests.password_hash_fl2008.samba.tests.password_hash_fl2008.PassWordHashFl2008Tests.test_userPassword_sha512\(ad_dc_ntvfs:local\)
|
||||
^samba.tests.password_hash_fl2003.samba.tests.password_hash_fl2003.PassWordHashFl2003Tests.test_userPassword_cleartext_sha512\(fl2003dc:local\)
|
||||
^samba.tests.password_hash_fl2003.samba.tests.password_hash_fl2003.PassWordHashFl2003Tests.test_userPassword_sha256\(fl2003dc:local\)
|
||||
|
@ -113,6 +113,7 @@ struct ph_context {
|
||||
bool pwd_last_set_bypass;
|
||||
bool pwd_last_set_default;
|
||||
bool smartcard_reset;
|
||||
const char **userPassword_schemes;
|
||||
};
|
||||
|
||||
|
||||
@ -1120,8 +1121,8 @@ static int setup_primary_wdigest(struct setup_password_fields_io *io,
|
||||
DATA_BLOB *nt4dom;
|
||||
} wdigest[] = {
|
||||
/*
|
||||
* See
|
||||
* http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
|
||||
* See 3.1.1.8.11.3.1 WDIGEST_CREDENTIALS Construction
|
||||
* https://msdn.microsoft.com/en-us/library/cc245680.aspx
|
||||
* for what precalculated hashes are supposed to be stored...
|
||||
*
|
||||
* I can't reproduce all values which should contain "Digest" as realm,
|
||||
@ -1417,6 +1418,203 @@ static int setup_primary_wdigest(struct setup_password_fields_io *io,
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
#define SHA_SALT_PERMITTED_CHARS "abcdefghijklmnopqrstuvwxyz" \
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
|
||||
"0123456789./"
|
||||
#define SHA_SALT_SIZE 16
|
||||
#define SHA_256_SCHEME "CryptSHA256"
|
||||
#define SHA_512_SCHEME "CryptSHA512"
|
||||
#define CRYPT "{CRYPT}"
|
||||
#define SHA_ID_LEN 3
|
||||
#define SHA_256_ALGORITHM_ID 5
|
||||
#define SHA_512_ALGORITHM_ID 6
|
||||
#define ROUNDS_PARAMETER "rounds="
|
||||
|
||||
/*
|
||||
* Extract the crypt (3) algorithm number and number of hash rounds from the
|
||||
* supplied scheme string
|
||||
*/
|
||||
static bool parse_scheme(const char *scheme, int *algorithm, int *rounds) {
|
||||
|
||||
const char *rp = NULL; /* Pointer to the 'rounds=' option */
|
||||
char digits[21]; /* digits extracted from the rounds option */
|
||||
int i = 0; /* loop index variable */
|
||||
|
||||
if (strncasecmp(SHA_256_SCHEME, scheme, strlen(SHA_256_SCHEME)) == 0) {
|
||||
*algorithm = SHA_256_ALGORITHM_ID;
|
||||
} else if (strncasecmp(SHA_512_SCHEME, scheme, strlen(SHA_256_SCHEME))
|
||||
== 0) {
|
||||
*algorithm = SHA_512_ALGORITHM_ID;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
rp = strcasestr(scheme, ROUNDS_PARAMETER);
|
||||
if (rp == NULL) {
|
||||
/* No options specified, use crypt default number of rounds */
|
||||
*rounds = 0;
|
||||
return true;
|
||||
}
|
||||
rp += strlen(ROUNDS_PARAMETER);
|
||||
for (i = 0; isdigit(rp[i]) && i < (sizeof(digits) - 1); i++) {
|
||||
digits[i] = rp[i];
|
||||
}
|
||||
digits[i] = '\0';
|
||||
*rounds = atoi(digits);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the password hash specified by scheme, and return it in
|
||||
* hash_value
|
||||
*/
|
||||
static int setup_primary_userPassword_hash(
|
||||
TALLOC_CTX *ctx,
|
||||
struct setup_password_fields_io *io,
|
||||
const char* scheme,
|
||||
struct package_PrimaryUserPasswordValue *hash_value)
|
||||
{
|
||||
struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
|
||||
const char *salt = NULL; /* Randomly generated salt */
|
||||
const char *cmd = NULL; /* command passed to crypt */
|
||||
const char *hash = NULL; /* password hash generated by crypt */
|
||||
struct crypt_data crypt_data; /* working storage used by crypt */
|
||||
int algorithm = 0; /* crypt hash algorithm number */
|
||||
int rounds = 0; /* The number of hash rounds */
|
||||
DATA_BLOB *hash_blob = NULL;
|
||||
TALLOC_CTX *frame = talloc_stackframe();
|
||||
|
||||
/* Genrate a random password salt */
|
||||
salt = generate_random_str_list(frame,
|
||||
SHA_SALT_SIZE,
|
||||
SHA_SALT_PERMITTED_CHARS);
|
||||
if (salt == NULL) {
|
||||
TALLOC_FREE(frame);
|
||||
return ldb_oom(ldb);
|
||||
}
|
||||
|
||||
/* determine the hashing algoritm and number of rounds*/
|
||||
if (!parse_scheme(scheme, &algorithm, &rounds)) {
|
||||
ldb_asprintf_errstring(
|
||||
ldb,
|
||||
"setup_primary_userPassword: Invalid scheme of [%s] "
|
||||
"specified for 'password hash userPassword schemes' in "
|
||||
"samba.conf",
|
||||
scheme);
|
||||
TALLOC_FREE(frame);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
hash_value->scheme = talloc_strdup(ctx, CRYPT);
|
||||
hash_value->scheme_len = strlen(CRYPT) + 1;
|
||||
|
||||
/* generate the id/salt parameter used by crypt */
|
||||
if (rounds) {
|
||||
cmd = talloc_asprintf(frame,
|
||||
"$%d$rounds=%d$%s",
|
||||
algorithm,
|
||||
rounds,
|
||||
salt);
|
||||
} else {
|
||||
cmd = talloc_asprintf(frame, "$%d$%s", algorithm, salt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Relies on the assertion that cleartext_utf8->data is a zero
|
||||
* terminated UTF-8 string
|
||||
*/
|
||||
hash = crypt_r((char *)io->n.cleartext_utf8->data, cmd, &crypt_data);
|
||||
if (hash == NULL) {
|
||||
char buf[1024];
|
||||
ldb_asprintf_errstring(
|
||||
ldb,
|
||||
"setup_primary_userPassword: generation of a %s "
|
||||
"password hash failed: (%s)",
|
||||
scheme,
|
||||
strerror_r(errno, buf, sizeof(buf)));
|
||||
TALLOC_FREE(frame);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
hash_blob = talloc_zero(ctx, DATA_BLOB);
|
||||
|
||||
if (hash_blob == NULL) {
|
||||
TALLOC_FREE(frame);
|
||||
return ldb_oom(ldb);
|
||||
}
|
||||
|
||||
*hash_blob = data_blob_talloc(hash_blob,
|
||||
(const uint8_t *)hash,
|
||||
strlen(hash));
|
||||
if (hash_blob->data == NULL) {
|
||||
TALLOC_FREE(frame);
|
||||
return ldb_oom(ldb);
|
||||
}
|
||||
hash_value->value = hash_blob;
|
||||
TALLOC_FREE(frame);
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the desired extra password hashes
|
||||
*/
|
||||
static int setup_primary_userPassword(
|
||||
struct setup_password_fields_io *io,
|
||||
const struct supplementalCredentialsBlob *old_scb,
|
||||
struct package_PrimaryUserPasswordBlob *p_userPassword_b)
|
||||
{
|
||||
struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
|
||||
TALLOC_CTX *frame = talloc_stackframe();
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Save the current nt_hash, use this to determine if the password
|
||||
* has been changed by windows. Which will invalidate the userPassword
|
||||
* hash. Note once NTLM-Strong-NOWTF becomes available it should be
|
||||
* used in preference to the NT password hash
|
||||
*/
|
||||
if (io->g.nt_hash == NULL) {
|
||||
ldb_asprintf_errstring(ldb,
|
||||
"No NT Hash, unable to calculate userPassword hashes");
|
||||
return LDB_ERR_UNWILLING_TO_PERFORM;
|
||||
}
|
||||
p_userPassword_b->current_nt_hash = *io->g.nt_hash;
|
||||
|
||||
/*
|
||||
* Determine the number of hashes
|
||||
* Note: that currently there is no limit on the number of hashes
|
||||
* no checking is done on the number of schemes specified
|
||||
* or for uniqueness.
|
||||
*/
|
||||
p_userPassword_b->num_hashes = 0;
|
||||
for (i = 0; io->ac->userPassword_schemes[i]; i++) {
|
||||
p_userPassword_b->num_hashes++;
|
||||
}
|
||||
|
||||
p_userPassword_b->hashes
|
||||
= talloc_array(io->ac,
|
||||
struct package_PrimaryUserPasswordValue,
|
||||
p_userPassword_b->num_hashes);
|
||||
if (p_userPassword_b->hashes == NULL) {
|
||||
TALLOC_FREE(frame);
|
||||
return ldb_oom(ldb);
|
||||
}
|
||||
|
||||
for (i = 0; io->ac->userPassword_schemes[i]; i++) {
|
||||
ret = setup_primary_userPassword_hash(
|
||||
p_userPassword_b->hashes,
|
||||
io,
|
||||
io->ac->userPassword_schemes[i],
|
||||
&p_userPassword_b->hashes[i]);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
TALLOC_FREE(frame);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int setup_primary_samba_gpg(struct setup_password_fields_io *io,
|
||||
struct package_PrimarySambaGPGBlob *pgb)
|
||||
{
|
||||
@ -1553,7 +1751,7 @@ static int setup_primary_samba_gpg(struct setup_password_fields_io *io,
|
||||
#endif /* else ENABLE_GPGME */
|
||||
}
|
||||
|
||||
#define NUM_PACKAGES 5
|
||||
#define NUM_PACKAGES 6
|
||||
static int setup_supplemental_field(struct setup_password_fields_io *io)
|
||||
{
|
||||
struct ldb_context *ldb;
|
||||
@ -1561,7 +1759,8 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
|
||||
struct supplementalCredentialsBlob *old_scb = NULL;
|
||||
/*
|
||||
* Packages +
|
||||
* (Kerberos-Newer-Keys, Kerberos, WDigest, CLEARTEXT, SambaGPG)
|
||||
* ( Kerberos-Newer-Keys, Kerberos,
|
||||
* WDigest, CLEARTEXT, userPassword, SambaGPG)
|
||||
*/
|
||||
uint32_t num_names = 0;
|
||||
const char *names[1+NUM_PACKAGES];
|
||||
@ -1611,6 +1810,7 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
|
||||
* Primary:Kerberos
|
||||
* Primary:WDigest
|
||||
* Primary:CLEARTEXT (optional)
|
||||
* Primary:userPassword
|
||||
* Primary:SambaGPG (optional)
|
||||
*
|
||||
* And the 'Packages' package is insert before the last
|
||||
@ -1781,6 +1981,53 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
|
||||
num_packages++;
|
||||
}
|
||||
|
||||
if (io->ac->userPassword_schemes) {
|
||||
/*
|
||||
* setup 'Primary:userPassword' element
|
||||
*/
|
||||
struct package_PrimaryUserPasswordBlob
|
||||
p_userPassword_b;
|
||||
DATA_BLOB p_userPassword_b_blob;
|
||||
char *p_userPassword_b_hexstr;
|
||||
|
||||
names[num_names++] = "userPassword";
|
||||
|
||||
ret = setup_primary_userPassword(io,
|
||||
old_scb,
|
||||
&p_userPassword_b);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ndr_err = ndr_push_struct_blob(
|
||||
&p_userPassword_b_blob,
|
||||
io->ac,
|
||||
&p_userPassword_b,
|
||||
(ndr_push_flags_fn_t)
|
||||
ndr_push_package_PrimaryUserPasswordBlob);
|
||||
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
||||
NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
|
||||
ldb_asprintf_errstring(
|
||||
ldb,
|
||||
"setup_supplemental_field: failed to push "
|
||||
"package_PrimaryUserPasswordBlob: %s",
|
||||
nt_errstr(status));
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
p_userPassword_b_hexstr
|
||||
= data_blob_hex_string_upper(
|
||||
io->ac,
|
||||
&p_userPassword_b_blob);
|
||||
if (!p_userPassword_b_hexstr) {
|
||||
return ldb_oom(ldb);
|
||||
}
|
||||
pp->name = "Primary:userPassword";
|
||||
pp->reserved = 1;
|
||||
pp->data = p_userPassword_b_hexstr;
|
||||
pp++;
|
||||
num_packages++;
|
||||
}
|
||||
|
||||
/*
|
||||
* setup 'Primary:SambaGPG' element
|
||||
*/
|
||||
@ -3441,7 +3688,8 @@ static struct ph_context *ph_init_context(struct ldb_module *module,
|
||||
lp_ctx = talloc_get_type_abort(ldb_get_opaque(ldb, "loadparm"),
|
||||
struct loadparm_context);
|
||||
ac->gpg_key_ids = lpcfg_password_hash_gpg_key_ids(lp_ctx);
|
||||
|
||||
ac->userPassword_schemes
|
||||
= lpcfg_password_hash_userpassword_schemes(lp_ctx);
|
||||
return ac;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user