1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00

s4:dsdb Allow unicodePwd to be set when adding a user

Windows 7 sets it's join password using the unicodePwd attribute (as a
quoted, utf16 string), and does so during the LDAPAdd of the object.
Previously, this code only handled unicodePwd for modifies.

Andrew Bartlett
This commit is contained in:
Andrew Bartlett 2009-07-09 14:53:26 +10:00
parent 2c873c4353
commit 2481ce8942

View File

@ -1432,6 +1432,67 @@ static int setup_password_fields(struct setup_password_fields_io *io)
return LDB_SUCCESS;
}
static int setup_io(struct ph_context *ac,
const struct ldb_message *new_msg,
const struct ldb_message *searched_msg,
struct setup_password_fields_io *io)
{
const struct ldb_val *quoted_utf16;
struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
ZERO_STRUCTP(io);
/* Some operations below require kerberos contexts */
if (smb_krb5_init_context(ac,
ldb_get_event_context(ldb),
(struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
&io->smb_krb5_context) != 0) {
return LDB_ERR_OPERATIONS_ERROR;
}
io->ac = ac;
io->domain = ac->domain;
io->u.user_account_control = samdb_result_uint(searched_msg, "userAccountControl", 0);
io->u.sAMAccountName = samdb_result_string(searched_msg, "samAccountName", NULL);
io->u.user_principal_name = samdb_result_string(searched_msg, "userPrincipalName", NULL);
io->u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
io->n.cleartext_utf8 = ldb_msg_find_ldb_val(new_msg, "userPassword");
io->n.cleartext_utf16 = ldb_msg_find_ldb_val(new_msg, "clearTextPassword");
/* this rather strange looking piece of code is there to
handle a ldap client setting a password remotely using the
unicodePwd ldap field. The syntax is that the password is
in UTF-16LE, with a " at either end. Unfortunately the
unicodePwd field is also used to store the nt hashes
internally in Samba, and is used in the nt hash format on
the wire in DRS replication, so we have a single name for
two distinct values. The code below leaves us with a small
chance (less than 1 in 2^32) of a mixup, if someone manages
to create a MD4 hash which starts and ends in 0x22 0x00, as
that would then be treated as a UTF16 password rather than
a nthash */
quoted_utf16 = ldb_msg_find_ldb_val(new_msg, "unicodePwd");
if (quoted_utf16 &&
quoted_utf16->length >= 4 &&
quoted_utf16->data[0] == '"' &&
quoted_utf16->data[1] == 0 &&
quoted_utf16->data[quoted_utf16->length-2] == '"' &&
quoted_utf16->data[quoted_utf16->length-1] == 0) {
io->n.quoted_utf16.data = talloc_memdup(io->ac, quoted_utf16->data+2, quoted_utf16->length-4);
io->n.quoted_utf16.length = quoted_utf16->length-4;
io->n.cleartext_utf16 = &io->n.quoted_utf16;
io->n.nt_hash = NULL;
} else {
io->n.nt_hash = samdb_result_hash(io->ac, new_msg, "unicodePwd");
}
io->n.lm_hash = samdb_result_hash(io->ac, new_msg, "dBCSPwd");
return LDB_SUCCESS;
}
static struct ph_context *ph_init_context(struct ldb_module *module,
struct ldb_request *req)
{
@ -1743,50 +1804,32 @@ static int password_hash_add_do_add(struct ph_context *ac)
{
struct ldb_context *ldb;
struct ldb_request *down_req;
struct smb_krb5_context *smb_krb5_context;
struct ldb_message *msg;
struct setup_password_fields_io io;
int ret;
ldb = ldb_module_get_ctx(ac->module);
/* Prepare the internal data structure containing the passwords */
ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
if (ret != LDB_SUCCESS) {
return ret;
}
msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
if (msg == NULL) {
return LDB_ERR_OPERATIONS_ERROR;
}
/* Some operations below require kerberos contexts */
if (smb_krb5_init_context(ac,
ldb_get_event_context(ldb),
(struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
&smb_krb5_context) != 0) {
return LDB_ERR_OPERATIONS_ERROR;
}
ZERO_STRUCT(io);
io.ac = ac;
io.domain = ac->domain;
io.smb_krb5_context = smb_krb5_context;
io.u.user_account_control = samdb_result_uint(msg, "userAccountControl", 0);
io.u.sAMAccountName = samdb_result_string(msg, "samAccountName", NULL);
io.u.user_principal_name = samdb_result_string(msg, "userPrincipalName", NULL);
io.u.is_computer = ldb_msg_check_string_attribute(msg, "objectClass", "computer");
io.n.cleartext_utf8 = ldb_msg_find_ldb_val(msg, "userPassword");
io.n.cleartext_utf16 = ldb_msg_find_ldb_val(msg, "clearTextPassword");
io.n.nt_hash = samdb_result_hash(io.ac, msg, "unicodePwd");
io.n.lm_hash = samdb_result_hash(io.ac, msg, "dBCSPwd");
/* remove attributes */
if (io.n.cleartext_utf8) ldb_msg_remove_attr(msg, "userPassword");
if (io.n.cleartext_utf16) ldb_msg_remove_attr(msg, "clearTextPassword");
if (io.n.nt_hash) ldb_msg_remove_attr(msg, "unicodePwd");
if (io.n.lm_hash) ldb_msg_remove_attr(msg, "dBCSPwd");
/* remove attributes that we just read into 'io' */
ldb_msg_remove_attr(msg, "userPassword");
ldb_msg_remove_attr(msg, "clearTextPassword");
ldb_msg_remove_attr(msg, "unicodePwd");
ldb_msg_remove_attr(msg, "dBCSPwd");
ldb_msg_remove_attr(msg, "pwdLastSet");
io.o.kvno = samdb_result_uint(msg, "msDs-KeyVersionNumber", 1) - 1;
ldb_msg_remove_attr(msg, "msDs-KeyVersionNumber");
ldb = ldb_module_get_ctx(ac->module);
ret = setup_password_fields(&io);
if (ret != LDB_SUCCESS) {
return ret;
@ -2096,12 +2139,9 @@ static int password_hash_mod_do_mod(struct ph_context *ac)
{
struct ldb_context *ldb;
struct ldb_request *mod_req;
struct smb_krb5_context *smb_krb5_context;
struct ldb_message *msg;
struct ldb_message *orig_msg;
struct ldb_message *searched_msg;
const struct ldb_message *searched_msg;
struct setup_password_fields_io io;
const struct ldb_val *quoted_utf16;
int ret;
ldb = ldb_module_get_ctx(ac->module);
@ -2115,59 +2155,18 @@ static int password_hash_mod_do_mod(struct ph_context *ac)
/* modify dn */
msg->dn = ac->req->op.mod.message->dn;
/* Some operations below require kerberos contexts */
if (smb_krb5_init_context(ac,
ldb_get_event_context(ldb),
(struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
&smb_krb5_context) != 0) {
return LDB_ERR_OPERATIONS_ERROR;
/* Prepare the internal data structure containing the passwords */
ret = setup_io(ac,
ac->req->op.mod.message,
ac->search_res->message,
&io);
if (ret != LDB_SUCCESS) {
return ret;
}
searched_msg = ac->search_res->message;
orig_msg = discard_const(ac->req->op.mod.message);
searched_msg = ac->search_res->message;
ZERO_STRUCT(io);
io.ac = ac;
io.domain = ac->domain;
io.smb_krb5_context = smb_krb5_context;
io.u.user_account_control = samdb_result_uint(searched_msg, "userAccountControl", 0);
io.u.sAMAccountName = samdb_result_string(searched_msg, "samAccountName", NULL);
io.u.user_principal_name = samdb_result_string(searched_msg, "userPrincipalName", NULL);
io.u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
io.n.cleartext_utf8 = ldb_msg_find_ldb_val(orig_msg, "userPassword");
io.n.cleartext_utf16 = ldb_msg_find_ldb_val(orig_msg, "clearTextPassword");
/* this rather strange looking piece of code is there to
handle a ldap client setting a password remotely using the
unicodePwd ldap field. The syntax is that the password is
in UTF-16LE, with a " at either end. Unfortunately the
unicodePwd field is also used to store the nt hashes
internally in Samba, and is used in the nt hash format on
the wire in DRS replication, so we have a single name for
two distinct values. The code below leaves us with a small
chance (less than 1 in 2^32) of a mixup, if someone manages
to create a MD4 hash which starts and ends in 0x22 0x00, as
that would then be treated as a UTF16 password rather than
a nthash */
quoted_utf16 = ldb_msg_find_ldb_val(orig_msg, "unicodePwd");
if (quoted_utf16 &&
quoted_utf16->length >= 4 &&
quoted_utf16->data[0] == '"' &&
quoted_utf16->data[1] == 0 &&
quoted_utf16->data[quoted_utf16->length-2] == '"' &&
quoted_utf16->data[quoted_utf16->length-1] == 0) {
io.n.quoted_utf16.data = talloc_memdup(orig_msg, quoted_utf16->data+2, quoted_utf16->length-4);
io.n.quoted_utf16.length = quoted_utf16->length-4;
io.n.cleartext_utf16 = &io.n.quoted_utf16;
io.n.nt_hash = NULL;
} else {
io.n.nt_hash = samdb_result_hash(io.ac, orig_msg, "unicodePwd");
}
io.n.lm_hash = samdb_result_hash(io.ac, orig_msg, "dBCSPwd");
/* Fill in some final details (only relevent once the password has been set) */
io.o.kvno = samdb_result_uint(searched_msg, "msDs-KeyVersionNumber", 0);
io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);