summaryrefslogtreecommitdiff
path: root/source4/rpc_server/samr/samdb.c
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2004-05-10 11:23:50 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:51:55 -0500
commit0f581e4af943a7e5dfd71d1c308ac668f287aed3 (patch)
tree023b42ba3ffcd6e3a58f6fe9065ac4c51d7b551e /source4/rpc_server/samr/samdb.c
parent0997d02c50dfe1bd65db89e91705166d29b5ec56 (diff)
downloadsamba-0f581e4af943a7e5dfd71d1c308ac668f287aed3.tar.gz
samba-0f581e4af943a7e5dfd71d1c308ac668f287aed3.tar.bz2
samba-0f581e4af943a7e5dfd71d1c308ac668f287aed3.zip
r623: setUserInfo level 24 (password set) now works in the SAMR server. This includes all
of the password complexity, password history and other password restrictions. (This used to be commit cb070b9084d95cf5178edbef951b75eab62b7220)
Diffstat (limited to 'source4/rpc_server/samr/samdb.c')
-rw-r--r--source4/rpc_server/samr/samdb.c270
1 files changed, 270 insertions, 0 deletions
diff --git a/source4/rpc_server/samr/samdb.c b/source4/rpc_server/samr/samdb.c
index c6a85176da..5f59ce1883 100644
--- a/source4/rpc_server/samr/samdb.c
+++ b/source4/rpc_server/samr/samdb.c
@@ -372,6 +372,52 @@ NTTIME samdb_result_force_pwd_change(void *ctx, TALLOC_CTX *mem_ctx,
}
/*
+ pull a samr_Hash structutre from a result set.
+*/
+struct samr_Hash samdb_result_hash(struct ldb_message *msg, const char *attr)
+{
+ struct samr_Hash hash;
+ const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
+ ZERO_STRUCT(hash);
+ if (val) {
+ memcpy(hash.hash, val->data, MIN(val->length, 16));
+ }
+ return hash;
+}
+
+/*
+ pull an array of samr_Hash structutres from a result set.
+*/
+uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr, struct samr_Hash **hashes)
+{
+ uint_t count = 0;
+ const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
+ int i;
+
+ *hashes = NULL;
+ if (!val) {
+ return 0;
+ }
+ count = val->length / 16;
+ if (count == 0) {
+ return 0;
+ }
+
+ *hashes = talloc_array_p(mem_ctx, struct samr_Hash, count);
+ if (! *hashes) {
+ return 0;
+ }
+
+ for (i=0;i<count;i++) {
+ memcpy((*hashes)[i].hash, (i*16)+(char *)val->data, 16);
+ }
+
+ return count;
+}
+
+
+/*
pull a samr_LogonHours structutre from a result set.
*/
struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
@@ -591,6 +637,55 @@ int samdb_msg_add_uint(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
}
/*
+ add a double element to a message (actually a large integer)
+*/
+int samdb_msg_add_double(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, double v)
+{
+ const char *s = talloc_asprintf(mem_ctx, "%.0f", v);
+ return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, s);
+}
+
+/*
+ add a samr_Hash element to a message
+*/
+int samdb_msg_add_hash(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, struct samr_Hash hash)
+{
+ struct samdb_context *sam_ctx = ctx;
+ struct ldb_val val;
+ val.data = talloc(mem_ctx, 16);
+ val.length = 16;
+ if (!val.data) {
+ return -1;
+ }
+ memcpy(val.data, hash.hash, 16);
+ ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
+ return ldb_msg_add_value(sam_ctx->ldb, msg, attr_name, &val);
+}
+
+/*
+ add a samr_Hash array to a message
+*/
+int samdb_msg_add_hashes(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, struct samr_Hash *hashes, uint_t count)
+{
+ struct samdb_context *sam_ctx = ctx;
+ struct ldb_val val;
+ int i;
+ val.data = talloc(mem_ctx, count*16);
+ val.length = count*16;
+ if (!val.data) {
+ return -1;
+ }
+ for (i=0;i<count;i++) {
+ memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
+ }
+ ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
+ return ldb_msg_add_value(sam_ctx->ldb, msg, attr_name, &val);
+}
+
+/*
add a acct_flags element to a message
*/
int samdb_msg_add_acct_flags(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
@@ -682,3 +777,178 @@ int samdb_modify(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
ldb_set_alloc(sam_ctx->ldb, samdb_alloc, mem_ctx);
return ldb_modify(sam_ctx->ldb, msg);
}
+
+/*
+ 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
+*/
+NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
+ const char *user_dn, const char *domain_dn,
+ struct ldb_message *mod, const char *new_pass)
+{
+ const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
+ "ntPwdHistory", "unicodePwd",
+ "lmPwdHash", "ntPwdHash", "badPwdCount",
+ NULL };
+ const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
+ "maxPwdAge", "minPwdAge",
+ "minPwdLength", "pwdLastSet", NULL };
+ const char *unicodePwd;
+ double minPwdAge, pwdLastSet;
+ uint_t minPwdLength, pwdProperties, pwdHistoryLength;
+ uint_t userAccountControl, badPwdCount;
+ struct samr_Hash *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
+ struct samr_Hash *new_lmPwdHistory, *new_ntPwdHistory;
+ struct samr_Hash lmNewHash, ntNewHash;
+ uint_t lmPwdHistory_len, ntPwdHistory_len;
+ struct ldb_message **res;
+ int count;
+ time_t now = time(NULL);
+ NTTIME now_nt;
+ double now_double;
+ int i;
+
+ /* we need to know the time to compute password age */
+ unix_to_nt_time(&now_nt, now);
+ now_double = nttime_to_double_nt(now_nt);
+
+ /* pull all the user parameters */
+ count = samdb_search(ctx, mem_ctx, NULL, &res, user_attrs, "dn=%s", user_dn);
+ if (count != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
+ userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
+ badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
+ lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
+ "lmPwdHistory", &lmPwdHistory);
+ ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
+ "ntPwdHistory", &ntPwdHistory);
+ lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
+ ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
+ pwdLastSet = samdb_result_double(res[0], "pwdLastSet", 0);
+
+ /* pull the domain parameters */
+ count = samdb_search(ctx, mem_ctx, NULL, &res, domain_attrs, "dn=%s", domain_dn);
+ if (count != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ 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_double(res[0], "minPwdAge", 0);
+
+ /* are all password changes disallowed? */
+ if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* can this user change password? */
+ if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* check the various password restrictions */
+ if (minPwdLength > str_charnum(new_pass)) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* yes, this is a minus. The ages are in negative 100nsec units! */
+ if (pwdLastSet - minPwdAge > now_double) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* possibly check password complexity */
+ if (pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
+ !samdb_password_complexity_ok(new_pass)) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* compute the new nt and lm hashes */
+ E_deshash(new_pass, lmNewHash.hash);
+ E_md4hash(new_pass, ntNewHash.hash);
+
+ /* check the immediately past password */
+ if (pwdHistoryLength > 0 &&
+ (memcmp(lmNewHash.hash, lmPwdHash.hash, 16) == 0 ||
+ memcmp(ntNewHash.hash, ntPwdHash.hash, 16) == 0)) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* check the password history */
+ lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
+ ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
+
+ if (pwdHistoryLength > 0) {
+ if (strcmp(unicodePwd, new_pass) == 0 ||
+ memcmp(lmNewHash.hash, lmPwdHash.hash, 16) == 0 ||
+ memcmp(ntNewHash.hash, ntPwdHash.hash, 16) == 0) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+
+ for (i=0;i<lmPwdHistory_len;i++) {
+ if (memcmp(lmNewHash.hash, lmPwdHistory[i].hash, 16) == 0) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+ for (i=0;i<ntPwdHistory_len;i++) {
+ if (memcmp(ntNewHash.hash, ntPwdHistory[i].hash, 16) == 0) {
+ 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 */
+ CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
+ CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
+
+ if ((pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
+ (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
+ CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
+ "unicodePwd", new_pass));
+ }
+
+ if (pwdHistoryLength > 0) {
+ new_lmPwdHistory = talloc_array_p(mem_ctx, struct samr_Hash,
+ pwdHistoryLength);
+ if (!new_lmPwdHistory) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ new_ntPwdHistory = talloc_array_p(mem_ctx, struct samr_Hash,
+ pwdHistoryLength);
+ if (!new_ntPwdHistory) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
+ new_lmPwdHistory[i+1] = lmPwdHistory[i];
+ }
+ for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
+ new_ntPwdHistory[i+1] = ntPwdHistory[i];
+ }
+ new_lmPwdHistory[0] = lmNewHash;
+ new_ntPwdHistory[0] = ntNewHash;
+
+ CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
+ "lmPwdHistory",
+ new_lmPwdHistory,
+ MIN(pwdHistoryLength, lmPwdHistory_len+1)));
+ CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
+ "ntPwdHistory",
+ new_ntPwdHistory,
+ MIN(pwdHistoryLength, ntPwdHistory_len+1)));
+ }
+
+ CHECK_RET(samdb_msg_add_double(ctx, mem_ctx, mod, "pwdLastSet", now_double));
+
+ return NT_STATUS_OK;
+}