summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/auth/ntlm/auth_sam.c2
-rw-r--r--source4/dsdb/common/util.c88
-rw-r--r--source4/dsdb/samdb/ldb_modules/objectclass.c12
-rw-r--r--source4/dsdb/samdb/ldb_modules/password_hash.c195
-rw-r--r--source4/kdc/kpasswdd.c37
-rwxr-xr-xsource4/lib/ldb/tests/python/ldap.py8
-rw-r--r--source4/libcli/auth/smbencrypt.c55
-rw-r--r--source4/librpc/idl/drsblobs.idl2
-rw-r--r--source4/rpc_server/lsa/dcesrv_lsa.c21
-rw-r--r--source4/rpc_server/netlogon/dcerpc_netlogon.c27
-rw-r--r--source4/rpc_server/samr/samr_password.c99
-rw-r--r--source4/scripting/python/samba/provision.py3
-rw-r--r--source4/torture/rpc/netlogon.c71
-rw-r--r--source4/torture/rpc/samr.c178
14 files changed, 598 insertions, 200 deletions
diff --git a/source4/auth/ntlm/auth_sam.c b/source4/auth/ntlm/auth_sam.c
index ac36d4f1ef..78429106f6 100644
--- a/source4/auth/ntlm/auth_sam.c
+++ b/source4/auth/ntlm/auth_sam.c
@@ -248,7 +248,7 @@ static NTSTATUS authsam_authenticate(struct auth_context *auth_context,
}
}
- nt_status = samdb_result_passwords(mem_ctx, msgs[0], &lm_pwd, &nt_pwd);
+ nt_status = samdb_result_passwords(mem_ctx, auth_context->lp_ctx, msgs[0], &lm_pwd, &nt_pwd);
NT_STATUS_NOT_OK_RETURN(nt_status);
nt_status = authsam_password_ok(auth_context, mem_ctx,
diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c
index 5b93efb316..6a6f370943 100644
--- a/source4/dsdb/common/util.c
+++ b/source4/dsdb/common/util.c
@@ -26,6 +26,7 @@
#include "ldb.h"
#include "ldb_errors.h"
#include "../lib/util/util_ldb.h"
+#include "../lib/crypto/crypto.h"
#include "dsdb/samdb/samdb.h"
#include "libcli/security/security.h"
#include "librpc/gen_ndr/ndr_security.h"
@@ -571,7 +572,7 @@ uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
return count;
}
-NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, struct ldb_message *msg,
struct samr_Password **lm_pwd, struct samr_Password **nt_pwd)
{
struct samr_Password *lmPwdHash, *ntPwdHash;
@@ -587,14 +588,21 @@ NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
}
}
if (lm_pwd) {
- int num_lm;
- num_lm = samdb_result_hashes(mem_ctx, msg, "dBCSPwd", &lmPwdHash);
- if (num_lm == 0) {
- *lm_pwd = NULL;
- } else if (num_lm > 1) {
- return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ /* Ensure that if we have turned off LM
+ * authentication, that we never use the LM hash, even
+ * if we store it */
+ if (lp_lanman_auth(lp_ctx)) {
+ int num_lm;
+ num_lm = samdb_result_hashes(mem_ctx, msg, "dBCSPwd", &lmPwdHash);
+ if (num_lm == 0) {
+ *lm_pwd = NULL;
+ } else if (num_lm > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ *lm_pwd = &lmPwdHash[0];
+ }
} else {
- *lm_pwd = &lmPwdHash[0];
+ *lm_pwd = NULL;
}
}
return NT_STATUS_OK;
@@ -1531,7 +1539,7 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
struct ldb_dn *user_dn,
struct ldb_dn *domain_dn,
struct ldb_message *mod,
- const char *new_pass,
+ const DATA_BLOB *new_password,
struct samr_Password *lmNewHash,
struct samr_Password *ntNewHash,
bool user_change,
@@ -1632,40 +1640,47 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
*_dominfo = dominfo;
}
- if (restrictions && new_pass) {
-
+ if (restrictions && new_password) {
+ char *new_pass;
+
/* check the various password restrictions */
- if (restrictions && minPwdLength > strlen_m(new_pass)) {
+ if (restrictions && minPwdLength > utf16_len_n(new_password->data, new_password->length) / 2) {
if (reject_reason) {
*reject_reason = SAMR_REJECT_TOO_SHORT;
}
return NT_STATUS_PASSWORD_RESTRICTION;
}
+
+ /* Create the NT hash */
+ mdfour(local_ntNewHash.hash, new_password->data, new_password->length);
- /* possibly check password complexity */
- if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
- !samdb_password_complexity_ok(new_pass)) {
- if (reject_reason) {
- *reject_reason = SAMR_REJECT_COMPLEXITY;
+ ntNewHash = &local_ntNewHash;
+
+ /* Only check complexity if we can convert it at all. Assuming unconvertable passwords are 'strong' */
+ if (convert_string_talloc(mem_ctx, lp_iconv_convenience(ldb_get_opaque(ctx, "loadparm")),
+ CH_UTF16, CH_UNIX,
+ new_password->data, new_password->length,
+ (void **)&new_pass) != -1) {
+
+
+ /* possibly check password complexity */
+ if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
+ !samdb_password_complexity_ok(new_pass)) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
}
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
-
- /* compute the new nt and lm hashes */
- if (E_deshash(new_pass, local_lmNewHash.hash)) {
- lmNewHash = &local_lmNewHash;
- }
- if (!E_md4hash(new_pass, local_ntNewHash.hash)) {
- /* If we can't convert this password to UCS2, then we should not accept it */
- if (reject_reason) {
- *reject_reason = SAMR_REJECT_OTHER;
+
+ /* compute the new lm hashes (for checking history - case insenitivly!) */
+ if (E_deshash(new_pass, local_lmNewHash.hash)) {
+ lmNewHash = &local_lmNewHash;
}
- return NT_STATUS_PASSWORD_RESTRICTION;
+
}
- ntNewHash = &local_ntNewHash;
}
- if (user_change) {
+ if (restrictions && user_change) {
/* are all password changes disallowed? */
if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
if (reject_reason) {
@@ -1731,16 +1746,15 @@ 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 (new_pass) {
- /* if we know the cleartext, then only set it.
+ if (new_password) {
+ /* if we know the cleartext UTF16 password, then set it.
* Modules in ldb will set all the appropriate
* hashes */
- CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
- "userPassword", new_pass));
+ CHECK_RET(ldb_msg_add_value(mod, "clearTextPassword", new_password, NULL));
} 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, "userPassword"));
+ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "clearTextPassword"));
if (lmNewHash) {
CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "dBCSPwd", lmNewHash));
@@ -1769,7 +1783,7 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
*/
NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
const struct dom_sid *user_sid,
- const char *new_pass,
+ const DATA_BLOB *new_pass,
struct samr_Password *lmNewHash,
struct samr_Password *ntNewHash,
bool user_change,
diff --git a/source4/dsdb/samdb/ldb_modules/objectclass.c b/source4/dsdb/samdb/ldb_modules/objectclass.c
index e610c358f6..7d00851792 100644
--- a/source4/dsdb/samdb/ldb_modules/objectclass.c
+++ b/source4/dsdb/samdb/ldb_modules/objectclass.c
@@ -382,11 +382,17 @@ static int fix_attributes(struct ldb_context *ldb, const struct dsdb_schema *sch
int i;
for (i=0; i < msg->num_elements; i++) {
const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(schema, msg->elements[i].name);
+ /* Add in a very special case for 'clearTextPassword',
+ * which is used for internal processing only, and is
+ * not presented in the schema */
if (!attribute) {
- ldb_asprintf_errstring(ldb, "attribute %s is not a valid attribute in schema", msg->elements[i].name);
- return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
+ if (strcasecmp(msg->elements[i].name, "clearTextPassword") != 0) {
+ ldb_asprintf_errstring(ldb, "attribute %s is not a valid attribute in schema", msg->elements[i].name);
+ return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
+ }
+ } else {
+ msg->elements[i].name = attribute->lDAPDisplayName;
}
- msg->elements[i].name = attribute->lDAPDisplayName;
}
return LDB_SUCCESS;
diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c
index e36de3c5c4..c4451d1355 100644
--- a/source4/dsdb/samdb/ldb_modules/password_hash.c
+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c
@@ -109,7 +109,8 @@ struct setup_password_fields_io {
/* new credentials */
struct {
- const char *cleartext;
+ const struct ldb_val *cleartext_utf8;
+ const struct ldb_val *cleartext_utf16;
struct samr_Password *nt_hash;
struct samr_Password *lm_hash;
} n;
@@ -144,6 +145,9 @@ struct setup_password_fields_io {
} g;
};
+/* Get the NT hash, and fill it in as an entry in the password history,
+ and specify it into io->g.nt_hash */
+
static int setup_nt_fields(struct setup_password_fields_io *io)
{
uint32_t i;
@@ -181,6 +185,9 @@ static int setup_nt_fields(struct setup_password_fields_io *io)
return LDB_SUCCESS;
}
+/* Get the LANMAN hash, and fill it in as an entry in the password history,
+ and specify it into io->g.lm_hash */
+
static int setup_lm_fields(struct setup_password_fields_io *io)
{
uint32_t i;
@@ -220,6 +227,10 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io)
Principal *salt_principal;
krb5_salt salt;
krb5_keyblock key;
+ krb5_data cleartext_data;
+
+ cleartext_data.data = io->n.cleartext_utf8->data;
+ cleartext_data.length = io->n.cleartext_utf8->length;
/* Many, many thanks to lukeh@padl.com for this
* algorithm, described in his Nov 10 2004 mail to
@@ -314,11 +325,11 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io)
* create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
* the salt and the cleartext password
*/
- krb5_ret = krb5_string_to_key_salt(io->smb_krb5_context->krb5_context,
- ENCTYPE_AES256_CTS_HMAC_SHA1_96,
- io->n.cleartext,
- salt,
- &key);
+ krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
+ ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ cleartext_data,
+ salt,
+ &key);
if (krb5_ret) {
ldb_asprintf_errstring(io->ac->module->ldb,
"setup_kerberos_keys: "
@@ -339,11 +350,11 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io)
* create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
* the salt and the cleartext password
*/
- krb5_ret = krb5_string_to_key_salt(io->smb_krb5_context->krb5_context,
- ENCTYPE_AES128_CTS_HMAC_SHA1_96,
- io->n.cleartext,
- salt,
- &key);
+ krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
+ ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+ cleartext_data,
+ salt,
+ &key);
if (krb5_ret) {
ldb_asprintf_errstring(io->ac->module->ldb,
"setup_kerberos_keys: "
@@ -364,11 +375,11 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io)
* create ENCTYPE_DES_CBC_MD5 key out of
* the salt and the cleartext password
*/
- krb5_ret = krb5_string_to_key_salt(io->smb_krb5_context->krb5_context,
- ENCTYPE_DES_CBC_MD5,
- io->n.cleartext,
- salt,
- &key);
+ krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
+ ENCTYPE_DES_CBC_MD5,
+ cleartext_data,
+ salt,
+ &key);
if (krb5_ret) {
ldb_asprintf_errstring(io->ac->module->ldb,
"setup_kerberos_keys: "
@@ -389,11 +400,11 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io)
* create ENCTYPE_DES_CBC_CRC key out of
* the salt and the cleartext password
*/
- krb5_ret = krb5_string_to_key_salt(io->smb_krb5_context->krb5_context,
- ENCTYPE_DES_CBC_CRC,
- io->n.cleartext,
- salt,
- &key);
+ krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
+ ENCTYPE_DES_CBC_CRC,
+ cleartext_data,
+ salt,
+ &key);
if (krb5_ret) {
ldb_asprintf_errstring(io->ac->module->ldb,
"setup_kerberos_keys: "
@@ -648,7 +659,6 @@ static int setup_primary_wdigest(struct setup_password_fields_io *io,
DATA_BLOB dns_domain;
DATA_BLOB dns_domain_l;
DATA_BLOB dns_domain_u;
- DATA_BLOB cleartext;
DATA_BLOB digest;
DATA_BLOB delim;
DATA_BLOB backslash;
@@ -929,8 +939,6 @@ static int setup_primary_wdigest(struct setup_password_fields_io *io,
dns_domain_l = data_blob_string_const(io->domain->dns_domain);
dns_domain_u = data_blob_string_const(io->domain->realm);
- cleartext = data_blob_string_const(io->n.cleartext);
-
digest = data_blob_string_const("Digest");
delim = data_blob_string_const(":");
@@ -956,7 +964,7 @@ static int setup_primary_wdigest(struct setup_password_fields_io *io,
MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
}
MD5Update(&md5, delim.data, delim.length);
- MD5Update(&md5, cleartext.data, cleartext.length);
+ MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
MD5Final(pdb->hashes[i].hash, &md5);
}
@@ -1011,7 +1019,7 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
ZERO_STRUCT(zero16);
ZERO_STRUCT(names);
- if (!io->n.cleartext) {
+ if (!io->n.cleartext_utf8) {
/*
* when we don't have a cleartext password
* we can't setup a supplementalCredential value
@@ -1193,7 +1201,7 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
if (pc) {
*nc = "CLEARTEXT";
- pcb.cleartext = io->n.cleartext;
+ pcb.cleartext = *io->n.cleartext_utf16;
ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
lp_iconv_convenience(ldb_get_opaque(io->ac->module->ldb, "loadparm")),
@@ -1285,58 +1293,97 @@ static int setup_password_fields(struct setup_password_fields_io *io)
{
bool ok;
int ret;
-
+ ssize_t converted_pw_len;
+
/*
* refuse the change if someone want to change the cleartext
* and supply his own hashes at the same time...
*/
- if (io->n.cleartext && (io->n.nt_hash || io->n.lm_hash)) {
+ if ((io->n.cleartext_utf8 || io->n.cleartext_utf16) && (io->n.nt_hash || io->n.lm_hash)) {
ldb_asprintf_errstring(io->ac->module->ldb,
"setup_password_fields: "
"it's only allowed to set the cleartext password or the password hashes");
return LDB_ERR_UNWILLING_TO_PERFORM;
}
-
- if (io->n.cleartext) {
- struct samr_Password *hash;
-
- hash = talloc(io->ac, struct samr_Password);
- if (!hash) {
+
+ if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
+ ldb_asprintf_errstring(io->ac->module->ldb,
+ "setup_password_fields: "
+ "it's only allowed to set the cleartext password as userPassword or clearTextPasssword, not both at once");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ if (io->n.cleartext_utf8) {
+ char **cleartext_utf16_str;
+ struct ldb_val *cleartext_utf16_blob;
+ io->n.cleartext_utf16 = cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
+ if (!io->n.cleartext_utf16) {
ldb_oom(io->ac->module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
-
- /* compute the new nt hash */
- ok = E_md4hash(io->n.cleartext, hash->hash);
- if (ok) {
- io->n.nt_hash = hash;
- } else {
+ converted_pw_len = convert_string_talloc(io->ac, lp_iconv_convenience(ldb_get_opaque(io->ac->module->ldb, "loadparm")),
+ CH_UTF8, CH_UTF16, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length,
+ (void **)&cleartext_utf16_str);
+ if (converted_pw_len == -1) {
ldb_asprintf_errstring(io->ac->module->ldb,
"setup_password_fields: "
- "failed to generate nthash from cleartext password");
+ "failed to generate UTF16 password from cleartext UTF8 password");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ *cleartext_utf16_blob = data_blob_const(cleartext_utf16_str, converted_pw_len);
+ } else if (io->n.cleartext_utf16) {
+ char *cleartext_utf8_str;
+ struct ldb_val *cleartext_utf8_blob;
+ io->n.cleartext_utf8 = cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
+ if (!io->n.cleartext_utf8) {
+ ldb_oom(io->ac->module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
+ converted_pw_len = convert_string_talloc(io->ac, lp_iconv_convenience(ldb_get_opaque(io->ac->module->ldb, "loadparm")),
+ CH_UTF16, CH_UTF8, io->n.cleartext_utf16->data, io->n.cleartext_utf16->length,
+ (void **)&cleartext_utf8_str);
+ if (converted_pw_len == -1) {
+ /* We can't bail out entirely, as these unconvertable passwords are frustratingly valid */
+ io->n.cleartext_utf8 = NULL;
+ talloc_free(cleartext_utf8_blob);
+ }
+ *cleartext_utf8_blob = data_blob_const(cleartext_utf8_str, converted_pw_len);
}
-
- if (io->n.cleartext) {
- struct samr_Password *hash;
-
- hash = talloc(io->ac, struct samr_Password);
- if (!hash) {
+ if (io->n.cleartext_utf16) {
+ struct samr_Password *nt_hash;
+ nt_hash = talloc(io->ac, struct samr_Password);
+ if (!nt_hash) {
ldb_oom(io->ac->module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
+ io->n.nt_hash = nt_hash;
- /* compute the new lm hash */
- ok = E_deshash(io->n.cleartext, hash->hash);
- if (ok) {
- io->n.lm_hash = hash;
- } else {
- talloc_free(hash->hash);
- }
+ /* compute the new nt hash */
+ mdfour(nt_hash->hash, io->n.cleartext_utf16->data, io->n.cleartext_utf16->length);
}
- if (io->n.cleartext) {
+ if (io->n.cleartext_utf8) {
+ struct samr_Password *lm_hash;
+ char *cleartext_unix;
+ converted_pw_len = convert_string_talloc(io->ac, lp_iconv_convenience(ldb_get_opaque(io->ac->module->ldb, "loadparm")),
+ CH_UTF8, CH_UNIX, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length,
+ (void **)&cleartext_unix);
+ if (converted_pw_len != -1) {
+ lm_hash = talloc(io->ac, struct samr_Password);
+ if (!lm_hash) {
+ ldb_oom(io->ac->module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* compute the new lm hash. */
+ ok = E_deshash((char *)cleartext_unix, lm_hash->hash);
+ if (ok) {
+ io->n.lm_hash = lm_hash;
+ } else {
+ talloc_free(lm_hash->hash);
+ }
+ }
+
ret = setup_kerberos_keys(io);
if (ret != 0) {
return ret;
@@ -1560,6 +1607,7 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
{
struct ph_context *ac;
struct ldb_message_element *sambaAttr;
+ struct ldb_message_element *clearTextPasswordAttr;
struct ldb_message_element *ntAttr;
struct ldb_message_element *lmAttr;
int ret;
@@ -1591,6 +1639,7 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
* or LM hashes, then we don't need to make any changes. */
sambaAttr = ldb_msg_find_element(req->op.mod.message, "userPassword");
+ clearTextPasswordAttr = ldb_msg_find_element(req->op.mod.message, "clearTextPassword");
ntAttr = ldb_msg_find_element(req->op.mod.message, "unicodePwd");
lmAttr = ldb_msg_find_element(req->op.mod.message, "dBCSPwd");
@@ -1611,6 +1660,10 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
ldb_set_errstring(module->ldb, "mupltiple values for userPassword not allowed!\n");
return LDB_ERR_CONSTRAINT_VIOLATION;
}
+ if (clearTextPasswordAttr && clearTextPasswordAttr->num_values > 1) {
+ ldb_set_errstring(module->ldb, "mupltiple values for clearTextPassword not allowed!\n");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
if (ntAttr && (ntAttr->num_values > 1)) {
ldb_set_errstring(module->ldb, "mupltiple values for unicodePwd not allowed!\n");
@@ -1626,6 +1679,11 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
return LDB_ERR_CONSTRAINT_VIOLATION;
}
+ if (clearTextPasswordAttr && clearTextPasswordAttr->num_values == 0) {
+ ldb_set_errstring(module->ldb, "clearTextPassword must have a value!\n");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
if (ntAttr && (ntAttr->num_values == 0)) {
ldb_set_errstring(module->ldb, "unicodePwd must have a value!\n");
return LDB_ERR_CONSTRAINT_VIOLATION;
@@ -1687,12 +1745,14 @@ static int password_hash_add_do_add(struct ph_context *ac) {
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 = samdb_result_string(msg, "userPassword", NULL);
+ 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) ldb_msg_remove_attr(msg, "userPassword");
+ 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");
ldb_msg_remove_attr(msg, "pwdLastSet");
@@ -1772,6 +1832,7 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
{
struct ph_context *ac;
struct ldb_message_element *sambaAttr;
+ struct ldb_message_element *clearTextAttr;
struct ldb_message_element *ntAttr;
struct ldb_message_element *lmAttr;
struct ldb_message *msg;
@@ -1802,13 +1863,16 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
}
sambaAttr = ldb_msg_find_element(req->op.mod.message, "userPassword");
+ clearTextAttr = ldb_msg_find_element(req->op.mod.message, "clearTextPassword");
ntAttr = ldb_msg_find_element(req->op.mod.message, "unicodePwd");
lmAttr = ldb_msg_find_element(req->op.mod.message, "dBCSPwd");
- /* If no part of this touches the userPassword OR unicodePwd and/or dBCSPwd, 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 ((!sambaAttr) && (!ntAttr) && (!lmAttr)) {
+ /* If no part of this touches the userPassword OR
+ * clearTextPassword OR unicodePwd and/or dBCSPwd, 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 ((!sambaAttr) && (!clearTextAttr) && (!ntAttr) && (!lmAttr)) {
return ldb_next_request(module, req);
}
@@ -1817,6 +1881,9 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
if (sambaAttr && (sambaAttr->num_values > 1)) {
return LDB_ERR_CONSTRAINT_VIOLATION;
}
+ if (clearTextAttr && (clearTextAttr->num_values > 1)) {
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
if (ntAttr && (ntAttr->num_values > 1)) {
return LDB_ERR_CONSTRAINT_VIOLATION;
}
@@ -1839,6 +1906,7 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
/* - remove any modification to the password from the first commit
* we will make the real modification later */
if (sambaAttr) ldb_msg_remove_attr(msg, "userPassword");
+ if (clearTextAttr) ldb_msg_remove_attr(msg, "clearTextPassword");
if (ntAttr) ldb_msg_remove_attr(msg, "unicodePwd");
if (lmAttr) ldb_msg_remove_attr(msg, "dBCSPwd");
@@ -2028,7 +2096,8 @@ static int password_hash_mod_do_mod(struct ph_context *ac) {
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 = samdb_result_string(orig_msg, "userPassword", NULL);
+ io.n.cleartext_utf8 = ldb_msg_find_ldb_val(orig_msg, "userPassword");
+ io.n.cleartext_utf16 = ldb_msg_find_ldb_val(orig_msg, "clearTextPassword");
io.n.nt_hash = samdb_result_hash(io.ac, orig_msg, "unicodePwd");
io.n.lm_hash = samdb_result_hash(io.ac, orig_msg, "dBCSPwd");
diff --git a/source4/kdc/kpasswdd.c b/source4/kdc/kpasswdd.c
index d662844c4e..1336b0157e 100644
--- a/source4/kdc/kpasswdd.c
+++ b/source4/kdc/kpasswdd.c
@@ -175,7 +175,7 @@ static bool kpasswd_make_pwchange_reply(struct kdc_server *kdc,
static bool kpasswdd_change_password(struct kdc_server *kdc,
TALLOC_CTX *mem_ctx,
struct auth_session_info *session_info,
- const char *password,
+ const DATA_BLOB *password,
DATA_BLOB *reply)
{
NTSTATUS status;
@@ -219,6 +219,8 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
DATA_BLOB *reply)
{
struct auth_session_info *session_info;
+ ssize_t pw_len;
+
if (!NT_STATUS_IS_OK(gensec_session_info(gensec_security,
&session_info))) {
return kpasswdd_make_error_reply(kdc, mem_ctx,
@@ -230,12 +232,20 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
switch (version) {
case KRB5_KPASSWD_VERS_CHANGEPW:
{
- char *password = talloc_strndup(mem_ctx, (const char *)input->data, input->length);
- if (!password) {
+ DATA_BLOB password;
+ pw_len = convert_string_talloc(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx),
+ CH_UTF8, CH_UTF16,
+ (const char *)input->data,
+ input->length,
+ (void **)&password.data);
+
+ if (pw_len == -1) {
return false;
}
+ password.length = pw_len;
+
return kpasswdd_change_password(kdc, mem_ctx, session_info,
- password, reply);
+ &password, reply);
break;
}
case KRB5_KPASSWD_VERS_SETPW:
@@ -248,7 +258,7 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
krb5_context context = kdc->smb_krb5_context->krb5_context;
ChangePasswdDataMS chpw;
- char *password;
+ DATA_BLOB password;
krb5_principal principal;
char *set_password_on_princ;
@@ -271,13 +281,18 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
reply);
}
- password = talloc_strndup(mem_ctx,
- (const char *)chpw.newpasswd.data,
- chpw.newpasswd.length);
- if (!password) {
+ pw_len = convert_string_talloc(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx),
+ CH_UTF8, CH_UTF16,
+ (const char *)chpw.newpasswd.data,
+ chpw.newpasswd.length,
+ (void **)&password.data);
+ if (pw_len == -1) {
free_ChangePasswdDataMS(&chpw);
return false;
}
+
+ password.length = pw_len;
+
if ((chpw.targname && !chpw.targrealm)
|| (!chpw.targname && chpw.targrealm)) {
return kpasswdd_make_error_reply(kdc, mem_ctx,
@@ -306,7 +321,7 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
} else {
free_ChangePasswdDataMS(&chpw);
return kpasswdd_change_password(kdc, mem_ctx, session_info,
- password, reply);
+ &password, reply);
}
free_ChangePasswdDataMS(&chpw);
@@ -371,7 +386,7 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
/* Admin password set */
status = samdb_set_password(samdb, mem_ctx,
set_password_on_dn, NULL,
- msg, password, NULL, NULL,
+ msg, &password, NULL, NULL,
false, /* this is not a user password change */
&reject_reason, &dominfo);
}
diff --git a/source4/lib/ldb/tests/python/ldap.py b/source4/lib/ldb/tests/python/ldap.py
index e2cc658521..71fd98876e 100755
--- a/source4/lib/ldb/tests/python/ldap.py
+++ b/source4/lib/ldb/tests/python/ldap.py
@@ -756,7 +756,7 @@ member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """
ldb.delete(res[0].dn)
- attrs = ["cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "memberOf"]
+ attrs = ["cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "memberOf", "allowedAttributes", "allowedAttributesEffective"]
print "Testing ldb.search for (&(cn=ldaptestUSer2)(objectClass=user))"
res = ldb.search(self.base_dn, expression="(&(cn=ldaptestUSer2)(objectClass=user))", scope=SCOPE_SUBTREE, attrs=attrs)
self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestUSer2)(objectClass=user))")
@@ -768,9 +768,11 @@ member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """
self.assertTrue("objectGUID" in res[0])
self.assertTrue("whenCreated" in res[0])
self.assertTrue("nTSecurityDescriptor" in res[0])
+ self.assertTrue("allowedAttributes" in res[0])
+ self.assertTrue("allowedAttributesEffective" in res[0])
self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
- attrs = ["cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "member"]
+ attrs = ["cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "member", "allowedAttributes", "allowedAttributesEffective"]
print "Testing ldb.search for (&(cn=ldaptestgroup2)(objectClass=group))"
res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs)
self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group))")
@@ -782,6 +784,8 @@ member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """
self.assertTrue("objectGuid" not in res[0])
self.assertTrue("whenCreated" in res[0])
self.assertTrue("nTSecurityDescriptor" in res[0])
+ self.assertTrue("allowedAttributes" in res[0])
+ self.assertTrue("allowedAttributesEffective" in res[0])
memberUP = []
for m in res[0]["member"]:
memberUP.append(m.upper())
diff --git a/source4/libcli/auth/smbencrypt.c b/source4/libcli/auth/smbencrypt.c
index 3af7e45002..096f51e49b 100644
--- a/source4/libcli/auth/smbencrypt.c
+++ b/source4/libcli/auth/smbencrypt.c
@@ -536,9 +536,62 @@ bool decode_pw_buffer(uint8_t in_buffer[516], char *new_pwrd,
#ifdef DEBUG_PASSWORD
DEBUG(100,("decode_pw_buffer: new_pwrd: "));
dump_data(100, (const uint8_t *)new_pwrd, converted_pw_len);
- DEBUG(100,("multibyte len:%d\n", converted_pw_len));
+ DEBUG(100,("multibyte len:%d\n", (int)converted_pw_len));
DEBUG(100,("original char len:%d\n", byte_len/2));
#endif
return true;
}
+
+/***********************************************************
+ encode a password buffer with an already unicode password. The
+ rest of the buffer is filled with random data to make it harder to attack.
+************************************************************/
+bool set_pw_in_buffer(uint8_t buffer[516], DATA_BLOB *password)
+{
+ if (password->length > 512) {
+ return false;
+ }
+
+ memcpy(&buffer[512 - password->length], password->data, password->length);
+
+ generate_random_buffer(buffer, 512 - password->length);
+
+ /*
+ * The length of the new password is in the last 4 bytes of
+ * the data buffer.
+ */
+ SIVAL(buffer, 512, password->length);
+ return true;
+}
+
+/***********************************************************
+ decode a password buffer
+ *new_pw_size is the length in bytes of the extracted unicode password
+************************************************************/
+bool extract_pw_from_buffer(TALLOC_CTX *mem_ctx,
+ uint8_t in_buffer[516], DATA_BLOB *new_pass)
+{
+ int byte_len=0;
+
+ /* The length of the new password is in the last 4 bytes of the data buffer. */
+
+ byte_len = IVAL(in_buffer, 512);
+
+#ifdef DEBUG_PASSWORD
+ dump_data(100, in_buffer, 516);
+#endif
+
+ /* Password cannot be longer than the size of the password buffer */
+ if ( (byte_len < 0) || (byte_len > 512)) {
+ return false;
+ }
+
+ *new_pass = data_blob_talloc(mem_ctx, &in_buffer[512 - byte_len], byte_len);
+
+ if (!*new_pass->data) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/source4/librpc/idl/drsblobs.idl b/source4/librpc/idl/drsblobs.idl
index 4274d2000a..087f0c982d 100644
--- a/source4/librpc/idl/drsblobs.idl
+++ b/source4/librpc/idl/drsblobs.idl
@@ -332,7 +332,7 @@ interface drsblobs {
);
typedef [public] struct {
- [flag(STR_NOTERM|NDR_REMAINING)] string cleartext;
+ [flag(NDR_REMAINING)] DATA_BLOB cleartext;
} package_PrimaryCLEARTEXTBlob;
void decode_PrimaryCLEARTEXT(
diff --git a/source4/rpc_server/lsa/dcesrv_lsa.c b/source4/rpc_server/lsa/dcesrv_lsa.c
index 9cda7d0d89..4c596f1f03 100644
--- a/source4/rpc_server/lsa/dcesrv_lsa.c
+++ b/source4/rpc_server/lsa/dcesrv_lsa.c
@@ -968,19 +968,14 @@ static NTSTATUS dcesrv_lsa_CreateTrustedDomain_base(struct dcesrv_call_state *dc
mem_ctx, msg_user, "unicodePwd",
&auth_struct.incoming.current[i]->AuthInfo.nt4owf.password);
} else if (auth_struct.incoming.current[i]->AuthType == TRUST_AUTH_TYPE_CLEAR) {
- struct samr_Password hash;
-/*
- . We cannot do this, as windows chooses to send in random passwords here, that won't convert to UTF8
- samdb_msg_add_string(trusted_domain_state->policy->sam_ldb,
- mem_ctx, msg_user, "userPassword",
- auth_struct.incoming.current->array[i].AuthInfo.clear.password);
-*/
- mdfour(hash.hash, auth_struct.incoming.current[i]->AuthInfo.clear.password,
- auth_struct.incoming.current[i]->AuthInfo.clear.size);
- samdb_msg_add_hash(trusted_domain_state->policy->sam_ldb,
- mem_ctx, msg_user, "unicodePwd",
- &hash);
- }
+ DATA_BLOB new_password = data_blob_const(auth_struct.incoming.current[i]->AuthInfo.clear.password,
+ auth_struct.incoming.current[i]->AuthInfo.clear.size);
+ ret = ldb_msg_add_value(msg_user, "clearTextPassword", &new_password, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
}
}
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index 470c27a075..9d4c897892 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -107,7 +107,7 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca
if (r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
char *encoded_account = ldb_binary_encode_string(mem_ctx, r->in.account_name);
- char *flatname;
+ const char *flatname;
if (!encoded_account) {
return NT_STATUS_NO_MEMORY;
}
@@ -370,7 +370,7 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call
creds->sid,
NULL, /* Don't have plaintext */
NULL, &r->in.new_password,
- false, /* This is not considered a password change */
+ true, /* Password change */
NULL, NULL);
return nt_status;
}
@@ -385,15 +385,14 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
struct creds_CredentialState *creds;
struct ldb_context *sam_ctx;
NTSTATUS nt_status;
- char new_pass[512];
- bool ret;
+ DATA_BLOB new_password;
struct samr_CryptPassword password_buf;
nt_status = dcesrv_netr_creds_server_step_check(dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
r->in.computer_name, mem_ctx,
- &r->in.credential, &r->out.return_authenticator,
- &creds);
+ &r->in.credential, &r->out.return_authenticator,
+ &creds);
NT_STATUS_NOT_OK_RETURN(nt_status);
sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
@@ -402,22 +401,20 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
}
memcpy(password_buf.data, r->in.new_password.data, 512);
- SIVAL(password_buf.data,512,r->in.new_password.length);
+ SIVAL(password_buf.data, 512, r->in.new_password.length);
creds_arcfour_crypt(creds, password_buf.data, 516);
- ret = decode_pw_buffer(password_buf.data, new_pass, sizeof(new_pass),
- STR_UNICODE);
- if (!ret) {
- DEBUG(3,("netr_ServerPasswordSet2: failed to decode password buffer\n"));
- return NT_STATUS_ACCESS_DENIED;
+ if (!extract_pw_from_buffer(mem_ctx, password_buf.data, &new_password)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ return NT_STATUS_WRONG_PASSWORD;
}
-
+
/* Using the sid for the account as the key, set the password */
nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
creds->sid,
- new_pass, /* we have plaintext */
+ &new_password, /* we have plaintext */
NULL, NULL,
- false, /* This is not considered a password change */
+ true, /* Password change */
NULL, NULL);
return nt_status;
}
diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c
index 8a855a7bdb..859fd03801 100644
--- a/source4/rpc_server/samr/samr_password.c
+++ b/source4/rpc_server/samr/samr_password.c
@@ -86,7 +86,8 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
}
msg = res[0];
- status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
+ status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ msg, &lm_pwd, &nt_pwd);
if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
ldb_transaction_cancel(sam_ctx);
return NT_STATUS_WRONG_PASSWORD;
@@ -183,8 +184,8 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
struct samr_OemChangePasswordUser2 *r)
{
NTSTATUS status;
- char new_pass[512];
- uint32_t new_pass_len;
+ DATA_BLOB new_password, new_unicode_password;
+ char *new_pass;
struct samr_CryptPassword *pwbuf = r->in.password;
struct ldb_context *sam_ctx;
struct ldb_dn *user_dn;
@@ -195,6 +196,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
DATA_BLOB lm_pwd_blob;
uint8_t new_lm_hash[16];
struct samr_Password lm_verifier;
+ ssize_t unicode_pw_len;
if (pwbuf == NULL) {
return NT_STATUS_INVALID_PARAMETER;
@@ -231,7 +233,8 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
user_dn = res[0]->dn;
- status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
+ status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ res[0], &lm_pwd, NULL);
if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
ldb_transaction_cancel(sam_ctx);
return NT_STATUS_WRONG_PASSWORD;
@@ -242,18 +245,33 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
data_blob_free(&lm_pwd_blob);
- if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
- STR_ASCII)) {
+ if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
ldb_transaction_cancel(sam_ctx);
DEBUG(3,("samr: failed to decode password buffer\n"));
return NT_STATUS_WRONG_PASSWORD;
}
+
+ if (convert_string_talloc(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
+ CH_DOS, CH_UNIX,
+ (const char *)new_password.data,
+ new_password.length,
+ (void **)&new_pass) == -1) {
+ DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
- /* check LM verifier */
- if (lm_pwd == NULL) {
+ unicode_pw_len = convert_string_talloc(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
+ CH_DOS, CH_UTF16,
+ (const char *)new_password.data,
+ new_password.length,
+ (void **)&new_unicode_password.data);
+ if (unicode_pw_len == -1) {
+ DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
ldb_transaction_cancel(sam_ctx);
return NT_STATUS_WRONG_PASSWORD;
}
+ new_unicode_password.length = unicode_pw_len;
E_deshash(new_pass, new_lm_hash);
E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
@@ -278,7 +296,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
* due to password policies */
status = samdb_set_password(sam_ctx, mem_ctx,
user_dn, NULL,
- mod, new_pass,
+ mod, &new_unicode_password,
NULL, NULL,
true, /* this is a user password change */
NULL,
@@ -320,7 +338,7 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
struct samr_ChangePasswordUser3 *r)
{
NTSTATUS status;
- char new_pass[512];
+ DATA_BLOB new_password;
struct ldb_context *sam_ctx = NULL;
struct ldb_dn *user_dn;
int ret;
@@ -369,7 +387,8 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
user_dn = res[0]->dn;
- status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
+ status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ res[0], &lm_pwd, &nt_pwd);
if (!NT_STATUS_IS_OK(status) ) {
goto failed;
}
@@ -384,40 +403,49 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
data_blob_free(&nt_pwd_blob);
- if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
- STR_UNICODE)) {
+ if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
+ ldb_transaction_cancel(sam_ctx);
DEBUG(3,("samr: failed to decode password buffer\n"));
- status = NT_STATUS_WRONG_PASSWORD;
- goto failed;
+ return NT_STATUS_WRONG_PASSWORD;
}
-
+
if (r->in.nt_verifier == NULL) {
status = NT_STATUS_WRONG_PASSWORD;
goto failed;
}
/* check NT verifier */
- E_md4hash(new_pass, new_nt_hash);
+ mdfour(new_nt_hash, new_password.data, new_password.length);
+
E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
status = NT_STATUS_WRONG_PASSWORD;
goto failed;
}
- /* check LM verifier */
+ /* check LM verifier (really not needed as we just checked the
+ * much stronger NT hash, but the RPC-SAMR test checks for
+ * this) */
if (lm_pwd && r->in.lm_verifier != NULL) {
- E_deshash(new_pass, new_lm_hash);
- E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
- if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
- status = NT_STATUS_WRONG_PASSWORD;
- goto failed;
+ char *new_pass;
+ if (convert_string_talloc(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
+ CH_UTF16, CH_UNIX,
+ (const char *)new_password.data,
+ new_password.length,
+ (void **)&new_pass) != -1) {
+ E_deshash(new_pass, new_lm_hash);
+ E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
+ if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
}
}
-
mod = ldb_msg_new(mem_ctx);
if (mod == NULL) {
- return NT_STATUS_NO_MEMORY;
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
}
mod->dn = ldb_dn_copy(mod, user_dn);
@@ -430,7 +458,7 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
* due to password policies */
status = samdb_set_password(sam_ctx, mem_ctx,
user_dn, NULL,
- mod, new_pass,
+ mod, &new_password,
NULL, NULL,
true, /* this is a user password change */
&reason,
@@ -517,7 +545,7 @@ NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
struct samr_CryptPassword *pwbuf)
{
NTSTATUS nt_status;
- char new_pass[512];
+ DATA_BLOB new_password;
DATA_BLOB session_key = data_blob(NULL, 0);
nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
@@ -527,17 +555,16 @@ NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
arcfour_crypt_blob(pwbuf->data, 516, &session_key);
- if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
- STR_UNICODE)) {
+ if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
DEBUG(3,("samr: failed to decode password buffer\n"));
return NT_STATUS_WRONG_PASSWORD;
}
-
+
/* set the password - samdb needs to know both the domain and user DNs,
so the domain password policy can be used */
return samdb_set_password(sam_ctx, mem_ctx,
account_dn, domain_dn,
- msg, new_pass,
+ msg, &new_password,
NULL, NULL,
false, /* This is a password set, not change */
NULL, NULL);
@@ -557,8 +584,7 @@ NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
struct samr_CryptPasswordEx *pwbuf)
{
NTSTATUS nt_status;
- char new_pass[512];
- uint32_t new_pass_len;
+ DATA_BLOB new_password;
DATA_BLOB co_session_key;
DATA_BLOB session_key = data_blob(NULL, 0);
struct MD5Context ctx;
@@ -580,17 +606,16 @@ NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
- if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
- STR_UNICODE)) {
+ if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
DEBUG(3,("samr: failed to decode password buffer\n"));
return NT_STATUS_WRONG_PASSWORD;
}
-
+
/* set the password - samdb needs to know both the domain and user DNs,
so the domain password policy can be used */
return samdb_set_password(sam_ctx, mem_ctx,
account_dn, domain_dn,
- msg, new_pass,
+ msg, &new_password,
NULL, NULL,
false, /* This is a password set, not change */
NULL, NULL);
diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py
index 427ca975b3..4764d0cafa 100644
--- a/source4/scripting/python/samba/provision.py
+++ b/source4/scripting/python/samba/provision.py
@@ -465,6 +465,7 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
"objectclass",
"samldb",
"kludge_acl",
+ "password_hash",
"operational"]
tdb_modules_list = [
"subtree_rename",
@@ -516,7 +517,7 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
"DOMAINDN_LDB": domaindn_ldb,
"SCHEMADN_MOD": "schema_fsmo,instancetype",
"CONFIGDN_MOD": "naming_fsmo,instancetype",
- "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
+ "DOMAINDN_MOD": "pdc_fsmo,instancetype",
"MODULES_LIST": ",".join(modules_list),
"TDB_MODULES_LIST": tdb_modules_list_as_string,
"MODULES_LIST2": ",".join(modules_list2),
diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c
index 3d4c594d05..f4ae5b35eb 100644
--- a/source4/torture/rpc/netlogon.c
+++ b/source4/torture/rpc/netlogon.c
@@ -29,6 +29,7 @@
#include "lib/cmdline/popt_common.h"
#include "torture/rpc/rpc.h"
#include "torture/rpc/netlogon.h"
+#include "../lib/crypto/crypto.h"
#include "libcli/auth/libcli_auth.h"
#include "librpc/gen_ndr/ndr_netlogon_c.h"
#include "librpc/gen_ndr/ndr_lsa_c.h"
@@ -77,11 +78,10 @@ static bool test_SetupCredentials(struct dcerpc_pipe *p, struct torture_context
struct netr_ServerAuthenticate a;
struct netr_Credential credentials1, credentials2, credentials3;
struct creds_CredentialState *creds;
- struct samr_Password mach_password;
- const char *plain_pass;
- const char *machine_name;
+ const struct samr_Password *mach_password;
+ const char *machine_name;
- plain_pass = cli_credentials_get_password(credentials);
+ mach_password = cli_credentials_get_nt_hash(credentials, tctx);
machine_name = cli_credentials_get_workstation(credentials);
torture_comment(tctx, "Testing ServerReqChallenge\n");
@@ -99,8 +99,6 @@ static bool test_SetupCredentials(struct dcerpc_pipe *p, struct torture_context
status = dcerpc_netr_ServerReqChallenge(p, tctx, &r);
torture_assert_ntstatus_ok(tctx, status, "ServerReqChallenge");
- E_md4hash(plain_pass, mach_password.hash);
-
a.in.server_name = NULL;
a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name);
a.in.secure_channel_type = SEC_CHAN_BDC;
@@ -109,7 +107,7 @@ static bool test_SetupCredentials(struct dcerpc_pipe *p, struct torture_context
a.out.credentials = &credentials3;
creds_client_init(creds, &credentials1, &credentials2,
- &mach_password, &credentials3,
+ mach_password, &credentials3,
0);
torture_comment(tctx, "Testing ServerAuthenticate\n");
@@ -142,12 +140,12 @@ bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx,
struct netr_ServerAuthenticate2 a;
struct netr_Credential credentials1, credentials2, credentials3;
struct creds_CredentialState *creds;
- struct samr_Password mach_password;
+ const struct samr_Password *mach_password;
const char *machine_name;
const char *plain_pass;
+ mach_password = cli_credentials_get_nt_hash(machine_credentials, tctx);
machine_name = cli_credentials_get_workstation(machine_credentials);
- plain_pass = cli_credentials_get_password(machine_credentials);
torture_comment(tctx, "Testing ServerReqChallenge\n");
@@ -164,8 +162,6 @@ bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx,
status = dcerpc_netr_ServerReqChallenge(p, tctx, &r);
torture_assert_ntstatus_ok(tctx, status, "ServerReqChallenge");
- E_md4hash(plain_pass, mach_password.hash);
-
a.in.server_name = NULL;
a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name);
a.in.secure_channel_type = sec_chan_type;
@@ -176,7 +172,7 @@ bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx,
a.out.credentials = &credentials3;
creds_client_init(creds, &credentials1, &credentials2,
- &mach_password, &credentials3,
+ mach_password, &credentials3,
negotiate_flags);
torture_comment(tctx, "Testing ServerAuthenticate2\n");
@@ -326,6 +322,24 @@ static bool test_SetPassword(struct torture_context *tctx,
}
/*
+ generate a random password for password change tests
+*/
+static DATA_BLOB netlogon_very_rand_pass(TALLOC_CTX *mem_ctx, int len)
+{
+ int i;
+ DATA_BLOB password = data_blob_talloc(mem_ctx, NULL, len * 2 /* number of unicode chars */);
+ generate_random_buffer(password.data, password.length);
+
+ for (i=0; i < len; i++) {
+ if (((uint16_t *)password.data)[i] == 0) {
+ ((uint16_t *)password.data)[i] = 1;
+ }
+ }
+
+ return password;
+}
+
+/*
try a change password for our machine account
*/
static bool test_SetPassword2(struct torture_context *tctx,
@@ -335,8 +349,10 @@ static bool test_SetPassword2(struct torture_context *tctx,
NTSTATUS status;
struct netr_ServerPasswordSet2 r;
const char *password;
+ DATA_BLOB new_random_pass;
struct creds_CredentialState *creds;
struct samr_CryptPassword password_buf;
+ struct samr_Password nt_hash;
if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
return false;
@@ -448,6 +464,37 @@ static bool test_SetPassword2(struct torture_context *tctx,
test_SetupCredentials(p, tctx, machine_credentials, &creds),
"ServerPasswordSet failed to actually change the password");
+ new_random_pass = netlogon_very_rand_pass(tctx, 128);
+
+ /* now try a random stream of bytes for a password */
+ set_pw_in_buffer(password_buf.data, &new_random_pass);
+
+ creds_arcfour_crypt(creds, password_buf.data, 516);
+
+ memcpy(r.in.new_password.data, password_buf.data, 512);
+ r.in.new_password.length = IVAL(password_buf.data, 512);
+
+ torture_comment(tctx,
+ "Testing a third ServerPasswordSet2 on machine account, with a compleatly random password\n");
+
+ creds_client_authenticator(creds, &r.in.credential);
+
+ status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet (3)");
+
+ if (!creds_client_check(creds, &r.out.return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ mdfour(nt_hash.hash, new_random_pass.data, new_random_pass.length);
+
+ cli_credentials_set_password(machine_credentials, NULL, CRED_UNINITIALISED);
+ cli_credentials_set_nt_hash(machine_credentials, &nt_hash, CRED_SPECIFIED);
+
+ torture_assert (tctx,
+ test_SetupCredentials(p, tctx, machine_credentials, &creds),
+ "ServerPasswordSet failed to actually change the password");
+
return true;
}
diff --git a/source4/torture/rpc/samr.c b/source4/torture/rpc/samr.c
index b3343e38d1..1f19b8ec3f 100644
--- a/source4/torture/rpc/samr.c
+++ b/source4/torture/rpc/samr.c
@@ -498,6 +498,24 @@ static char *samr_rand_pass(TALLOC_CTX *mem_ctx, int min_len)
}
/*
+ generate a random password for password change tests
+*/
+static DATA_BLOB samr_very_rand_pass(TALLOC_CTX *mem_ctx, int len)
+{
+ int i;
+ DATA_BLOB password = data_blob_talloc(mem_ctx, NULL, len * 2 /* number of unicode chars */);
+ generate_random_buffer(password.data, password.length);
+
+ for (i=0; i < len; i++) {
+ if (((uint16_t *)password.data)[i] == 0) {
+ ((uint16_t *)password.data)[i] = 1;
+ }
+ }
+
+ return password;
+}
+
+/*
generate a random password for password change tests (fixed length)
*/
static char *samr_rand_pass_fixed_len(TALLOC_CTX *mem_ctx, int len)
@@ -1841,6 +1859,156 @@ bool test_ChangePasswordUser3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
return ret;
}
+bool test_ChangePasswordRandomBytes(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char *account_string,
+ struct policy_handle *handle,
+ char **password)
+{
+ NTSTATUS status;
+ struct samr_ChangePasswordUser3 r;
+ struct samr_SetUserInfo s;
+ union samr_UserInfo u;
+ DATA_BLOB session_key;
+ DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+ uint8_t confounder[16];
+ struct MD5Context ctx;
+
+ bool ret = true;
+ struct lsa_String server, account;
+ struct samr_CryptPassword nt_pass;
+ struct samr_Password nt_verifier;
+ DATA_BLOB new_random_pass;
+ char *newpass;
+ char *oldpass;
+ uint8_t old_nt_hash[16], new_nt_hash[16];
+ NTTIME t;
+
+ new_random_pass = samr_very_rand_pass(mem_ctx, 128);
+
+ if (!*password) {
+ printf("Failing ChangePasswordUser3 as old password was NULL. Previous test failed?\n");
+ return false;
+ }
+
+ oldpass = *password;
+ server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p));
+ init_lsa_String(&account, account_string);
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = 25;
+
+ ZERO_STRUCT(u);
+
+ u.info25.info.fields_present = SAMR_FIELD_PASSWORD;
+
+ set_pw_in_buffer(u.info25.password.data, &new_random_pass);
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ generate_random_buffer((uint8_t *)confounder, 16);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, confounder, 16);
+ MD5Update(&ctx, session_key.data, session_key.length);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ arcfour_crypt_blob(u.info25.password.data, 516, &confounded_session_key);
+ memcpy(&u.info25.password.data[516], confounder, 16);
+
+ printf("Testing SetUserInfo level 25 (set password ex) with a password made up of only random bytes\n");
+
+ status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ }
+
+ printf("Testing ChangePasswordUser3 with a password made up of only random bytes\n");
+
+ mdfour(old_nt_hash, new_random_pass.data, new_random_pass.length);
+
+ new_random_pass = samr_very_rand_pass(mem_ctx, 128);
+
+ mdfour(new_nt_hash, new_random_pass.data, new_random_pass.length);
+
+ set_pw_in_buffer(nt_pass.data, &new_random_pass);
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.nt_password = &nt_pass;
+ r.in.nt_verifier = &nt_verifier;
+ r.in.lm_change = 0;
+ r.in.lm_password = NULL;
+ r.in.lm_verifier = NULL;
+ r.in.password3 = NULL;
+
+ unix_to_nt_time(&t, time(NULL));
+
+ status = dcerpc_samr_ChangePasswordUser3(p, mem_ctx, &r);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ if (r.out.reject && r.out.reject->reason != SAMR_REJECT_OTHER) {
+ printf("expected SAMR_REJECT_OTHER (%d), got %d\n",
+ SAMR_REJECT_OTHER, r.out.reject->reason);
+ return false;
+ }
+ /* Perhaps the server has a 'min password age' set? */
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("ChangePasswordUser3 failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ newpass = samr_rand_pass(mem_ctx, 128);
+
+ mdfour(old_nt_hash, new_random_pass.data, new_random_pass.length);
+
+ E_md4hash(newpass, new_nt_hash);
+
+ encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.nt_password = &nt_pass;
+ r.in.nt_verifier = &nt_verifier;
+ r.in.lm_change = 0;
+ r.in.lm_password = NULL;
+ r.in.lm_verifier = NULL;
+ r.in.password3 = NULL;
+
+ unix_to_nt_time(&t, time(NULL));
+
+ status = dcerpc_samr_ChangePasswordUser3(p, mem_ctx, &r);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ if (r.out.reject && r.out.reject->reason != SAMR_REJECT_OTHER) {
+ printf("expected SAMR_REJECT_OTHER (%d), got %d\n",
+ SAMR_REJECT_OTHER, r.out.reject->reason);
+ return false;
+ }
+ /* Perhaps the server has a 'min password age' set? */
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("ChangePasswordUser3 (on second random password) failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ *password = talloc_strdup(mem_ctx, newpass);
+ }
+
+ return ret;
+}
+
static bool test_GetMembersInAlias(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
struct policy_handle *alias_handle)
@@ -2061,7 +2229,11 @@ static bool test_user_ops(struct dcerpc_pipe *p,
if (!test_ChangePasswordUser2(p, tctx, base_acct_name, &password, samr_rand_pass(tctx, 4), false)) {
ret = false;
}
-
+
+ /* Try a compleatly random password */
+ if (!test_ChangePasswordRandomBytes(p, tctx, base_acct_name, user_handle, &password)) {
+ ret = false;
+ }
}
for (i = 0; password_fields[i]; i++) {
@@ -2554,7 +2726,7 @@ static bool test_CreateUser(struct dcerpc_pipe *p, struct torture_context *tctx,
status = dcerpc_samr_CreateUser(p, user_ctx, &r);
if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
- if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
printf("Server correctly refused create of '%s'\n", r.in.account_name->string);
return true;
} else {
@@ -2678,7 +2850,7 @@ static bool test_CreateUser2(struct dcerpc_pipe *p, struct torture_context *tctx
status = dcerpc_samr_CreateUser2(p, user_ctx, &r);
if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
- if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
printf("Server correctly refused create of '%s'\n", r.in.account_name->string);
continue;
} else {