From: Andrew Bartlett Date: Thu, 9 Jul 2009 04:53:26 +0000 (+1000) Subject: s4:dsdb Allow unicodePwd to be set when adding a user X-Git-Tag: tevent-0.9.8~713 X-Git-Url: http://git.samba.org/samba.git/?p=ira%2Fwip.git;a=commitdiff_plain;h=2481ce89427ef38b47fb29d16c15b77e9d2c20b9 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 --- diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c index 5a9926b6d18..44b7ef91e9f 100644 --- a/source4/dsdb/samdb/ldb_modules/password_hash.c +++ b/source4/dsdb/samdb/ldb_modules/password_hash.c @@ -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; - } - - 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"); + /* 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; - 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);