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:
parent
2c873c4353
commit
2481ce8942
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user