bool pwd_last_set_bypass;
bool pwd_last_set_default;
bool smartcard_reset;
+ const char **userPassword_schemes;
};
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,
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)
{
#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;
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];
* Primary:Kerberos
* Primary:WDigest
* Primary:CLEARTEXT (optional)
+ * Primary:userPassword
* Primary:SambaGPG (optional)
*
* And the 'Packages' package is insert before the last
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
*/
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;
}