diff options
Diffstat (limited to 'source4/rpc_server/samr')
-rw-r--r-- | source4/rpc_server/samr/dcesrv_samr.c | 4 | ||||
-rw-r--r-- | source4/rpc_server/samr/samdb.c | 105 | ||||
-rw-r--r-- | source4/rpc_server/samr/samr_password.c | 224 |
3 files changed, 285 insertions, 48 deletions
diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c index 0db3a4ff6a..7592c479fa 100644 --- a/source4/rpc_server/samr/dcesrv_samr.c +++ b/source4/rpc_server/samr/dcesrv_samr.c @@ -1715,8 +1715,8 @@ static NTSTATUS samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX /* the set password levels are handled separately */ case 24: - status = samr_set_password(a_state->sam_ctx, - dce_call, + status = samr_set_password(dce_call, + a_state->sam_ctx, a_state->account_dn, a_state->domain_state->domain_dn, mem_ctx, msg, diff --git a/source4/rpc_server/samr/samdb.c b/source4/rpc_server/samr/samdb.c index c7a2f770c0..1d5b90a22d 100644 --- a/source4/rpc_server/samr/samdb.c +++ b/source4/rpc_server/samr/samdb.c @@ -233,6 +233,32 @@ uint_t samdb_search_uint(void *ctx, } /* + search the sam for a single signed 64 bit integer attribute in exactly 1 record +*/ +int64_t samdb_search_int64(void *ctx, + TALLOC_CTX *mem_ctx, + int64_t default_value, + const char *basedn, + const char *attr_name, + const char *format, ...) +{ + va_list ap; + int count; + struct ldb_message **res; + const char * const attrs[2] = { attr_name, NULL }; + + va_start(ap, format); + count = samdb_search_v(ctx, mem_ctx, basedn, &res, attrs, format, ap); + va_end(ap); + + if (count != 1) { + return default_value; + } + + return samdb_result_int64(res[0], attr_name, default_value); +} + +/* search the sam for multipe records each giving a single string attribute return the number of matches, or -1 on error */ @@ -289,6 +315,14 @@ uint_t samdb_result_uint(struct ldb_message *msg, const char *attr, uint_t defau } /* + pull a (signed) int64 from a result set. +*/ +int64_t samdb_result_int64(struct ldb_message *msg, const char *attr, int64_t default_value) +{ + return ldb_msg_find_int64(msg, attr, default_value); +} + +/* pull a string from a result set. */ const char *samdb_result_string(struct ldb_message *msg, const char *attr, @@ -350,11 +384,11 @@ NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, const char } /* - pull a double (really a large integer) from a result set. + pull a uint64_t from a result set. */ -double samdb_result_double(struct ldb_message *msg, const char *attr, double default_value) +uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value) { - return ldb_msg_find_double(msg, attr, default_value); + return ldb_msg_find_uint64(msg, attr, default_value); } @@ -365,17 +399,20 @@ double samdb_result_double(struct ldb_message *msg, const char *attr, double def NTTIME samdb_result_allow_pwd_change(void *ctx, TALLOC_CTX *mem_ctx, const char *domain_dn, struct ldb_message *msg, const char *attr) { - double attr_time = samdb_result_double(msg, attr, 0); - if (attr_time > 0) { - const char *minPwdAge = samdb_search_string(ctx, mem_ctx, NULL, "minPwdAge", - "dn=%s", domain_dn); - if (minPwdAge) { - /* yes, this is a -= not a += as minPwdAge is stored as the negative - of the number of 100-nano-seconds */ - attr_time -= strtod(minPwdAge, NULL); - } + uint64_t attr_time = samdb_result_uint64(msg, attr, 0); + int64_t minPwdAge; + + if (attr_time == 0) { + return 0; } - return nttime_from_double_nt(attr_time); + + minPwdAge = samdb_search_int64(ctx, mem_ctx, 0, "minPwdAge", "dn=%s", domain_dn); + + /* yes, this is a -= not a += as minPwdAge is stored as the negative + of the number of 100-nano-seconds */ + attr_time -= minPwdAge; + + return attr_time; } /* @@ -385,17 +422,21 @@ NTTIME samdb_result_allow_pwd_change(void *ctx, TALLOC_CTX *mem_ctx, NTTIME samdb_result_force_pwd_change(void *ctx, TALLOC_CTX *mem_ctx, const char *domain_dn, struct ldb_message *msg, const char *attr) { - double attr_time = samdb_result_double(msg, attr, 0); - if (attr_time > 0) { - const char *maxPwdAge = samdb_search_string(ctx, mem_ctx, NULL, "maxPwdAge", - "dn=%s", domain_dn); - if (!maxPwdAge || strcmp(maxPwdAge, "0") == 0) { - attr_time = 0; - } else { - attr_time -= strtod(maxPwdAge, NULL); - } + uint64_t attr_time = samdb_result_uint64(msg, attr, 0); + int64_t maxPwdAge; + + if (attr_time == 0) { + return 0; } - return nttime_from_double_nt(attr_time); + + maxPwdAge = samdb_search_int64(ctx, mem_ctx, 0, "maxPwdAge", "dn=%s", domain_dn); + if (maxPwdAge == 0) { + attr_time = 0; + } else { + attr_time -= maxPwdAge; + } + + return attr_time; } /* @@ -721,12 +762,22 @@ 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) + add a (signed) int64_t element to a message +*/ +int samdb_msg_add_int64(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg, + const char *attr_name, int64_t v) +{ + const char *s = talloc_asprintf(mem_ctx, "%lld", v); + return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, s); +} + +/* + add a uint64_t element to a message */ -int samdb_msg_add_double(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg, - const char *attr_name, double v) +int samdb_msg_add_uint64(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg, + const char *attr_name, uint64_t v) { - const char *s = talloc_asprintf(mem_ctx, "%.0f", v); + const char *s = talloc_asprintf(mem_ctx, "%llu", v); return samdb_msg_add_string(ctx, mem_ctx, msg, attr_name, s); } diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c index 5b99e04fab..812e0c69b9 100644 --- a/source4/rpc_server/samr/samr_password.c +++ b/source4/rpc_server/samr/samr_password.c @@ -109,7 +109,8 @@ NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX status = samdb_set_password(a_state->sam_ctx, mem_ctx, a_state->account_dn, a_state->domain_state->domain_dn, - &mod, NULL, &new_lmPwdHash, &new_ntPwdHash, True); + &mod, NULL, &new_lmPwdHash, &new_ntPwdHash, + True, NULL); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -210,7 +211,7 @@ NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_ user_dn, domain_dn, &mod, new_pass, NULL, NULL, - True); + True, NULL); if (!NT_STATUS_IS_OK(status)) { samdb_close(sam_ctx); return status; @@ -241,10 +242,158 @@ NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX /* samr_ChangePasswordUser3 */ -NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, +NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, + TALLOC_CTX *mem_ctx, struct samr_ChangePasswordUser3 *r) -{ - DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR); +{ + NTSTATUS status; + char new_pass[512]; + uint32 new_pass_len; + void *sam_ctx = NULL; + const char *user_dn, *domain_dn = NULL; + int ret; + struct ldb_message **res, mod; + const char * const attrs[] = { "objectSid", "ntPwdHash", NULL }; + const char * const dom_attrs[] = { "minPwdLength", "pwdHistoryLength", + "pwdProperties", "minPwdAge", "maxPwdAge", + NULL }; + const char *domain_sid; + struct samr_Hash *ntPwdHash; + struct samr_DomInfo1 *dominfo; + struct samr_ChangeReject *reject; + uint32 reason = 0; + + ZERO_STRUCT(r->out); + + if (r->in.nt_password == NULL || + r->in.nt_verifier == NULL) { + status = NT_STATUS_INVALID_PARAMETER; + goto failed; + } + + /* this call doesn't take a policy handle, so we need to open + the sam db from scratch */ + sam_ctx = samdb_connect(); + if (sam_ctx == NULL) { + status = NT_STATUS_INVALID_SYSTEM_SERVICE; + goto failed; + } + + /* we need the users dn and the domain dn (derived from the + user SID). We also need the current lm and nt password hashes + in order to decrypt the incoming passwords */ + ret = samdb_search(sam_ctx, + mem_ctx, NULL, &res, attrs, + "(&(sAMAccountName=%s)(objectclass=user))", + r->in.account->name); + if (ret != 1) { + status = NT_STATUS_NO_SUCH_USER; + goto failed; + } + + user_dn = res[0]->dn; + + ret = samdb_result_hashes(mem_ctx, res[0], "ntPwdHash", &ntPwdHash); + if (ret != 1) { + status = NT_STATUS_WRONG_PASSWORD; + goto failed; + } + + /* decrypt the password we have been given */ + SamOEMhash(r->in.nt_password->data, ntPwdHash->hash, 516); + + if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass), + &new_pass_len, STR_UNICODE)) { + DEBUG(3,("samr: failed to decode password buffer\n")); + status = NT_STATUS_WRONG_PASSWORD; + goto failed; + } + + /* work out the domain dn */ + domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid"); + if (domain_sid == NULL) { + status = NT_STATUS_NO_SUCH_DOMAIN; + goto failed; + } + + domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn", + "(objectSid=%s)", domain_sid); + if (!domain_dn) { + status = NT_STATUS_INTERNAL_DB_CORRUPTION; + goto failed; + } + + + ZERO_STRUCT(mod); + mod.dn = talloc_strdup(mem_ctx, user_dn); + if (!mod.dn) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + /* set the password - samdb needs to know both the domain and user DNs, + so the domain password policy can be used */ + status = samdb_set_password(sam_ctx, mem_ctx, + user_dn, domain_dn, + &mod, new_pass, + NULL, NULL, + True, &reason); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + /* modify the samdb record */ + ret = samdb_replace(sam_ctx, mem_ctx, &mod); + if (ret != 0) { + status = NT_STATUS_UNSUCCESSFUL; + goto failed; + } + + samdb_close(sam_ctx); + return NT_STATUS_OK; + +failed: + if (sam_ctx) { + samdb_close(sam_ctx); + } + + /* on failure we need to fill in the reject reasons */ + dominfo = talloc_p(mem_ctx, struct samr_DomInfo1); + if (dominfo == NULL) { + return NT_STATUS_NO_MEMORY; + } + reject = talloc_p(mem_ctx, struct samr_ChangeReject); + if (reject == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCTP(dominfo); + ZERO_STRUCTP(reject); + + reject->reason = reason; + + r->out.dominfo = dominfo; + r->out.reject = reject; + + if (!domain_dn) { + return status; + } + + ret = samdb_search(sam_ctx, + mem_ctx, NULL, &res, dom_attrs, + "dn=%s", domain_dn); + if (ret != 1) { + status = NT_STATUS_NO_SUCH_USER; + goto failed; + } + + dominfo->min_pwd_len = samdb_result_uint(res[0], "minPwdLength", 0); + dominfo->password_properties = samdb_result_uint(res[0], "pwdProperties", 0); + dominfo->password_history = samdb_result_uint(res[0], "pwdHistoryLength", 0); + dominfo->max_password_age = samdb_result_int64(res[0], "maxPwdAge", 0); + dominfo->min_password_age = samdb_result_int64(res[0], "minPwdAge", 0); + + return status; } @@ -269,12 +418,13 @@ BOOL samdb_password_complexity_ok(const char *pass) changes (as is needed by some of the set user info levels) */ 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, - struct samr_Hash *lmNewHash, - struct samr_Hash *ntNewHash, - BOOL user_change) + const char *user_dn, const char *domain_dn, + struct ldb_message *mod, + const char *new_pass, + struct samr_Hash *lmNewHash, + struct samr_Hash *ntNewHash, + BOOL user_change, + uint32 *reject_reason) { const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory", "ntPwdHistory", "unicodePwd", @@ -284,7 +434,8 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx, "maxPwdAge", "minPwdAge", "minPwdLength", "pwdLastSet", NULL }; const char *unicodePwd; - double minPwdAge, pwdLastSet; + NTTIME pwdLastSet; + int64_t minPwdAge; uint_t minPwdLength, pwdProperties, pwdHistoryLength; uint_t userAccountControl, badPwdCount; struct samr_Hash *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash; @@ -295,12 +446,10 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx, 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); @@ -316,7 +465,7 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx, "ntPwdHistory", &ntPwdHistory); lmPwdHash = samdb_result_hash(res[0], "lmPwdHash"); ntPwdHash = samdb_result_hash(res[0], "ntPwdHash"); - pwdLastSet = samdb_result_double(res[0], "pwdLastSet", 0); + pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0); /* pull the domain parameters */ count = samdb_search(ctx, mem_ctx, NULL, &res, domain_attrs, "dn=%s", domain_dn); @@ -326,17 +475,23 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx, 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); + minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0); if (new_pass) { /* check the various password restrictions */ if (minPwdLength > str_charnum(new_pass)) { + if (reject_reason) { + *reject_reason = SAMR_REJECT_TOO_SHORT; + } return NT_STATUS_PASSWORD_RESTRICTION; } /* possibly check password complexity */ if (pwdProperties & DOMAIN_PASSWORD_COMPLEX && !samdb_password_complexity_ok(new_pass)) { + if (reject_reason) { + *reject_reason = SAMR_REJECT_COMPLEXITY; + } return NT_STATUS_PASSWORD_RESTRICTION; } @@ -351,25 +506,40 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx, if (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_double) { + 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 && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) { + if (reject_reason) { + *reject_reason = SAMR_REJECT_COMPLEXITY; + } return NT_STATUS_PASSWORD_RESTRICTION; } if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) { + if (reject_reason) { + *reject_reason = SAMR_REJECT_COMPLEXITY; + } return NT_STATUS_PASSWORD_RESTRICTION; } } @@ -380,23 +550,38 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx, if (pwdHistoryLength > 0) { if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) { + if (reject_reason) { + *reject_reason = SAMR_REJECT_COMPLEXITY; + } return NT_STATUS_PASSWORD_RESTRICTION; } if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) { + if (reject_reason) { + *reject_reason = SAMR_REJECT_COMPLEXITY; + } return NT_STATUS_PASSWORD_RESTRICTION; } if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) { + if (reject_reason) { + *reject_reason = SAMR_REJECT_COMPLEXITY; + } return NT_STATUS_PASSWORD_RESTRICTION; } } for (i=0; lmNewHash && i<lmPwdHistory_len;i++) { if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) { + if (reject_reason) { + *reject_reason = SAMR_REJECT_COMPLEXITY; + } return NT_STATUS_PASSWORD_RESTRICTION; } } for (i=0; ntNewHash && i<ntPwdHistory_len;i++) { if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) { + if (reject_reason) { + *reject_reason = SAMR_REJECT_COMPLEXITY; + } return NT_STATUS_PASSWORD_RESTRICTION; } } @@ -425,7 +610,7 @@ NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx, CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd")); } - CHECK_RET(samdb_msg_add_double(ctx, mem_ctx, mod, "pwdLastSet", now_double)); + CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt)); if (pwdHistoryLength == 0) { CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory")); @@ -509,6 +694,7 @@ NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call, account_dn, domain_dn, msg, new_pass, NULL, NULL, - False /* This is a password set, not change */); + False /* This is a password set, not change */, + NULL); } |