diff options
-rw-r--r-- | source4/auth/ntlm/auth_sam.c | 2 | ||||
-rw-r--r-- | source4/dsdb/common/util.c | 88 | ||||
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/objectclass.c | 12 | ||||
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/password_hash.c | 195 | ||||
-rw-r--r-- | source4/kdc/kpasswdd.c | 37 | ||||
-rw-r--r-- | source4/libcli/auth/smbencrypt.c | 33 | ||||
-rw-r--r-- | source4/librpc/idl/drsblobs.idl | 2 | ||||
-rw-r--r-- | source4/rpc_server/lsa/dcesrv_lsa.c | 21 | ||||
-rw-r--r-- | source4/rpc_server/netlogon/dcerpc_netlogon.c | 27 | ||||
-rw-r--r-- | source4/rpc_server/samr/samr_password.c | 88 |
10 files changed, 322 insertions, 183 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..0a87c5ffe1 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/libcli/auth/smbencrypt.c b/source4/libcli/auth/smbencrypt.c index 3af7e45002..6eb8767140 100644 --- a/source4/libcli/auth/smbencrypt.c +++ b/source4/libcli/auth/smbencrypt.c @@ -536,9 +536,40 @@ 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; } + +/*********************************************************** + 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..336720ecc7 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; + char *new_pass; struct samr_CryptPassword *pwbuf = r->in.password; struct ldb_context *sam_ctx; struct ldb_dn *user_dn; @@ -231,7 +232,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,15 +244,18 @@ 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; } - - /* check LM verifier */ - if (lm_pwd == NULL) { + + 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; } @@ -278,7 +283,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_password, NULL, NULL, true, /* this is a user password change */ NULL, @@ -320,7 +325,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 +374,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 +390,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 +445,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 +532,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 +542,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 +571,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 +593,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); |