summaryrefslogtreecommitdiff
path: root/source4/rpc_server/samr
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2004-05-24 05:35:59 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:53:54 -0500
commit934f6fda20fd35c5ff34a2289d9d587ff3938634 (patch)
tree08ce59d17e2c086ab8c80f673f5aa9fa0a2c4e23 /source4/rpc_server/samr
parentb63cd185b921d2c248e99cb3eea7dfc882c92c90 (diff)
downloadsamba-934f6fda20fd35c5ff34a2289d9d587ff3938634.tar.gz
samba-934f6fda20fd35c5ff34a2289d9d587ff3938634.tar.bz2
samba-934f6fda20fd35c5ff34a2289d9d587ff3938634.zip
r839: password set/change in the samr server is complex enough that it
deserves its own C module (This used to be commit 2ba7ff824c32b3db037263ddcff9c876293ea284)
Diffstat (limited to 'source4/rpc_server/samr')
-rw-r--r--source4/rpc_server/samr/dcesrv_samr.c314
-rw-r--r--source4/rpc_server/samr/dcesrv_samr.h70
-rw-r--r--source4/rpc_server/samr/samdb.c222
-rw-r--r--source4/rpc_server/samr/samr_password.c508
4 files changed, 585 insertions, 529 deletions
diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c
index d1f3f8e028..0db3a4ff6a 100644
--- a/source4/rpc_server/samr/dcesrv_samr.c
+++ b/source4/rpc_server/samr/dcesrv_samr.c
@@ -22,55 +22,8 @@
#include "includes.h"
#include "rpc_server/common/common.h"
+#include "rpc_server/samr/dcesrv_samr.h"
-/*
- this type allows us to distinguish handle types
-*/
-enum samr_handle {
- SAMR_HANDLE_CONNECT,
- SAMR_HANDLE_DOMAIN,
- SAMR_HANDLE_USER,
- SAMR_HANDLE_GROUP,
- SAMR_HANDLE_ALIAS
-};
-
-
-/*
- state asscoiated with a samr_Connect*() operation
-*/
-struct samr_connect_state {
- int reference_count;
- void *sam_ctx;
- TALLOC_CTX *mem_ctx;
- uint32 access_mask;
-};
-
-/*
- state associated with a samr_OpenDomain() operation
-*/
-struct samr_domain_state {
- struct samr_connect_state *connect_state;
- int reference_count;
- void *sam_ctx;
- TALLOC_CTX *mem_ctx;
- uint32 access_mask;
- const char *domain_sid;
- const char *domain_name;
- const char *domain_dn;
-};
-
-/*
- state associated with a open account handle
-*/
-struct samr_account_state {
- struct samr_domain_state *domain_state;
- void *sam_ctx;
- TALLOC_CTX *mem_ctx;
- uint32 access_mask;
- const char *account_sid;
- const char *account_name;
- const char *account_dn;
-};
/*
@@ -1662,37 +1615,6 @@ static NTSTATUS samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CT
}
-/*
- set password via a samr_CryptPassword buffer
- this will in the 'msg' with modify operations that will update the user
- password when applied
-*/
-static NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
- struct samr_account_state *a_state, TALLOC_CTX *mem_ctx,
- struct ldb_message *msg,
- struct samr_CryptPassword *pwbuf)
-{
- char new_pass[512];
- uint32 new_pass_len;
- DATA_BLOB session_key = dce_call->conn->session_key;
-
- SamOEMhashBlob(pwbuf->data, 516, &session_key);
-
- if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
- &new_pass_len, STR_UNICODE)) {
- 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(a_state->sam_ctx, mem_ctx,
- a_state->account_dn, a_state->domain_state->domain_dn,
- msg, new_pass,
- NULL, NULL,
- False /* This is a password set, not change */);
-}
-
/*
samr_SetUserInfo
*/
@@ -1793,7 +1715,11 @@ 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(dce_call, a_state, mem_ctx, msg,
+ status = samr_set_password(a_state->sam_ctx,
+ dce_call,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
&r->in.info->info24.password);
break;
@@ -1819,232 +1745,6 @@ static NTSTATUS samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX
/*
- samr_ChangePasswordUser
-*/
-static NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
- struct samr_ChangePasswordUser *r)
-{
- struct dcesrv_handle *h;
- struct samr_account_state *a_state;
- struct ldb_message **res, mod, *msg;
- int ret;
- struct samr_Hash *lmPwdHash=NULL, *ntPwdHash=NULL;
- struct samr_Hash new_lmPwdHash, new_ntPwdHash, checkHash;
- NTSTATUS status = NT_STATUS_OK;
- const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , NULL };
-
- DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_USER);
-
- a_state = h->data;
-
- /* fetch the old hashes */
- ret = samdb_search(a_state->sam_ctx, mem_ctx, NULL, &res, attrs,
- "dn=%s", a_state->account_dn);
- if (ret != 1) {
- return NT_STATUS_INTERNAL_DB_CORRUPTION;
- }
- msg = res[0];
-
- /* basic sanity checking on parameters */
- if (!r->in.lm_present || !r->in.nt_present ||
- !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
- !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
- /* we should really handle a change with lm not
- present */
- return NT_STATUS_INVALID_PARAMETER_MIX;
- }
- if (!r->in.cross1_present || !r->in.nt_cross) {
- return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
- }
- if (!r->in.cross2_present || !r->in.lm_cross) {
- return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
- }
-
- ret = samdb_result_hashes(mem_ctx, msg, "lmPwdHash", &lmPwdHash);
- if (ret != 1) {
- return NT_STATUS_WRONG_PASSWORD;
- }
- ret = samdb_result_hashes(mem_ctx, msg, "ntPwdHash", &ntPwdHash);
- if (ret != 1) {
- return NT_STATUS_WRONG_PASSWORD;
- }
-
- /* decrypt and check the new lm hash */
- D_P16(lmPwdHash->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
- D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
- if (memcmp(checkHash.hash, lmPwdHash->hash, 16) != 0) {
- return NT_STATUS_WRONG_PASSWORD;
- }
-
- /* decrypt and check the new nt hash */
- D_P16(ntPwdHash->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
- D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
- if (memcmp(checkHash.hash, ntPwdHash->hash, 16) != 0) {
- return NT_STATUS_WRONG_PASSWORD;
- }
-
- /* check the nt cross hash */
- D_P16(lmPwdHash->hash, r->in.nt_cross->hash, checkHash.hash);
- if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
- return NT_STATUS_WRONG_PASSWORD;
- }
-
- /* check the lm cross hash */
- D_P16(ntPwdHash->hash, r->in.lm_cross->hash, checkHash.hash);
- if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
- return NT_STATUS_WRONG_PASSWORD;
- }
-
- ZERO_STRUCT(mod);
- mod.dn = talloc_strdup(mem_ctx, a_state->account_dn);
- if (!mod.dn) {
- return NT_STATUS_NO_MEMORY;
- }
-
- 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);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- /* modify the samdb record */
- ret = samdb_replace(a_state->sam_ctx, mem_ctx, &mod);
- if (ret != 0) {
- return NT_STATUS_UNSUCCESSFUL;
- }
-
- return NT_STATUS_OK;
-}
-
-/*
- samr_OemChangePasswordUser2
-*/
-static NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
- struct samr_OemChangePasswordUser2 *r)
-{
- NTSTATUS status;
- char new_pass[512];
- uint32 new_pass_len;
- struct samr_CryptPassword *pwbuf = r->in.password;
- void *sam_ctx;
- const char *user_dn, *domain_dn;
- int ret;
- struct ldb_message **res, mod;
- const char * const attrs[] = { "objectSid", "lmPwdHash", NULL };
- const char *domain_sid;
- struct samr_Hash *lmPwdHash;
-
- if (pwbuf == NULL) {
- return NT_STATUS_WRONG_PASSWORD;
- }
-
- /* 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) {
- return NT_STATUS_INVALID_SYSTEM_SERVICE;
- }
-
- /* we need the users dn and the domain dn (derived from the
- user SID). We also need the current lm password hash in
- order to decrypt the incoming password */
- ret = samdb_search(sam_ctx,
- mem_ctx, NULL, &res, attrs,
- "(&(sAMAccountName=%s)(objectclass=user))",
- r->in.account->name);
- if (ret != 1) {
- samdb_close(sam_ctx);
- return NT_STATUS_NO_SUCH_USER;
- }
-
- user_dn = res[0]->dn;
-
- ret = samdb_result_hashes(mem_ctx, res[0], "lmPwdHash", &lmPwdHash);
- if (ret != 1) {
- samdb_close(sam_ctx);
- return NT_STATUS_WRONG_PASSWORD;
- }
-
- /* decrypt the password we have been given */
- SamOEMhash(pwbuf->data, lmPwdHash->hash, 516);
-
- if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
- &new_pass_len, STR_ASCII)) {
- DEBUG(3,("samr: failed to decode password buffer\n"));
- samdb_close(sam_ctx);
- return NT_STATUS_WRONG_PASSWORD;
- }
-
- /* work out the domain dn */
- domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
- if (domain_sid == NULL) {
- samdb_close(sam_ctx);
- return NT_STATUS_NO_SUCH_USER;
- }
-
- domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
- "(objectSid=%s)", domain_sid);
- if (!domain_dn) {
- samdb_close(sam_ctx);
- return NT_STATUS_INTERNAL_DB_CORRUPTION;
- }
-
-
- ZERO_STRUCT(mod);
- mod.dn = talloc_strdup(mem_ctx, user_dn);
- if (!mod.dn) {
- samdb_close(sam_ctx);
- return NT_STATUS_NO_MEMORY;
- }
-
- /* 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);
- if (!NT_STATUS_IS_OK(status)) {
- samdb_close(sam_ctx);
- return status;
- }
-
- /* modify the samdb record */
- ret = samdb_replace(sam_ctx, mem_ctx, &mod);
- if (ret != 0) {
- samdb_close(sam_ctx);
- return NT_STATUS_UNSUCCESSFUL;
- }
-
- samdb_close(sam_ctx);
- return NT_STATUS_OK;
-}
-
-
-/*
- samr_ChangePasswordUser2
-*/
-static NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
- struct samr_ChangePasswordUser2 *r)
-{
- DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
-}
-
-
-/*
- samr_ChangePasswordUser3
-*/
-static NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
- struct samr_ChangePasswordUser3 *r)
-{
- DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
-}
-
-
-
-
-/*
samr_GetGroupsForUser
*/
static NTSTATUS samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
@@ -2388,7 +2088,7 @@ static NTSTATUS samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_
samr_ValidatePassword
*/
static NTSTATUS samr_ValidatePassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
- struct samr_ValidatePassword *r)
+ struct samr_ValidatePassword *r)
{
DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
}
diff --git a/source4/rpc_server/samr/dcesrv_samr.h b/source4/rpc_server/samr/dcesrv_samr.h
new file mode 100644
index 0000000000..980bb5e9cd
--- /dev/null
+++ b/source4/rpc_server/samr/dcesrv_samr.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the samr pipe - definitions
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ this type allows us to distinguish handle types
+*/
+enum samr_handle {
+ SAMR_HANDLE_CONNECT,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_HANDLE_USER,
+ SAMR_HANDLE_GROUP,
+ SAMR_HANDLE_ALIAS
+};
+
+
+/*
+ state asscoiated with a samr_Connect*() operation
+*/
+struct samr_connect_state {
+ int reference_count;
+ void *sam_ctx;
+ TALLOC_CTX *mem_ctx;
+ uint32 access_mask;
+};
+
+/*
+ state associated with a samr_OpenDomain() operation
+*/
+struct samr_domain_state {
+ struct samr_connect_state *connect_state;
+ int reference_count;
+ void *sam_ctx;
+ TALLOC_CTX *mem_ctx;
+ uint32 access_mask;
+ const char *domain_sid;
+ const char *domain_name;
+ const char *domain_dn;
+};
+
+/*
+ state associated with a open account handle
+*/
+struct samr_account_state {
+ struct samr_domain_state *domain_state;
+ void *sam_ctx;
+ TALLOC_CTX *mem_ctx;
+ uint32 access_mask;
+ const char *account_sid;
+ const char *account_name;
+ const char *account_dn;
+};
diff --git a/source4/rpc_server/samr/samdb.c b/source4/rpc_server/samr/samdb.c
index a0591af451..c7a2f770c0 100644
--- a/source4/rpc_server/samr/samdb.c
+++ b/source4/rpc_server/samr/samdb.c
@@ -872,225 +872,3 @@ int samdb_replace(void *ctx, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
return samdb_modify(ctx, mem_ctx, 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,
- struct samr_Hash *lmNewHash,
- struct samr_Hash *ntNewHash,
- BOOL user_change)
-{
- 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 local_lmNewHash, local_ntNewHash;
- int 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);
-
- if (new_pass) {
- /* check the various password restrictions */
- if (minPwdLength > str_charnum(new_pass)) {
- 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 */
- if (E_deshash(new_pass, local_lmNewHash.hash)) {
- lmNewHash = &local_lmNewHash;
- }
- E_md4hash(new_pass, local_ntNewHash.hash);
- ntNewHash = &local_ntNewHash;
- }
-
- if (user_change) {
- /* 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;
- }
-
- /* yes, this is a minus. The ages are in negative 100nsec units! */
- if (pwdLastSet - minPwdAge > now_double) {
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
-
- /* check the immediately past password */
- if (pwdHistoryLength > 0) {
- if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
- if (ntNewHash && 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 (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
- if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
- if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
- }
-
- for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
- if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
- return NT_STATUS_PASSWORD_RESTRICTION;
- }
- }
- for (i=0; ntNewHash && 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 */
- 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"));
- }
-
- if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
- (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
- CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
- "unicodePwd", new_pass));
- } else {
- CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
- }
-
- CHECK_RET(samdb_msg_add_double(ctx, mem_ctx, mod, "pwdLastSet", now_double));
-
- if (pwdHistoryLength == 0) {
- CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
- CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
- return NT_STATUS_OK;
- }
-
- /* store the password history */
- 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];
- }
-
- /* Don't store 'long' passwords in the LM history,
- but make sure to 'expire' one password off the other end */
- if (lmNewHash) {
- new_lmPwdHistory[0] = *lmNewHash;
- } else {
- ZERO_STRUCT(new_lmPwdHistory[0]);
- }
- lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
-
- if (ntNewHash) {
- new_ntPwdHistory[0] = *ntNewHash;
- } else {
- ZERO_STRUCT(new_ntPwdHistory[0]);
- }
- ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
-
- CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
- "lmPwdHistory",
- new_lmPwdHistory,
- lmPwdHistory_len));
-
- CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
- "ntPwdHistory",
- new_ntPwdHistory,
- ntPwdHistory_len));
- return NT_STATUS_OK;
-}
diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c
new file mode 100644
index 0000000000..bc31d82a6a
--- /dev/null
+++ b/source4/rpc_server/samr/samr_password.c
@@ -0,0 +1,508 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ samr server password set/change handling
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/samr/dcesrv_samr.h"
+
+/*
+ samr_ChangePasswordUser
+*/
+NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message **res, mod, *msg;
+ int ret;
+ struct samr_Hash *lmPwdHash=NULL, *ntPwdHash=NULL;
+ struct samr_Hash new_lmPwdHash, new_ntPwdHash, checkHash;
+ NTSTATUS status = NT_STATUS_OK;
+ const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , NULL };
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+
+ /* fetch the old hashes */
+ ret = samdb_search(a_state->sam_ctx, mem_ctx, NULL, &res, attrs,
+ "dn=%s", a_state->account_dn);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ /* basic sanity checking on parameters */
+ if (!r->in.lm_present || !r->in.nt_present ||
+ !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
+ !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
+ /* we should really handle a change with lm not
+ present */
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+ if (!r->in.cross1_present || !r->in.nt_cross) {
+ return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
+ }
+ if (!r->in.cross2_present || !r->in.lm_cross) {
+ return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
+ }
+
+ ret = samdb_result_hashes(mem_ctx, msg, "lmPwdHash", &lmPwdHash);
+ if (ret != 1) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ ret = samdb_result_hashes(mem_ctx, msg, "ntPwdHash", &ntPwdHash);
+ if (ret != 1) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* decrypt and check the new lm hash */
+ D_P16(lmPwdHash->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
+ D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
+ if (memcmp(checkHash.hash, lmPwdHash->hash, 16) != 0) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* decrypt and check the new nt hash */
+ D_P16(ntPwdHash->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
+ D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
+ if (memcmp(checkHash.hash, ntPwdHash->hash, 16) != 0) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* check the nt cross hash */
+ D_P16(lmPwdHash->hash, r->in.nt_cross->hash, checkHash.hash);
+ if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* check the lm cross hash */
+ D_P16(ntPwdHash->hash, r->in.lm_cross->hash, checkHash.hash);
+ if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ ZERO_STRUCT(mod);
+ mod.dn = talloc_strdup(mem_ctx, a_state->account_dn);
+ if (!mod.dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ 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);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* modify the samdb record */
+ ret = samdb_replace(a_state->sam_ctx, mem_ctx, &mod);
+ if (ret != 0) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_OemChangePasswordUser2
+*/
+NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OemChangePasswordUser2 *r)
+{
+ NTSTATUS status;
+ char new_pass[512];
+ uint32 new_pass_len;
+ struct samr_CryptPassword *pwbuf = r->in.password;
+ void *sam_ctx;
+ const char *user_dn, *domain_dn;
+ int ret;
+ struct ldb_message **res, mod;
+ const char * const attrs[] = { "objectSid", "lmPwdHash", NULL };
+ const char *domain_sid;
+ struct samr_Hash *lmPwdHash;
+
+ if (pwbuf == NULL) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* 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) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /* we need the users dn and the domain dn (derived from the
+ user SID). We also need the current lm password hash in
+ order to decrypt the incoming password */
+ ret = samdb_search(sam_ctx,
+ mem_ctx, NULL, &res, attrs,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ r->in.account->name);
+ if (ret != 1) {
+ samdb_close(sam_ctx);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ user_dn = res[0]->dn;
+
+ ret = samdb_result_hashes(mem_ctx, res[0], "lmPwdHash", &lmPwdHash);
+ if (ret != 1) {
+ samdb_close(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* decrypt the password we have been given */
+ SamOEMhash(pwbuf->data, lmPwdHash->hash, 516);
+
+ if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
+ &new_pass_len, STR_ASCII)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ samdb_close(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* work out the domain dn */
+ domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
+ if (domain_sid == NULL) {
+ samdb_close(sam_ctx);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
+ "(objectSid=%s)", domain_sid);
+ if (!domain_dn) {
+ samdb_close(sam_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+
+ ZERO_STRUCT(mod);
+ mod.dn = talloc_strdup(mem_ctx, user_dn);
+ if (!mod.dn) {
+ samdb_close(sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* 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);
+ if (!NT_STATUS_IS_OK(status)) {
+ samdb_close(sam_ctx);
+ return status;
+ }
+
+ /* modify the samdb record */
+ ret = samdb_replace(sam_ctx, mem_ctx, &mod);
+ if (ret != 0) {
+ samdb_close(sam_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ samdb_close(sam_ctx);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_ChangePasswordUser2
+*/
+NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_ChangePasswordUser3
+*/
+NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser3 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+
+/*
+ check that a password is sufficiently complex
+*/
+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,
+ struct samr_Hash *lmNewHash,
+ struct samr_Hash *ntNewHash,
+ BOOL user_change)
+{
+ 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 local_lmNewHash, local_ntNewHash;
+ int 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);
+
+ if (new_pass) {
+ /* check the various password restrictions */
+ if (minPwdLength > str_charnum(new_pass)) {
+ 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 */
+ if (E_deshash(new_pass, local_lmNewHash.hash)) {
+ lmNewHash = &local_lmNewHash;
+ }
+ E_md4hash(new_pass, local_ntNewHash.hash);
+ ntNewHash = &local_ntNewHash;
+ }
+
+ if (user_change) {
+ /* 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;
+ }
+
+ /* yes, this is a minus. The ages are in negative 100nsec units! */
+ if (pwdLastSet - minPwdAge > now_double) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* check the immediately past password */
+ if (pwdHistoryLength > 0) {
+ if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ if (ntNewHash && 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 (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+
+ for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
+ if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+ for (i=0; ntNewHash && 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 */
+ 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"));
+ }
+
+ if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
+ (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
+ CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
+ "unicodePwd", new_pass));
+ } else {
+ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
+ }
+
+ CHECK_RET(samdb_msg_add_double(ctx, mem_ctx, mod, "pwdLastSet", now_double));
+
+ if (pwdHistoryLength == 0) {
+ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
+ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
+ return NT_STATUS_OK;
+ }
+
+ /* store the password history */
+ 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];
+ }
+
+ /* Don't store 'long' passwords in the LM history,
+ but make sure to 'expire' one password off the other end */
+ if (lmNewHash) {
+ new_lmPwdHistory[0] = *lmNewHash;
+ } else {
+ ZERO_STRUCT(new_lmPwdHistory[0]);
+ }
+ lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
+
+ if (ntNewHash) {
+ new_ntPwdHistory[0] = *ntNewHash;
+ } else {
+ ZERO_STRUCT(new_ntPwdHistory[0]);
+ }
+ ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
+
+ CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
+ "lmPwdHistory",
+ new_lmPwdHistory,
+ lmPwdHistory_len));
+
+ CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
+ "ntPwdHistory",
+ new_ntPwdHistory,
+ ntPwdHistory_len));
+ return NT_STATUS_OK;
+}
+
+/*
+ set password via a samr_CryptPassword buffer
+ this will in the 'msg' with modify operations that will update the user
+ password when applied
+*/
+NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
+ void *sam_ctx,
+ const char *account_dn, const char *domain_dn,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct samr_CryptPassword *pwbuf)
+{
+ char new_pass[512];
+ uint32 new_pass_len;
+ DATA_BLOB session_key = dce_call->conn->session_key;
+
+ SamOEMhashBlob(pwbuf->data, 516, &session_key);
+
+ if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
+ &new_pass_len, STR_UNICODE)) {
+ 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,
+ NULL, NULL,
+ False /* This is a password set, not change */);
+}
+