diff options
Diffstat (limited to 'source4/rpc_server/samr/samr_password.c')
-rw-r--r-- | source4/rpc_server/samr/samr_password.c | 224 |
1 files changed, 205 insertions, 19 deletions
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); } |