summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/auth/auth_sam.c1
-rw-r--r--source4/dsdb/samdb/ldb_modules/config.mk12
-rw-r--r--source4/dsdb/samdb/ldb_modules/password_hash.c711
-rw-r--r--source4/dsdb/samdb/samdb.c88
-rw-r--r--source4/kdc/hdb-ldb.c120
-rw-r--r--source4/lib/ldb/common/ldb_modules.c1
-rw-r--r--source4/rpc_server/netlogon/dcerpc_netlogon.c10
-rw-r--r--source4/rpc_server/samr/dcesrv_samr.c6
-rw-r--r--source4/rpc_server/samr/samr_password.c149
-rw-r--r--source4/setup/provision_init.ldif3
10 files changed, 822 insertions, 279 deletions
diff --git a/source4/auth/auth_sam.c b/source4/auth/auth_sam.c
index 15abeab091..a013a2051c 100644
--- a/source4/auth/auth_sam.c
+++ b/source4/auth/auth_sam.c
@@ -35,6 +35,7 @@ static const char *user_attrs[] = {
"userPrincipalName",
"servicePrincipalName",
"msDS-KeyVersionNumber",
+ "krb5Key",
/* passwords */
"unicodePwd",
diff --git a/source4/dsdb/samdb/ldb_modules/config.mk b/source4/dsdb/samdb/ldb_modules/config.mk
index 3d20bff809..f9c267e2db 100644
--- a/source4/dsdb/samdb/ldb_modules/config.mk
+++ b/source4/dsdb/samdb/ldb_modules/config.mk
@@ -50,3 +50,15 @@ OBJ_FILES = \
# End MODULE libldb_rootdse
################################################
+################################################
+# Start MODULE libldb_password_hash
+[MODULE::libldb_password_hash]
+SUBSYSTEM = LIBLDB
+OBJ_FILES = \
+ password_hash.o
+REQUIRED_SUBSYSTEMS = \
+ HEIMDAL_HDB HEIMDAL_KRB5
+#
+# End MODULE libldb_rootdse
+################################################
+
diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c
new file mode 100644
index 0000000000..bb42a0e634
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c
@@ -0,0 +1,711 @@
+/*
+ ldb database module
+
+ Copyright (C) Simo Sorce 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Andrew Tridgell 2004
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb password_hash module
+ *
+ * Description: correctly update hash values based on changes to unicodePwd and friends
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "libcli/ldap/ldap.h"
+#include "ldb/include/ldb_errors.h"
+#include "ldb/include/ldb_private.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "system/time.h"
+#include "dsdb/samdb/samdb.h"
+#include "ads.h"
+#include "hdb.h"
+
+/* If we have decided there is reason to work on this request, then
+ * setup all the password hash types correctly.
+ *
+ * If the administrator doesn't want the unicodePwd stored (set in the
+ * domain and per-account policies) then we must strip that out before
+ * we do the first operation.
+ *
+ * Once this is done (which could update anything at all), we
+ * calculate the password hashes.
+ *
+ * This function must not only update the ntPwdHash, lmPwdHash and
+ * krb5Key fields, it must also atomicly increment the
+ * msDS-KeyVersionNumber. We should be in a transaction, so all this
+ * should be quite safe...
+ *
+ * Finally, if the administrator has requested that a password history
+ * be maintained, then this should also be written out.
+ *
+ */
+
+
+static int password_hash_handle(struct ldb_module *module, struct ldb_request *req,
+ const struct ldb_message *msg)
+{
+ int ret, old_ret = -1;
+ uint_t pwdProperties, pwdHistoryLength;
+ uint_t userAccountControl;
+ const char *dnsDomain, *realm;
+ const char *unicodePwd;
+ struct samr_Password *lmPwdHistory, *ntPwdHistory;
+ struct samr_Password *lmPwdHash, *ntPwdHash;
+ struct samr_Password *lmOldHash = NULL, *ntOldHash = NULL;
+ struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
+ struct samr_Password local_lmNewHash, local_ntNewHash;
+ int lmPwdHistory_len, ntPwdHistory_len;
+ uint_t kvno;
+ struct dom_sid *domain_sid;
+ time_t now = time(NULL);
+ NTTIME now_nt;
+ int i;
+ krb5_error_code krb5_ret;
+
+ struct smb_krb5_context *smb_krb5_context;
+
+ struct ldb_message_element *attribute;
+ struct ldb_dn *dn = msg->dn;
+ struct ldb_message *msg2;
+
+ struct ldb_request search_request;
+ struct ldb_request modify_request;
+ struct ldb_request modified_orig_request;
+ struct ldb_result *res, *dom_res, *old_res;
+
+ struct ldb_message_element *objectclasses;
+ struct ldb_val computer_val;
+ struct ldb_val person_val;
+ BOOL is_computer;
+
+ struct ldb_message *modify_msg;
+
+ const char *domain_expression;
+ const char *old_user_attrs[] = { "lmPwdHash", "ntPwdHash", NULL };
+ const char *user_attrs[] = { "userAccountControl", "lmPwdHistory",
+ "ntPwdHistory",
+ "ntPwdHash",
+ "objectSid", "msDS-KeyVersionNumber",
+ "objectClass", "userPrincipalName",
+ "samAccountName",
+ NULL };
+ const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
+ "dnsDomain", NULL };
+
+ TALLOC_CTX *mem_ctx;
+
+ /* Do the original action */
+
+ /* If no part of this touches the unicodePwd, then we don't
+ * need to make any changes. For password changes/set there should
+ * be a 'delete' or a 'modify' on this attribute. */
+ if ((attribute = ldb_msg_find_element(msg, "unicodePwd")) == NULL ) {
+ return ldb_next_request(module, req);
+ }
+
+ mem_ctx = talloc_new(module);
+ if (!mem_ctx) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (req->operation == LDB_REQ_MODIFY) {
+ /* Look up the old ntPwdHash and lmPwdHash values, so
+ * we can later place these into the password
+ * history */
+
+ search_request.operation = LDB_REQ_SEARCH;
+ search_request.op.search.base = dn;
+ search_request.op.search.scope = LDB_SCOPE_BASE;
+ search_request.op.search.tree = ldb_parse_tree(module->ldb, NULL);
+ search_request.op.search.attrs = old_user_attrs;
+
+ old_ret = ldb_next_request(module, &search_request);
+ }
+
+ /* we can't change things untill we copy it */
+ msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
+
+ /* look again, this time at the copied attribute */
+ if (!msg2 || (attribute = ldb_msg_find_element(msg2, "unicodePwd")) == NULL ) {
+ /* Gah? where did it go? Oh well... */
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Wipe out the unicodePwd attribute set, we will handle it in
+ * the second modify. We might not want it written to disk */
+
+ if (req->operation == LDB_REQ_ADD) {
+ if (attribute->num_values != 1) {
+ ldb_set_errstring(module,
+ talloc_asprintf(mem_ctx, "unicodePwd_handle: "
+ "attempted set of multiple unicodePwd attributes on %s rejected",
+ ldb_dn_linearize(mem_ctx, dn)));
+ return LDB_ERR_CONSTRAINT_VIOLAION;
+ }
+
+ unicodePwd = (const char *)attribute->values[0].data;
+ ldb_msg_remove_attr(msg2, "unicodePwd");
+ } else if (((attribute->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_ADD)
+ || ((attribute->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_REPLACE)) {
+ if (attribute->num_values != 1) {
+ return LDB_ERR_CONSTRAINT_VIOLAION;
+ }
+
+ unicodePwd = (const char *)attribute->values[0].data;
+ ldb_msg_remove_attr(msg2, "unicodePwd");
+ } else {
+ unicodePwd = NULL;
+ }
+
+ modified_orig_request = *req;
+ switch (modified_orig_request.operation) {
+ case LDB_REQ_ADD:
+ modified_orig_request.op.add.message = msg2;
+ break;
+ case LDB_REQ_MODIFY:
+ modified_orig_request.op.mod.message = msg2;
+ break;
+ }
+
+ /* Send the (modified) request of the original caller down to the database */
+ ret = ldb_next_request(module, &modified_orig_request);
+ if (ret) {
+ return ret;
+ }
+
+ /* While we do the search first (for the old password hashes),
+ * we don't want to override any error that the modify may
+ * have returned. Now check the error */
+ if (req->operation == LDB_REQ_MODIFY) {
+ if (old_ret) {
+ talloc_free(mem_ctx);
+ return old_ret;
+ }
+
+ /* Find out the old passwords details of the user */
+ old_res = search_request.op.search.res;
+
+ if (old_res->count != 1) {
+ ldb_set_errstring(module,
+ talloc_asprintf(mem_ctx, "password_hash_handle: "
+ "(pre) search for %s found %d != 1 objects, for entry we just modified",
+ ldb_dn_linearize(mem_ctx, dn),
+ old_res->count));
+ /* What happend? The above add/modify worked... */
+ talloc_free(mem_ctx);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ lmOldHash = samdb_result_hash(mem_ctx, old_res->msgs[0], "lmPwdHash");
+ ntOldHash = samdb_result_hash(mem_ctx, old_res->msgs[0], "ntPwdHash");
+ }
+
+ /* Start finding out details we need for the second modify.
+ * We do this after the first add/modify because other modules
+ * will have filled in the templates, and we may have had
+ * things like the username (affecting the salt) changed along
+ * with the password. */
+
+ /* Now find out what is on the entry after the above add/modify */
+ search_request.operation = LDB_REQ_SEARCH;
+ search_request.op.search.base = dn;
+ search_request.op.search.scope = LDB_SCOPE_BASE;
+ search_request.op.search.tree = ldb_parse_tree(module->ldb, NULL);
+ search_request.op.search.attrs = user_attrs;
+
+ ret = ldb_next_request(module, &search_request);
+ if (ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* Find out the full details of the user */
+ res = search_request.op.search.res;
+ if (res->count != 1) {
+ ldb_set_errstring(module,
+ talloc_asprintf(mem_ctx, "password_hash_handle: "
+ "search for %s found %d != 1 objects, for entry we just added/modified",
+ ldb_dn_linearize(mem_ctx, dn),
+ res->count));
+ /* What happend? The above add/modify worked... */
+ talloc_free(mem_ctx);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ userAccountControl = samdb_result_uint(res->msgs[0], "userAccountControl", 0);
+ lmPwdHistory_len = samdb_result_hashes(mem_ctx, res->msgs[0],
+ "lmPwdHistory", &lmPwdHistory);
+ ntPwdHistory_len = samdb_result_hashes(mem_ctx, res->msgs[0],
+ "ntPwdHistory", &ntPwdHistory);
+ ntPwdHash = samdb_result_hash(mem_ctx, res->msgs[0], "ntPwdHash");
+ kvno = samdb_result_uint(res->msgs[0], "msDS-KeyVersionNumber", 0);
+
+ domain_sid = samdb_result_sid_prefix(mem_ctx, res->msgs[0], "objectSid");
+
+
+ objectclasses = ldb_msg_find_element(res->msgs[0], "objectClass");
+ person_val = data_blob_string_const("person");
+
+ if (!objectclasses || !ldb_msg_find_val(objectclasses, &person_val)) {
+ /* Not a 'person', so the rest of this doesn't make
+ * sense. How we got a unicodePwd this far I don't
+ * know... */
+ ldb_set_errstring(module,
+ talloc_asprintf(mem_ctx, "password_hash_handle: "
+ "attempted set of unicodePwd on non-'person' object %s rejected",
+ ldb_dn_linearize(mem_ctx, dn)));
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLAION;
+ }
+
+ computer_val = data_blob_string_const("computer");
+
+ if (ldb_msg_find_val(objectclasses, &computer_val)) {
+ is_computer = True;
+ } else {
+ is_computer = False;
+ }
+
+ domain_expression = talloc_asprintf(mem_ctx, "(&(objectSid=%s)(objectClass=domain))",
+ ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
+
+ /* Find the user's domain, then find out the domain password
+ * properties */
+ ret = ldb_search(module->ldb, NULL, LDB_SCOPE_SUBTREE, domain_expression,
+ domain_attrs, &dom_res);
+ if (ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ if (dom_res->count != 1) {
+ /* What happend? The user we are modifying must be odd... */
+ ldb_set_errstring(module,
+ talloc_asprintf(mem_ctx, "password_hash_handle: "
+ "search for domain %s found %d != 1 objects",
+ dom_sid_string(mem_ctx, domain_sid),
+ dom_res->count));
+ talloc_free(mem_ctx);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ pwdProperties = samdb_result_uint(dom_res->msgs[0], "pwdProperties", 0);
+ pwdHistoryLength = samdb_result_uint(dom_res->msgs[0], "pwdHistoryLength", 0);
+ dnsDomain = ldb_msg_find_string(dom_res->msgs[0], "dnsDomain", NULL);
+ realm = strupper_talloc(mem_ctx, dnsDomain);
+
+ /* Some operations below require kerberos contexts */
+ if (smb_krb5_init_context(mem_ctx, &smb_krb5_context) != 0) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare the modifications to set all the hash/key types */
+ modify_msg = ldb_msg_new(req);
+ modify_msg->dn = talloc_reference(modify_msg, dn);
+
+#define CHECK_RET(x) \
+ do { \
+ int check_ret = x; \
+ if (check_ret != LDB_SUCCESS) { \
+ talloc_free(mem_ctx); \
+ return check_ret; \
+ } \
+ } while(0)
+
+ /* Setup krb5Key (we want to either delete an existing value,
+ * or replace with a new one). Both the unicode and NT hash
+ * only branches append keys to this multivalued entry. */
+ CHECK_RET(ldb_msg_add_empty(modify_msg, "krb5Key", LDB_FLAG_MOD_REPLACE));
+ /* Yay, we can compute new password hashes from the unicode
+ * password */
+ if (unicodePwd) {
+ Principal *salt_principal;
+ const char *user_principal_name = ldb_msg_find_string(res->msgs[0], "userPrincipalName", NULL);
+
+ Key *keys;
+ size_t num_keys;
+
+ /* compute the new nt and lm hashes */
+ if (E_deshash(unicodePwd, local_lmNewHash.hash)) {
+ lmPwdHash = &local_lmNewHash;
+ } else {
+ lmPwdHash = NULL;
+ }
+ E_md4hash(unicodePwd, local_ntNewHash.hash);
+ ntPwdHash = &local_ntNewHash;
+ CHECK_RET(ldb_msg_add_empty(modify_msg, "ntPwdHash",
+ LDB_FLAG_MOD_REPLACE));
+ CHECK_RET(samdb_msg_add_hash(module->ldb, req,
+ modify_msg, "ntPwdHash",
+ ntPwdHash));
+ CHECK_RET(ldb_msg_add_empty(modify_msg, "lmPwdHash",
+ LDB_FLAG_MOD_REPLACE));
+ if (lmPwdHash) {
+ CHECK_RET(samdb_msg_add_hash(module->ldb, req,
+ modify_msg, "lmPwdHash",
+ lmPwdHash));
+ }
+
+ /* Many, many thanks to lukeh@padl.com for this
+ * algorithm, described in his Nov 10 2004 mail to
+ * samba-technical@samba.org */
+
+ if (is_computer) {
+ /* Determine a salting principal */
+ char *samAccountName = talloc_strdup(mem_ctx, ldb_msg_find_string(res->msgs[0], "samAccountName", NULL));
+ char *saltbody;
+ if (!samAccountName) {
+ ldb_set_errstring(module,
+ talloc_asprintf(mem_ctx, "password_hash_handle: "
+ "generation of new kerberos keys failed: %s is a computer without a samAccountName",
+ ldb_dn_linearize(mem_ctx, dn)));
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (samAccountName[strlen(samAccountName)-1] == '$') {
+ samAccountName[strlen(samAccountName)-1] = '\0';
+ }
+ saltbody = talloc_asprintf(mem_ctx, "%s.%s", samAccountName, dnsDomain);
+
+ krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context, &salt_principal, realm, "host", saltbody, NULL);
+ } else if (user_principal_name) {
+ char *p;
+ user_principal_name = talloc_strdup(mem_ctx, user_principal_name);
+ if (!user_principal_name) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ } else {
+ p = strchr(user_principal_name, '@');
+ if (p) {
+ p[0] = '\0';
+ }
+ krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context, &salt_principal, realm, user_principal_name, NULL);
+ }
+ } else {
+ const char *samAccountName = ldb_msg_find_string(res->msgs[0], "samAccountName", NULL);
+ if (!samAccountName) {
+ ldb_set_errstring(module,
+ talloc_asprintf(mem_ctx, "password_hash_handle: "
+ "generation of new kerberos keys failed: %s has no samAccountName",
+ ldb_dn_linearize(mem_ctx, dn)));
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ krb5_ret = krb5_make_principal(smb_krb5_context->krb5_context, &salt_principal, realm, samAccountName, NULL);
+ }
+
+
+ if (krb5_ret) {
+ ldb_set_errstring(module,
+ talloc_asprintf(mem_ctx, "password_hash_handle: "
+ "generation of a saltking principal failed: %s",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ krb5_ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* TODO: We may wish to control the encryption types chosen in future */
+ krb5_ret = hdb_generate_key_set_password(smb_krb5_context->krb5_context,
+ salt_principal, unicodePwd, &keys, &num_keys);
+ krb5_free_principal(smb_krb5_context->krb5_context, salt_principal);
+
+ if (krb5_ret) {
+ ldb_set_errstring(module,
+ talloc_asprintf(mem_ctx, "password_hash_handle: "
+ "generation of new kerberos keys failed: %s",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ krb5_ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Walking
+ */
+ for (i=0; i < num_keys; i++) {
+ unsigned char *buf;
+ size_t buf_size;
+ size_t len;
+ struct ldb_val val;
+
+ if (keys[i].key.keytype == ENCTYPE_ARCFOUR_HMAC) {
+ /* We might end up doing this below:
+ * This ensures we get the unicode
+ * conversion right. This should also
+ * be fixed in the Heimdal libs */
+ continue;
+ }
+ ASN1_MALLOC_ENCODE(Key, buf, buf_size, &keys[i], &len, krb5_ret);
+
+ val.data = talloc_memdup(req, buf, len);
+ val.length = len;
+ free(buf);
+ if (!val.data || krb5_ret) {
+ hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_msg_add_value(modify_msg, "krb5Key", &val);
+ if (ret != LDB_SUCCESS) {
+ hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ }
+
+ hdb_free_keys (smb_krb5_context->krb5_context, num_keys, keys);
+ }
+
+ /* Possibly kill off the cleartext or store it */
+ CHECK_RET(ldb_msg_add_empty(modify_msg, "unicodePwd", LDB_FLAG_MOD_REPLACE));
+
+ if (unicodePwd && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
+ (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
+ CHECK_RET(ldb_msg_add_string(modify_msg, "unicodePwd", unicodePwd));
+ }
+
+ /* Even if we didn't get a unicodePwd, we can still setup
+ * krb5Key from the NT hash.
+ *
+ * This is an append, so it works with the 'continue' in the
+ * unicode loop above, to use Samba's NT hash function, which
+ * is more correct than Heimdal's
+ */
+ if (ntPwdHash) {
+ unsigned char *buf;
+ size_t buf_size;
+ size_t len;
+ struct ldb_val val;
+ Key key;
+
+ key.mkvno = 0;
+ key.salt = NULL; /* No salt for this enc type */
+
+ krb5_ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
+ ENCTYPE_ARCFOUR_HMAC,
+ ntPwdHash->hash, sizeof(ntPwdHash->hash),
+ &key.key);
+ if (krb5_ret) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ASN1_MALLOC_ENCODE(Key, buf, buf_size, &key, &len, krb5_ret);
+ krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
+ &key.key);
+
+ val.data = talloc_memdup(req, buf, len);
+ val.length = len;
+ free(buf);
+ if (!val.data || ret) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ CHECK_RET(ldb_msg_add_value(modify_msg, "krb5Key", &val));
+ }
+
+ /* If the original caller did anything with pwdLastSet then skip this. It could be an incoming samsync */
+ if ((attribute = ldb_msg_find_element(msg, "pwdLastSet")) == NULL ) {
+ /* Update the password last set time */
+ unix_to_nt_time(&now_nt, now);
+ CHECK_RET(ldb_msg_add_empty(modify_msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE));
+ CHECK_RET(samdb_msg_add_uint64(module->ldb, mem_ctx, modify_msg, "pwdLastSet", now_nt));
+ }
+
+ /* If the original caller did anything with "msDS-KeyVersionNumber" then skip this. It could be an incoming samsync */
+ if ((attribute = ldb_msg_find_element(msg, "msDS-KeyVersionNumber")) == NULL ) {
+ if (kvno == 0) {
+ CHECK_RET(ldb_msg_add_empty(modify_msg, "msDS-KeyVersionNumber",
+ LDB_FLAG_MOD_REPLACE));
+ CHECK_RET(samdb_msg_add_uint(module->ldb, mem_ctx, modify_msg, "msDS-KeyVersionNumber", kvno + 1));
+ } else {
+ /* While we should be in a transaction, go one extra
+ * step in the dance for an 'atomic' increment. This
+ * may be of value against remote LDAP servers. (Note
+ * however that Mulitmaster replication stil offers no
+ * such guarantee) */
+
+ struct ldb_val old_kvno, new_kvno;
+ old_kvno.data = (uint8_t *)talloc_asprintf(mem_ctx, "%u", kvno);
+ if (!old_kvno.data) {
+ return -1;
+ }
+ old_kvno.length = strlen((char *)old_kvno.data);
+
+ new_kvno.data = (uint8_t *)talloc_asprintf(mem_ctx, "%u", kvno + 1);
+ if (!new_kvno.data) {
+ return -1;
+ }
+ new_kvno.length = strlen((char *)new_kvno.data);
+
+ CHECK_RET(ldb_msg_add_empty(modify_msg, "msDS-KeyVersionNumber",
+ LDB_FLAG_MOD_DELETE));
+ CHECK_RET(ldb_msg_add_empty(modify_msg, "msDS-KeyVersionNumber",
+ LDB_FLAG_MOD_ADD));
+ modify_msg->elements[modify_msg->num_elements - 2].num_values = 1;
+ modify_msg->elements[modify_msg->num_elements - 2].values = &old_kvno;
+ modify_msg->elements[modify_msg->num_elements - 1].num_values = 1;
+ modify_msg->elements[modify_msg->num_elements - 1].values = &new_kvno;
+ }
+ }
+
+ CHECK_RET(ldb_msg_add_empty(modify_msg, "lmPwdHistory",
+ LDB_FLAG_MOD_REPLACE));
+ CHECK_RET(ldb_msg_add_empty(modify_msg, "ntPwdHistory",
+ LDB_FLAG_MOD_REPLACE));
+
+ /* If we have something to put into the history, or an old
+ * history element to expire, update the history */
+ if (pwdHistoryLength > 0 &&
+ ((ntPwdHistory_len > 0) || (lmPwdHistory_len > 0)
+ || lmOldHash || ntOldHash)) {
+ /* store the password history */
+ new_lmPwdHistory = talloc_array(mem_ctx, struct samr_Password,
+ pwdHistoryLength);
+ if (!new_lmPwdHistory) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ new_ntPwdHistory = talloc_array(mem_ctx, struct samr_Password,
+ pwdHistoryLength);
+ if (!new_ntPwdHistory) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
+ new_lmPwdHistory[i+1] = lmPwdHistory[i];
+ }
+ for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
+ new_ntPwdHistory[i+1] = ntPwdHistory[i];
+ }
+
+ /* Don't store 'long' passwords in the LM history,
+ but make sure to 'expire' one password off the other end */
+ if (lmOldHash) {
+ new_lmPwdHistory[0] = *lmOldHash;
+ } else {
+ ZERO_STRUCT(new_lmPwdHistory[0]);
+ }
+ lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
+
+ /* Likewise, we might not have a new NT password (lm
+ * only password change function) */
+ if (ntOldHash) {
+ new_ntPwdHistory[0] = *ntOldHash;
+ } else {
+ ZERO_STRUCT(new_ntPwdHistory[0]);
+ }
+ ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
+
+ CHECK_RET(samdb_msg_add_hashes(module->ldb, mem_ctx, modify_msg,
+ "lmPwdHistory",
+ new_lmPwdHistory,
+ lmPwdHistory_len));
+
+ CHECK_RET(samdb_msg_add_hashes(module->ldb, mem_ctx, modify_msg,
+ "ntPwdHistory",
+ new_ntPwdHistory,
+ ntPwdHistory_len));
+ }
+
+ /* Too much code above, we should check we got it close to reasonable */
+ CHECK_RET(ldb_msg_sanity_check(modify_msg));
+
+ modify_request.operation = LDB_REQ_MODIFY;
+ modify_request.op.mod.message = modify_msg;
+
+ ret = ldb_next_request(module, &modify_request);
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/* add_record: do things with the unicodePwd attribute */
+static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
+{
+ const struct ldb_message *msg = req->op.add.message;
+
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_add_record\n");
+
+ if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ return password_hash_handle(module, req, msg);
+}
+
+/* modify_record: do things with the unicodePwd attribute */
+static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ const struct ldb_message *msg = req->op.mod.message;
+
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "password_hash_modify_record\n");
+
+ if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ return password_hash_handle(module, req, msg);
+}
+
+static int password_hash_request(struct ldb_module *module, struct ldb_request *req)
+{
+ switch (req->operation) {
+
+ case LDB_REQ_ADD:
+ return password_hash_add(module, req);
+
+ case LDB_REQ_MODIFY:
+ return password_hash_modify(module, req);
+
+ default:
+ return ldb_next_request(module, req);
+
+ }
+}
+
+static const struct ldb_module_ops password_hash_ops = {
+ .name = "password_hash",
+ .request = password_hash_request
+};
+
+
+/* the init function */
+#ifdef HAVE_DLOPEN_DISABLED
+ struct ldb_module *init_module(struct ldb_context *ldb, const char *options[])
+#else
+struct ldb_module *password_hash_module_init(struct ldb_context *ldb, const char *options[])
+#endif
+{
+ struct ldb_module *ctx;
+
+ ctx = talloc(ldb, struct ldb_module);
+ if (!ctx)
+ return NULL;
+
+ ctx->private_data = NULL;
+ ctx->ldb = ldb;
+ ctx->prev = ctx->next = NULL;
+ ctx->ops = &password_hash_ops;
+
+ return ctx;
+}
diff --git a/source4/dsdb/samdb/samdb.c b/source4/dsdb/samdb/samdb.c
index b6a2f9ce33..3d5535602b 100644
--- a/source4/dsdb/samdb/samdb.c
+++ b/source4/dsdb/samdb/samdb.c
@@ -510,13 +510,13 @@ NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb,
/*
pull a samr_Password structutre from a result set.
*/
-struct samr_Password samdb_result_hash(struct ldb_message *msg, const char *attr)
+struct samr_Password *samdb_result_hash(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
{
- struct samr_Password hash;
+ struct samr_Password *hash = NULL;
const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
- ZERO_STRUCT(hash);
- if (val) {
- memcpy(hash.hash, val->data, MIN(val->length, sizeof(hash.hash)));
+ if (val && (val->length >= sizeof(hash->hash))) {
+ hash = talloc(mem_ctx, struct samr_Password);
+ memcpy(hash->hash, val->data, MIN(val->length, sizeof(hash->hash)));
}
return hash;
}
@@ -555,62 +555,28 @@ uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
struct samr_Password **lm_pwd, struct samr_Password **nt_pwd)
{
-
- const char *unicodePwd = samdb_result_string(msg, "unicodePwd", NULL);
-
struct samr_Password *lmPwdHash, *ntPwdHash;
- if (unicodePwd) {
- if (nt_pwd) {
- ntPwdHash = talloc(mem_ctx, struct samr_Password);
- if (!ntPwdHash) {
- return NT_STATUS_NO_MEMORY;
- }
-
- E_md4hash(unicodePwd, ntPwdHash->hash);
- *nt_pwd = ntPwdHash;
- }
-
- if (lm_pwd) {
- BOOL lm_hash_ok;
-
- lmPwdHash = talloc(mem_ctx, struct samr_Password);
- if (!lmPwdHash) {
- return NT_STATUS_NO_MEMORY;
- }
-
- /* compute the new nt and lm hashes */
- lm_hash_ok = E_deshash(unicodePwd, lmPwdHash->hash);
-
- if (lm_hash_ok) {
- *lm_pwd = lmPwdHash;
- } else {
- *lm_pwd = NULL;
- }
+ if (nt_pwd) {
+ int num_nt;
+ num_nt = samdb_result_hashes(mem_ctx, msg, "ntPwdHash", &ntPwdHash);
+ if (num_nt == 0) {
+ *nt_pwd = NULL;
+ } else if (num_nt > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ *nt_pwd = &ntPwdHash[0];
}
- } else {
- if (nt_pwd) {
- int num_nt;
- num_nt = samdb_result_hashes(mem_ctx, msg, "ntPwdHash", &ntPwdHash);
- if (num_nt == 0) {
- *nt_pwd = NULL;
- } else if (num_nt > 1) {
- return NT_STATUS_INTERNAL_DB_CORRUPTION;
- } else {
- *nt_pwd = &ntPwdHash[0];
- }
- }
- if (lm_pwd) {
- int num_lm;
- num_lm = samdb_result_hashes(mem_ctx, msg, "lmPwdHash", &lmPwdHash);
- if (num_lm == 0) {
- *lm_pwd = NULL;
- } else if (num_lm > 1) {
- return NT_STATUS_INTERNAL_DB_CORRUPTION;
- } else {
- *lm_pwd = &lmPwdHash[0];
- }
+ }
+ if (lm_pwd) {
+ int num_lm;
+ num_lm = samdb_result_hashes(mem_ctx, msg, "lmPwdHash", &lmPwdHash);
+ if (num_lm == 0) {
+ *lm_pwd = NULL;
+ } else if (num_lm > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ *lm_pwd = &lmPwdHash[0];
}
-
}
return NT_STATUS_OK;
}
@@ -729,13 +695,9 @@ int samdb_msg_add_dom_sid(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, stru
int samdb_msg_add_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
const char *attr_name)
{
- char *a = talloc_strdup(mem_ctx, attr_name);
- if (a == NULL) {
- return -1;
- }
/* we use an empty replace rather than a delete, as it allows for
samdb_replace() to be used everywhere */
- return ldb_msg_add_empty(msg, a, LDB_FLAG_MOD_REPLACE);
+ return ldb_msg_add_empty(msg, attr_name, LDB_FLAG_MOD_REPLACE);
}
/*
diff --git a/source4/kdc/hdb-ldb.c b/source4/kdc/hdb-ldb.c
index 5a3d9c25e7..0e087e1d77 100644
--- a/source4/kdc/hdb-ldb.c
+++ b/source4/kdc/hdb-ldb.c
@@ -54,9 +54,7 @@ static const char * const krb5_attrs[] = {
"userPrincipalName",
"servicePrincipalName",
- "unicodePwd",
- "lmPwdHash",
- "ntPwdHash",
+ "krb5Key",
"userAccountControl",
@@ -222,7 +220,6 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db,
struct ldb_message *realm_ref_msg,
hdb_entry_ex *entry_ex)
{
- const char *unicodePwd;
unsigned int userAccountControl;
int i;
krb5_error_code ret = 0;
@@ -233,6 +230,7 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db,
struct hdb_ldb_private *private;
NTTIME acct_expiry;
+ struct ldb_message_element *ldb_keys;
struct ldb_message_element *objectclasses;
struct ldb_val computer_val;
@@ -382,106 +380,28 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db,
entry_ex->entry.generation = NULL;
- /* create the keys and enctypes */
- unicodePwd = ldb_msg_find_string(msg, "unicodePwd", NULL);
- if (unicodePwd) {
- /* Many, many thanks to lukeh@padl.com for this
- * algorithm, described in his Nov 10 2004 mail to
- * samba-technical@samba.org */
-
- Principal *salt_principal;
- const char *user_principal_name = ldb_msg_find_string(msg, "userPrincipalName", NULL);
- if (is_computer) {
- /* Determine a salting principal */
- char *samAccountName = talloc_strdup(mem_ctx, ldb_msg_find_string(msg, "samAccountName", NULL));
- char *saltbody;
- if (!samAccountName) {
- krb5_set_error_string(context, "LDB_message2entry: no samAccountName present");
- ret = ENOENT;
- goto out;
- }
- if (samAccountName[strlen(samAccountName)-1] == '$') {
- samAccountName[strlen(samAccountName)-1] = '\0';
- }
- saltbody = talloc_asprintf(mem_ctx, "%s.%s", samAccountName, dnsdomain);
-
- ret = krb5_make_principal(context, &salt_principal, realm, "host", saltbody, NULL);
- } else if (user_principal_name) {
- char *p;
- user_principal_name = talloc_strdup(mem_ctx, user_principal_name);
- if (!user_principal_name) {
- ret = ENOMEM;
- goto out;
- } else {
- p = strchr(user_principal_name, '@');
- if (p) {
- p[0] = '\0';
- }
- ret = krb5_make_principal(context, &salt_principal, realm, user_principal_name, NULL);
- }
- } else {
- const char *samAccountName = ldb_msg_find_string(msg, "samAccountName", NULL);
- ret = krb5_make_principal(context, &salt_principal, realm, samAccountName, NULL);
- }
+ /* Get krb5Key from the db */
- if (ret == 0) {
- size_t num_keys = entry_ex->entry.keys.len;
- /*
- * create keys from unicodePwd
- */
- ret = hdb_generate_key_set_password(context, salt_principal,
- unicodePwd,
- &entry_ex->entry.keys.val, &num_keys);
- entry_ex->entry.keys.len = num_keys;
- krb5_free_principal(context, salt_principal);
- }
+ ldb_keys = ldb_msg_find_element(msg, "krb5Key");
- if (ret != 0) {
- krb5_warnx(context, "could not generate keys from unicodePwd\n");
- entry_ex->entry.keys.val = NULL;
- entry_ex->entry.keys.len = 0;
- goto out;
- }
- } else {
- const struct ldb_val *val;
- krb5_data keyvalue;
-
- val = ldb_msg_find_ldb_val(msg, "ntPwdHash");
- if (!val) {
- krb5_warnx(context, "neither type of key available for this account\n");
- entry_ex->entry.keys.val = NULL;
- entry_ex->entry.keys.len = 0;
- } else if (val->length < 16) {
- entry_ex->entry.keys.val = NULL;
- entry_ex->entry.keys.len = 0;
- krb5_warnx(context, "ntPwdHash has invalid length: %d\n",
- (int)val->length);
- } else {
- ret = krb5_data_alloc (&keyvalue, 16);
- if (ret) {
- krb5_clear_error_string(context);
- ret = ENOMEM;
- goto out;
- }
-
- memcpy(keyvalue.data, val->data, 16);
+ /* allocate space to decode into */
+ entry_ex->entry.keys.val = calloc(ldb_keys->num_values, sizeof(Key));
+ if (entry_ex->entry.keys.val == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ entry_ex->entry.keys.len = ldb_keys->num_values;
- entry_ex->entry.keys.val = malloc(sizeof(entry_ex->entry.keys.val[0]));
- if (entry_ex->entry.keys.val == NULL) {
- krb5_data_free(&keyvalue);
- krb5_clear_error_string(context);
- ret = ENOMEM;
- goto out;
- }
-
- memset(&entry_ex->entry.keys.val[0], 0, sizeof(Key));
- entry_ex->entry.keys.val[0].key.keytype = ETYPE_ARCFOUR_HMAC_MD5;
- entry_ex->entry.keys.val[0].key.keyvalue = keyvalue;
-
- entry_ex->entry.keys.len = 1;
+ /* Decode Kerberos keys into the hdb structure */
+ for (i=0; i < entry_ex->entry.keys.len; i++) {
+ size_t decode_len;
+ ret = decode_Key(ldb_keys->values[i].data, ldb_keys->values[i].length,
+ &entry_ex->entry.keys.val[i], &decode_len);
+ if (ret) {
+ /* Could be bougus data in the entry, or out of memory */
+ goto out;
}
- }
-
+ }
entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes)));
if (entry_ex->entry.etypes == NULL) {
diff --git a/source4/lib/ldb/common/ldb_modules.c b/source4/lib/ldb/common/ldb_modules.c
index 4c483b7afb..8076dbfdf9 100644
--- a/source4/lib/ldb/common/ldb_modules.c
+++ b/source4/lib/ldb/common/ldb_modules.c
@@ -136,6 +136,7 @@ int ldb_load_modules(struct ldb_context *ldb, const char *options[])
{ "samba3sam", ldb_samba3sam_module_init },
{ "proxy", proxy_module_init },
{ "rootdse", rootdse_module_init },
+ { "password_hash", password_hash_module_init },
#endif
{ NULL, NULL }
};
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index 335cd3d9e7..cc5937060a 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -136,7 +136,7 @@ static NTSTATUS netr_ServerAuthenticate3(struct dcesrv_call_state *dce_call, TAL
int num_records;
struct ldb_message **msgs;
NTSTATUS nt_status;
- const char *attrs[] = {"unicodePwd", "lmPwdHash", "ntPwdHash", "userAccountControl",
+ const char *attrs[] = {"ntPwdHash", "userAccountControl",
"objectSid", NULL};
ZERO_STRUCTP(r->out.credentials);
@@ -197,11 +197,11 @@ static NTSTATUS netr_ServerAuthenticate3(struct dcesrv_call_state *dce_call, TAL
return NT_STATUS_ACCESS_DENIED;
}
+ *r->out.rid = samdb_result_rid_from_sid(mem_ctx, msgs[0],
+ "objectSid", 0);
- *r->out.rid = samdb_result_rid_from_sid(mem_ctx, msgs[0], "objectSid", 0);
-
- nt_status = samdb_result_passwords(mem_ctx, msgs[0], NULL, &mach_pwd);
- if (!NT_STATUS_IS_OK(nt_status) || mach_pwd == NULL) {
+ mach_pwd = samdb_result_hash(mem_ctx, msgs[0], "ntPwdHash");
+ if (mach_pwd == NULL) {
return NT_STATUS_ACCESS_DENIED;
}
diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c
index b7d770af84..e9787eb041 100644
--- a/source4/rpc_server/samr/dcesrv_samr.c
+++ b/source4/rpc_server/samr/dcesrv_samr.c
@@ -877,7 +877,7 @@ static NTSTATUS samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX
return NT_STATUS_NO_MEMORY;
}
- msg->dn = a_state->account_dn;
+ msg->dn = ldb_dn_copy(msg, a_state->account_dn);
if (samdb_msg_add_uint(a_state->sam_ctx, mem_ctx, msg,
"userAccountControl",
@@ -2880,6 +2880,10 @@ static NTSTATUS samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX
/* modify the samdb record */
ret = samdb_replace(a_state->sam_ctx, mem_ctx, msg);
if (ret != 0) {
+ DEBUG(1,("Failed to modify record %s: %s\n",
+ ldb_dn_linearize(mem_ctx, a_state->account_dn),
+ ldb_errstring(a_state->sam_ctx)));
+
/* we really need samdb.c to return NTSTATUS */
return NT_STATUS_UNSUCCESSFUL;
}
diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c
index 3831be10af..e8bb8cc66a 100644
--- a/source4/rpc_server/samr/samr_password.c
+++ b/source4/rpc_server/samr/samr_password.c
@@ -48,7 +48,7 @@ NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX
struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
struct samr_Password *lm_pwd, *nt_pwd;
NTSTATUS status = NT_STATUS_OK;
- const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , "unicodePwd", NULL };
+ const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , NULL };
DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
@@ -156,14 +156,17 @@ NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX
* makes the write to the database. */
ret = samdb_replace(sam_ctx, mem_ctx, msg);
if (ret != 0) {
+ DEBUG(1,("Failed to modify record to change password on %s: %s\n",
+ ldb_dn_linearize(mem_ctx, a_state->account_dn),
+ ldb_errstring(sam_ctx)));
ldb_transaction_cancel(sam_ctx);
- return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
/* And this confirms it in a transaction commit */
ret = ldb_transaction_commit(sam_ctx);
if (ret != 0) {
- DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
+ DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
ldb_dn_linearize(mem_ctx, a_state->account_dn),
ldb_errstring(sam_ctx)));
return NT_STATUS_INTERNAL_DB_CORRUPTION;
@@ -186,7 +189,7 @@ NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_
const struct ldb_dn *user_dn;
int ret;
struct ldb_message **res, *mod;
- const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
+ const char * const attrs[] = { "objectSid", "lmPwdHash", NULL };
struct samr_Password *lm_pwd;
DATA_BLOB lm_pwd_blob;
uint8_t new_lm_hash[16];
@@ -285,8 +288,11 @@ NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_
* makes the write to the database. */
ret = samdb_replace(sam_ctx, mem_ctx, mod);
if (ret != 0) {
+ DEBUG(1,("Failed to modify record to change password on %s: %s\n",
+ ldb_dn_linearize(mem_ctx, user_dn),
+ ldb_errstring(sam_ctx)));
ldb_transaction_cancel(sam_ctx);
- return NT_STATUS_UNSUCCESSFUL;
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
/* And this confirms it in a transaction commit */
@@ -316,7 +322,7 @@ NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
const struct ldb_dn *user_dn;
int ret;
struct ldb_message **res, *mod;
- const char * const attrs[] = { "ntPwdHash", "lmPwdHash", "unicodePwd", NULL };
+ const char * const attrs[] = { "ntPwdHash", "lmPwdHash", NULL };
struct samr_Password *nt_pwd, *lm_pwd;
DATA_BLOB nt_pwd_blob;
struct samr_DomInfo1 *dominfo = NULL;
@@ -526,22 +532,20 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
struct samr_DomInfo1 **_dominfo)
{
const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
- "ntPwdHistory", "unicodePwd",
- "lmPwdHash", "ntPwdHash", "badPwdCount",
- "objectSid", NULL };
+ "ntPwdHistory",
+ "lmPwdHash", "ntPwdHash",
+ "objectSid",
+ "pwdLastSet", NULL };
const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
"maxPwdAge", "minPwdAge",
- "minPwdLength", "pwdLastSet", NULL };
- const char *unicodePwd;
+ "minPwdLength", NULL };
NTTIME pwdLastSet;
int64_t minPwdAge;
uint_t minPwdLength, pwdProperties, pwdHistoryLength;
- uint_t userAccountControl, badPwdCount;
- struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
- struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
+ uint_t userAccountControl;
+ struct samr_Password *lmPwdHistory, *ntPwdHistory, *lmPwdHash, *ntPwdHash;
struct samr_Password local_lmNewHash, local_ntNewHash;
int lmPwdHistory_len, ntPwdHistory_len;
- uint_t kvno;
struct dom_sid *domain_sid;
struct ldb_message **res;
int count;
@@ -557,17 +561,14 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
if (count != 1) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
- badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
"lmPwdHistory", &lmPwdHistory);
ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
"ntPwdHistory", &ntPwdHistory);
- lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
- ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
+ lmPwdHash = samdb_result_hash(mem_ctx, res[0], "lmPwdHash");
+ ntPwdHash = samdb_result_hash(mem_ctx, res[0], "ntPwdHash");
pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
- kvno = samdb_result_uint(res[0], "msDS-KeyVersionNumber", 0);
if (domain_dn) {
/* pull the domain parameters */
@@ -663,13 +664,13 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
/* check the immediately past password */
if (pwdHistoryLength > 0) {
- if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
+ if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
if (reject_reason) {
*reject_reason = SAMR_REJECT_COMPLEXITY;
}
return NT_STATUS_PASSWORD_RESTRICTION;
}
- if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
+ if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
if (reject_reason) {
*reject_reason = SAMR_REJECT_COMPLEXITY;
}
@@ -681,27 +682,6 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
- if (pwdHistoryLength > 0) {
- if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
- if (reject_reason) {
- *reject_reason = SAMR_REJECT_COMPLEXITY;
- }
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
- if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
- if (reject_reason) {
- *reject_reason = SAMR_REJECT_COMPLEXITY;
- }
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
- if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
- if (reject_reason) {
- *reject_reason = SAMR_REJECT_COMPLEXITY;
- }
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
- }
-
for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
if (reject_reason) {
@@ -723,79 +703,30 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
#define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
/* the password is acceptable. Start forming the new fields */
- if (lmNewHash) {
- CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
- } else {
- CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
- }
-
- if (ntNewHash) {
- CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
- } else {
- CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
- }
-
- if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
- (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
+ if (new_pass) {
+ /* if we know the cleartext, then only set it.
+ * Modules in ldb will set all the appropriate
+ * hashes */
CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
"unicodePwd", new_pass));
} else {
+ /* We don't have the cleartext, so delete the old one
+ * and set what we have of the hashes */
CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
- }
- CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
-
- CHECK_RET(samdb_msg_add_uint(ctx, mem_ctx, mod, "msDS-KeyVersionNumber", kvno + 1));
-
- if (pwdHistoryLength == 0) {
- CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
- CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
- return NT_STATUS_OK;
- }
-
- /* store the password history */
- new_lmPwdHistory = talloc_array(mem_ctx, struct samr_Password,
- pwdHistoryLength);
- if (!new_lmPwdHistory) {
- return NT_STATUS_NO_MEMORY;
- }
- new_ntPwdHistory = talloc_array(mem_ctx, struct samr_Password,
- pwdHistoryLength);
- if (!new_ntPwdHistory) {
- return NT_STATUS_NO_MEMORY;
- }
- for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
- new_lmPwdHistory[i+1] = lmPwdHistory[i];
- }
- for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
- new_ntPwdHistory[i+1] = ntPwdHistory[i];
- }
-
- /* Don't store 'long' passwords in the LM history,
- but make sure to 'expire' one password off the other end */
- if (lmNewHash) {
- new_lmPwdHistory[0] = *lmNewHash;
- } else {
- ZERO_STRUCT(new_lmPwdHistory[0]);
+ if (lmNewHash) {
+ CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
+ } else {
+ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
+ }
+
+ if (ntNewHash) {
+ CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
+ } else {
+ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
+ }
}
- lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
- if (ntNewHash) {
- new_ntPwdHistory[0] = *ntNewHash;
- } else {
- ZERO_STRUCT(new_ntPwdHistory[0]);
- }
- ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
-
- CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
- "lmPwdHistory",
- new_lmPwdHistory,
- lmPwdHistory_len));
-
- CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
- "ntPwdHistory",
- new_ntPwdHistory,
- ntPwdHistory_len));
return NT_STATUS_OK;
}
diff --git a/source4/setup/provision_init.ldif b/source4/setup/provision_init.ldif
index b341808e0e..e4c2476992 100644
--- a/source4/setup/provision_init.ldif
+++ b/source4/setup/provision_init.ldif
@@ -22,6 +22,7 @@ dn: CASE_INSENSITIVE
sAMAccountName: CASE_INSENSITIVE
objectClass: CASE_INSENSITIVE
unicodePwd: HIDDEN
+krb5Key: HIDDEN
ntPwdHash: HIDDEN
ntPwdHistory: HIDDEN
lmPwdHash: HIDDEN
@@ -69,5 +70,5 @@ isSynchronized: TRUE
#Add modules to the list to activate them by default
#beware often order is important
dn: @MODULES
-@LIST: rootdse,samldb,operational,objectguid,rdn_name
+@LIST: rootdse,samldb,password_hash,operational,objectguid,rdn_name