1
0
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:
Gary Lockyer 2017-04-04 16:05:08 +12:00 committed by Andrew Bartlett
parent de5299d155
commit 4b49e18c14
2 changed files with 253 additions and 14 deletions

View File

@ -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\)

View File

@ -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;
}