summaryrefslogtreecommitdiff
path: root/source4/dsdb
diff options
context:
space:
mode:
Diffstat (limited to 'source4/dsdb')
-rw-r--r--source4/dsdb/samdb/samdb.c317
-rw-r--r--source4/dsdb/samdb/samdb.h1
2 files changed, 318 insertions, 0 deletions
diff --git a/source4/dsdb/samdb/samdb.c b/source4/dsdb/samdb/samdb.c
index 0f6e57c9cf..35de578f38 100644
--- a/source4/dsdb/samdb/samdb.c
+++ b/source4/dsdb/samdb/samdb.c
@@ -27,6 +27,9 @@
#include "lib/ldb/include/ldb.h"
#include "lib/ldb/include/ldb_errors.h"
#include "libcli/security/proto.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/auth/proto.h"
+#include "libcli/ldap/ldap.h"
#include "system/time.h"
#include "system/filesys.h"
#include "db_wrap.h"
@@ -1036,3 +1039,317 @@ failed:
talloc_free(tmp_ctx);
return NULL;
}
+
+/*
+ check that a password is sufficiently complex
+*/
+static BOOL samdb_password_complexity_ok(const char *pass)
+{
+ return check_password_quality(pass);
+}
+
+
+
+/*
+ set the user password using plaintext, obeying any user or domain
+ password restrictions
+
+ note that this function doesn't actually store the result in the
+ database, it just fills in the "mod" structure with ldb modify
+ elements to setup the correct change when samdb_replace() is
+ called. This allows the caller to combine the change with other
+ changes (as is needed by some of the set user info levels)
+
+ The caller should probably have a transaction wrapping this
+*/
+_PUBLIC_ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
+ const struct ldb_dn *user_dn,
+ const struct ldb_dn *domain_dn,
+ struct ldb_message *mod,
+ const char *new_pass,
+ struct samr_Password *lmNewHash,
+ struct samr_Password *ntNewHash,
+ BOOL user_change,
+ BOOL restrictions,
+ enum samr_RejectReason *reject_reason,
+ struct samr_DomInfo1 **_dominfo)
+{
+ const char * const user_attrs[] = { "userAccountControl", "sambaLMPwdHistory",
+ "sambaNTPwdHistory",
+ "lmPwdHash", "ntPwdHash",
+ "objectSid",
+ "pwdLastSet", NULL };
+ const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
+ "maxPwdAge", "minPwdAge",
+ "minPwdLength", NULL };
+ NTTIME pwdLastSet;
+ int64_t minPwdAge;
+ uint_t minPwdLength, pwdProperties, pwdHistoryLength;
+ uint_t userAccountControl;
+ struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash;
+ struct samr_Password local_lmNewHash, local_ntNewHash;
+ int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
+ struct dom_sid *domain_sid;
+ struct ldb_message **res;
+ int count;
+ time_t now = time(NULL);
+ NTTIME now_nt;
+ int i;
+
+ /* we need to know the time to compute password age */
+ unix_to_nt_time(&now_nt, now);
+
+ /* pull all the user parameters */
+ count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
+ if (count != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
+ sambaLMPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
+ "sambaLMPwdHistory", &sambaLMPwdHistory);
+ sambaNTPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
+ "sambaNTPwdHistory", &sambaNTPwdHistory);
+ lmPwdHash = samdb_result_hash(mem_ctx, res[0], "lmPwdHash");
+ ntPwdHash = samdb_result_hash(mem_ctx, res[0], "ntPwdHash");
+ pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
+
+ if (domain_dn) {
+ /* pull the domain parameters */
+ count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
+ if (count != 1) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ } else {
+ /* work out the domain sid, and pull the domain from there */
+ domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
+ if (domain_sid == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs,
+ "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
+ if (count != 1) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ }
+
+ pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
+ pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
+ minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
+ minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
+
+ if (_dominfo) {
+ struct samr_DomInfo1 *dominfo;
+ /* on failure we need to fill in the reject reasons */
+ dominfo = talloc(mem_ctx, struct samr_DomInfo1);
+ if (dominfo == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ dominfo->min_password_length = minPwdLength;
+ dominfo->password_properties = pwdProperties;
+ dominfo->password_history_length = pwdHistoryLength;
+ dominfo->max_password_age = minPwdAge;
+ dominfo->min_password_age = minPwdAge;
+ *_dominfo = dominfo;
+ }
+
+ if (new_pass) {
+ /* check the various password restrictions */
+ if (restrictions && minPwdLength > strlen_m(new_pass)) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_TOO_SHORT;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* 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;
+ }
+
+ /* compute the new nt and lm hashes */
+ if (E_deshash(new_pass, local_lmNewHash.hash)) {
+ lmNewHash = &local_lmNewHash;
+ }
+ E_md4hash(new_pass, local_ntNewHash.hash);
+ ntNewHash = &local_ntNewHash;
+ }
+
+ if (restrictions && user_change) {
+ /* are all password changes disallowed? */
+ if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_OTHER;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* can this user change password? */
+ if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_OTHER;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* yes, this is a minus. The ages are in negative 100nsec units! */
+ if (pwdLastSet - minPwdAge > now_nt) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_OTHER;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* check the immediately past password */
+ if (pwdHistoryLength > 0) {
+ if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+
+ /* check the password history */
+ sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength);
+ sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength);
+
+ for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
+ if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+ for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) {
+ if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+ }
+
+#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.
+ * Modules in ldb will set all the appropriate
+ * hashes */
+ CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
+ "sambaPassword", new_pass));
+ } 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, "sambaPassword"));
+
+ if (lmNewHash) {
+ CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
+ } else {
+ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
+ }
+
+ if (ntNewHash) {
+ CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
+ } else {
+ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ set the user password using plaintext, obeying any user or domain
+ password restrictions
+
+ This wrapper function takes a SID as input, rather than a user DN,
+ and actually performs the password change
+
+*/
+_PUBLIC_ NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ const char *new_pass,
+ struct samr_Password *lmNewHash,
+ struct samr_Password *ntNewHash,
+ BOOL user_change,
+ BOOL restrictions,
+ enum samr_RejectReason *reject_reason,
+ struct samr_DomInfo1 **_dominfo)
+{
+ NTSTATUS nt_status;
+ struct ldb_dn *user_dn;
+ struct ldb_message *msg;
+ int ret;
+
+ ret = ldb_transaction_start(ctx);
+ if (ret) {
+ DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ user_dn = samdb_search_dn(ctx, mem_ctx, NULL,
+ "(&(objectSid=%s)(objectClass=user))",
+ ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
+ if (!user_dn) {
+ ldb_transaction_cancel(ctx);
+ DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
+ dom_sid_string(mem_ctx, user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ ldb_transaction_cancel(ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(msg, user_dn);
+ if (!msg->dn) {
+ ldb_transaction_cancel(ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = samdb_set_password(ctx, mem_ctx,
+ user_dn, NULL,
+ msg, new_pass,
+ lmNewHash, ntNewHash,
+ user_change, /* This is a password set, not change */
+ restrictions, /* run restriction tests */
+ reject_reason, _dominfo);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ ldb_transaction_cancel(ctx);
+ return nt_status;
+ }
+
+ /* modify the samdb record */
+ ret = samdb_replace(ctx, mem_ctx, msg);
+ if (ret != 0) {
+ ldb_transaction_cancel(ctx);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ ret = ldb_transaction_commit(ctx);
+ if (ret != 0) {
+ DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
+ ldb_dn_linearize(mem_ctx, msg->dn),
+ ldb_errstring(ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h
index 292307f423..36bff2f7ff 100644
--- a/source4/dsdb/samdb/samdb.h
+++ b/source4/dsdb/samdb/samdb.h
@@ -28,6 +28,7 @@ struct drsuapi_DsNameInfo1;
#include "librpc/gen_ndr/security.h"
#include "lib/ldb/include/ldb.h"
+#include "librpc/gen_ndr/samr.h"
#include "dsdb/samdb/samdb_proto.h"
#endif /* __SAMDB_H__ */