[SCM] Samba Shared Repository - branch master updated

Matthias Dieter Wallnöfer mdw at samba.org
Tue Aug 17 11:40:00 MDT 2010


The branch, master has been updated
       via  786c41b... s4:netlogon RPC server - "ServerPasswordSet" operations - introduce also here the new password change syntax
       via  4c8edc8... s4:kdc/kpasswdd.c - let the user change his own password with his own rights
       via  cd711da... s4:samr RPC server - samr_password.c - make real user password changes work
       via  2a423e0... s4:kdc/rpc server - adapt the "samdb_set_password" calls which perform password sets
       via  eb345eb... s4:samdb_set_password/samdb_set_password_sid - make more arguments "const"
       via  d72d7f9... s4:samdb_set_password/samdb_set_password_sid - make the adaptions to support the password change control
       via  35954bb... s4:password_hash LDB module - perform the adaptions to understand the new password change control
       via  23bd3a7... s4:acl LDB module - support password changes over the DSDB_CONTROL_PASSWORD_CHANGE_OID control
       via  895a9fb... s4:DSDB - DSDB_CONTROL_PASSWORD_CHANGE_OID - add a structure as value to the control
       via  bbb9dc8... s4:DSDB - rename the "DSDB_CONTROL_PASSWORD_CHANGE_OLD_PW_CHECKED_OID"
      from  7eebceb... Revert "waf: enable gccdeps in developer mode"

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 786c41b0954b541518d1096019e1ce7ca11e5e98
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date:   Sun Aug 15 21:51:14 2010 +0200

    s4:netlogon RPC server - "ServerPasswordSet" operations - introduce also here the new password change syntax

commit 4c8edc8f5e9db9a08b5d672707ce71d82efdad15
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date:   Tue Jul 6 18:16:32 2010 +0200

    s4:kdc/kpasswdd.c - let the user change his own password with his own rights
    
    Now it's finally possible that the user can change his password with a DSDB
    connection using his credentials.
    
    NOTICE: I had to extract the old password from the SAMDB since I was unable to
    find it somewhere else (authinfo for example).

commit cd711da6ca206a6fe6911c55882ee3e7e7ecedc9
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date:   Tue Jul 6 18:07:31 2010 +0200

    s4:samr RPC server - samr_password.c - make real user password changes work
    
    Now it's finally possible that the user can change his password with a DSDB
    connection using his credentials.

commit 2a423e05476cfae4d3465683831aa4d5f03bd3c9
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date:   Sun Aug 15 21:06:11 2010 +0200

    s4:kdc/rpc server - adapt the "samdb_set_password" calls which perform password sets

commit eb345ebedf06ccef420f4352d145c54c1d39efe3
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date:   Sun Aug 15 21:26:07 2010 +0200

    s4:samdb_set_password/samdb_set_password_sid - make more arguments "const"

commit d72d7f9c5f318b58e38a47d38debe72bb6f53891
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date:   Sun Aug 15 20:44:28 2010 +0200

    s4:samdb_set_password/samdb_set_password_sid - make the adaptions to support the password change control
    
    And introduce parameters to pass the old password hashes.

commit 35954bb3108ce1d2c05c729712ce881402bf3723
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date:   Sun Aug 15 20:31:30 2010 +0200

    s4:password_hash LDB module - perform the adaptions to understand the new password change control

commit 23bd3a74176be4a1f8d6d70b148ababee397cf8c
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date:   Thu Jul 8 16:00:19 2010 +0200

    s4:acl LDB module - support password changes over the DSDB_CONTROL_PASSWORD_CHANGE_OID control
    
    This control is used from the SAMR and "kpasswd" password changes. It is
    strictly private and means "this is a password change and not a password set".

commit 895a9fbbfb06f371f4dbab3174451ace50dbb2b7
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date:   Sun Aug 15 20:01:27 2010 +0200

    s4:DSDB - DSDB_CONTROL_PASSWORD_CHANGE_OID - add a structure as value to the control
    
    This contains the NT and/or LM hash of the password specified by the user.

commit bbb9dc806e4399c65dee9b5dc2cde0bfaa9609bd
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date:   Sun Aug 15 19:52:18 2010 +0200

    s4:DSDB - rename the "DSDB_CONTROL_PASSWORD_CHANGE_OLD_PW_CHECKED_OID"
    
    Rename it to "DSDB_CONTROL_PASSWORD_CHANGE_OID". This control will afterwards
    contain a record with the specified old password as NT and/or LM hash.

-----------------------------------------------------------------------

Summary of changes:
 source4/dsdb/common/util.c                     |   52 ++++++----
 source4/dsdb/samdb/ldb_modules/acl.c           |   16 +++-
 source4/dsdb/samdb/ldb_modules/password_hash.c |   39 +++++--
 source4/dsdb/samdb/samdb.h                     |    7 +-
 source4/kdc/kpasswdd.c                         |   49 ++++++++-
 source4/rpc_server/netlogon/dcerpc_netlogon.c  |   45 ++++++++-
 source4/rpc_server/samr/samr_password.c        |  130 ++++++++++++++----------
 source4/setup/schema_samba4.ldif               |    2 +-
 8 files changed, 248 insertions(+), 92 deletions(-)


Changeset truncated at 500 lines:

diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c
index 7fcc3bf..cf13b64 100644
--- a/source4/dsdb/common/util.c
+++ b/source4/dsdb/common/util.c
@@ -997,7 +997,7 @@ int samdb_msg_add_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struc
   add a samr_Password element to a message
 */
 int samdb_msg_add_hash(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
-		       const char *attr_name, struct samr_Password *hash)
+		       const char *attr_name, const struct samr_Password *hash)
 {
 	struct ldb_val val;
 	val.data = talloc_memdup(mem_ctx, hash->hash, 16);
@@ -2000,19 +2000,20 @@ int samdb_set_password_callback(struct ldb_request *req, struct ldb_reply *ares)
 /*
  * Sets the user password using plaintext UTF16 (attribute "new_password") or
  * LM (attribute "lmNewHash") or NT (attribute "ntNewHash") hash. Also pass
- * as parameter if it's a user change or not ("userChange"). The "rejectReason"
- * gives some more informations if the changed failed.
+ * the old LM and/or NT hash (attributes "lmOldHash"/"ntOldHash") if it is a
+ * user change or not. The "rejectReason" gives some more informations if the
+ * change failed.
  *
- * Results: NT_STATUS_OK, NT_STATUS_INTERNAL_DB_CORRUPTION,
- *   NT_STATUS_INVALID_PARAMETER, NT_STATUS_UNSUCCESSFUL,
+ * Results: NT_STATUS_OK, NT_STATUS_INVALID_PARAMETER, NT_STATUS_UNSUCCESSFUL,
  *   NT_STATUS_WRONG_PASSWORD, NT_STATUS_PASSWORD_RESTRICTION
  */
 NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 			    struct ldb_dn *user_dn, struct ldb_dn *domain_dn,
 			    const DATA_BLOB *new_password,
-			    struct samr_Password *lmNewHash,
-			    struct samr_Password *ntNewHash,
-			    bool user_change,
+			    const struct samr_Password *lmNewHash,
+			    const struct samr_Password *ntNewHash,
+			    const struct samr_Password *lmOldHash,
+			    const struct samr_Password *ntOldHash,
 			    enum samPwdChangeReason *reject_reason,
 			    struct samr_DomInfo1 **_dominfo)
 {
@@ -2070,12 +2071,23 @@ NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 		return NT_STATUS_NO_MEMORY;
         }
 
-	if (user_change) {
-		/* a user password change and we've checked already the old
-		 * password somewhere else (callers responsability) */
+	/* A password change operation */
+	if ((ntOldHash != NULL) || (lmOldHash != NULL)) {
+		struct dsdb_control_password_change *change;
+
+		change = talloc(req, struct dsdb_control_password_change);
+		if (change == NULL) {
+			talloc_free(req);
+			talloc_free(msg);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		change->old_nt_pwd_hash = ntOldHash;
+		change->old_lm_pwd_hash = lmOldHash;
+
 		ret = ldb_request_add_control(req,
-					      DSDB_CONTROL_PASSWORD_CHANGE_OLD_PW_CHECKED_OID,
-					      true, NULL);
+					      DSDB_CONTROL_PASSWORD_CHANGE_OID,
+					      true, change);
 		if (ret != LDB_SUCCESS) {
 			talloc_free(req);
 			talloc_free(msg);
@@ -2170,8 +2182,9 @@ NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 /*
  * Sets the user password using plaintext UTF16 (attribute "new_password") or
  * LM (attribute "lmNewHash") or NT (attribute "ntNewHash") hash. Also pass
- * as parameter if it's a user change or not ("userChange"). The "rejectReason"
- * gives some more informations if the changed failed.
+ * the old LM and/or NT hash (attributes "lmOldHash"/"ntOldHash") if it is a
+ * user change or not. The "rejectReason" gives some more informations if the
+ * change failed.
  *
  * This wrapper function for "samdb_set_password" takes a SID as input rather
  * than a user DN.
@@ -2187,9 +2200,10 @@ NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 				const struct dom_sid *user_sid,
 				const DATA_BLOB *new_password,
-				struct samr_Password *lmNewHash, 
-				struct samr_Password *ntNewHash,
-				bool user_change,
+				const struct samr_Password *lmNewHash,
+				const struct samr_Password *ntNewHash,
+				const struct samr_Password *lmOldHash,
+				const struct samr_Password *ntOldHash,
 				enum samPwdChangeReason *reject_reason,
 				struct samr_DomInfo1 **_dominfo) 
 {
@@ -2217,7 +2231,7 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 				       user_dn, NULL,
 				       new_password,
 				       lmNewHash, ntNewHash,
-				       user_change,
+				       lmOldHash, ntOldHash,
 				       reject_reason, _dominfo);
 	if (!NT_STATUS_IS_OK(nt_status)) {
 		ldb_transaction_cancel(ldb);
diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c
index 4eb27e9..9965e53 100644
--- a/source4/dsdb/samdb/ldb_modules/acl.c
+++ b/source4/dsdb/samdb/ldb_modules/acl.c
@@ -779,7 +779,21 @@ static int acl_check_password_rights(TALLOC_CTX *mem_ctx,
 		talloc_free(tmp_ctx);
 		return LDB_SUCCESS;
 	}
-	if (rep_attr_cnt > 0 || (add_attr_cnt != del_attr_cnt)) {
+
+	if (ldb_request_get_control(req,
+				    DSDB_CONTROL_PASSWORD_CHANGE_OID) != NULL) {
+		/* The "DSDB_CONTROL_PASSWORD_CHANGE_OID" control means that we
+		 * have a user password change and not a set as the message
+		 * looks like. In it's value blob it contains the NT and/or LM
+		 * hash of the old password specified by the user.
+		 * This control is used by the SAMR and "kpasswd" password
+		 * change mechanisms. */
+		ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
+					       GUID_DRS_USER_CHANGE_PASSWORD,
+					       SEC_ADS_CONTROL_ACCESS,
+					       sid);
+	}
+	else if (rep_attr_cnt > 0 || (add_attr_cnt != del_attr_cnt)) {
 		ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module),
 					       GUID_DRS_FORCE_CHANGE_PASSWORD,
 					       SEC_ADS_CONTROL_ACCESS,
diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c
index a3c06b6..42ee65d 100644
--- a/source4/dsdb/samdb/ldb_modules/password_hash.c
+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c
@@ -95,12 +95,12 @@ struct ph_context {
 	struct ldb_reply *search_res;
 
 	struct dsdb_control_password_change_status *status;
+	struct dsdb_control_password_change *change;
 
 	bool pwd_reset;
 
 	bool change_status;
 	bool hash_values;
-	bool change_old_pw_checked;
 };
 
 
@@ -1436,7 +1436,7 @@ static int check_password_restrictions(struct setup_password_fields_io *io)
 	ldb = ldb_module_get_ctx(io->ac->module);
 
 	/* First check the old password is correct, for password changes */
-	if (!io->ac->pwd_reset && !io->ac->change_old_pw_checked) {
+	if (!io->ac->pwd_reset) {
 		bool nt_hash_checked = false;
 
 		/* we need the old nt or lm hash given by the client */
@@ -1858,6 +1858,27 @@ static int setup_io(struct ph_context *ac,
 		       sizeof(io->og.lm_hash->hash)));
 	}
 
+	/*
+	 * Handles the password change control if it's specified. It has the
+	 * precedance and overrides already specified old password values of
+	 * change requests (but that shouldn't happen since the control is
+	 * fully internal and only used in conjunction with replace requests!).
+	 */
+	if (ac->change != NULL) {
+		io->og.nt_hash = NULL;
+		if (ac->change->old_nt_pwd_hash != NULL) {
+			io->og.nt_hash = talloc_memdup(io->ac,
+						       ac->change->old_nt_pwd_hash,
+						       sizeof(struct samr_Password));
+		}
+		io->og.lm_hash = NULL;
+		if (lpcfg_lanman_auth(lp_ctx) && (ac->change->old_lm_pwd_hash != NULL)) {
+			io->og.lm_hash = talloc_memdup(io->ac,
+						       ac->change->old_lm_pwd_hash,
+						       sizeof(struct samr_Password));
+		}
+	}
+
 	/* refuse the change if someone wants to change the clear-
 	   text and supply his own hashes at the same time... */
 	if ((io->n.cleartext_utf8 || io->n.cleartext_utf16)
@@ -1913,10 +1934,8 @@ static int setup_io(struct ph_context *ac,
 		ac->pwd_reset = true;
 	} else if (ac->req->operation == LDB_MODIFY) {
 		if (io->og.cleartext_utf8 || io->og.cleartext_utf16
-		    || io->og.nt_hash || io->og.lm_hash
-		    || ac->change_old_pw_checked) {
-			/* If we have an old password or the "change old
-			 * password checked" control specified then for sure it
+		    || io->og.nt_hash || io->og.lm_hash) {
+			/* If we have an old password specified then for sure it
 			 * is a user "password change" */
 			ac->pwd_reset = false;
 		} else {
@@ -1975,14 +1994,12 @@ static void ph_apply_controls(struct ph_context *ac)
 		ctrl->critical = false;
 	}
 
-	ac->change_old_pw_checked = false;
 	ctrl = ldb_request_get_control(ac->req,
-				       DSDB_CONTROL_PASSWORD_CHANGE_OLD_PW_CHECKED_OID);
+				       DSDB_CONTROL_PASSWORD_CHANGE_OID);
 	if (ctrl != NULL) {
-		ac->change_old_pw_checked = true;
+		ac->change = (struct dsdb_control_password_change *) ctrl->data;
 
-		/* Mark the "change old password checked" control as uncritical
-		 * (done) */
+		/* Mark the "change" control as uncritical (done) */
 		ctrl->critical = false;
 	}
 }
diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h
index 75aae7f..a3d8f79 100644
--- a/source4/dsdb/samdb/samdb.h
+++ b/source4/dsdb/samdb/samdb.h
@@ -80,7 +80,12 @@ struct dsdb_control_password_change_status {
 
 #define DSDB_CONTROL_PASSWORD_HASH_VALUES_OID "1.3.6.1.4.1.7165.4.3.9"
 
-#define DSDB_CONTROL_PASSWORD_CHANGE_OLD_PW_CHECKED_OID "1.3.6.1.4.1.7165.4.3.10"
+#define DSDB_CONTROL_PASSWORD_CHANGE_OID "1.3.6.1.4.1.7165.4.3.10"
+
+struct dsdb_control_password_change {
+	const struct samr_Password *old_nt_pwd_hash;
+	const struct samr_Password *old_lm_pwd_hash;
+};
 
 /**
    DSDB_CONTROL_APPLY_LINKS is internal to Samba4 - a token passed between repl_meta_data and linked_attributes modules
diff --git a/source4/kdc/kpasswdd.c b/source4/kdc/kpasswdd.c
index 9c02ac3..e08a514 100644
--- a/source4/kdc/kpasswdd.c
+++ b/source4/kdc/kpasswdd.c
@@ -33,6 +33,7 @@
 #include "auth/credentials/credentials_krb5.h"
 #include "auth/auth.h"
 #include "dsdb/samdb/samdb.h"
+#include "../lib/util/util_ldb.h"
 #include "rpc_server/dcerpc_server.h"
 #include "rpc_server/samr/proto.h"
 #include "libcli/security/security.h"
@@ -170,9 +171,49 @@ static bool kpasswdd_change_password(struct kdc_server *kdc,
 	NTSTATUS status;
 	enum samPwdChangeReason reject_reason;
 	struct samr_DomInfo1 *dominfo;
+	struct samr_Password *oldLmHash, *oldNtHash;
 	struct ldb_context *samdb;
+	const char * const attrs[] = { "dBCSPwd", "unicodePwd", NULL };
+	struct ldb_message **res;
+	int ret;
+
+	/* Connect to a SAMDB with system privileges for fetching the old pw
+	 * hashes. */
+	samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx,
+			      system_session(kdc->task->lp_ctx));
+	if (!samdb) {
+		return kpasswdd_make_error_reply(kdc, mem_ctx,
+						KRB5_KPASSWD_HARDERROR,
+						"Failed to open samdb",
+						reply);
+	}
+
+	/* Fetch the old hashes to get the old password in order to perform
+	 * the password change operation. Naturally it would be much better to
+	 * have a password hash from an authentication around but this doesn't
+	 * seem to be the case here. */
+	ret = gendb_search(samdb, mem_ctx, NULL, &res, attrs,
+			   "(&(objectClass=user)(sAMAccountName=%s))",
+			   session_info->server_info->account_name);
+	if (ret != 1) {
+		return kpasswdd_make_error_reply(kdc, mem_ctx,
+						KRB5_KPASSWD_ACCESSDENIED,
+						"No such user when changing password",
+						reply);
+	}
+
+	status = samdb_result_passwords(mem_ctx, kdc->task->lp_ctx, res[0],
+					&oldLmHash, &oldNtHash);
+	if (!NT_STATUS_IS_OK(status)) {
+		return kpasswdd_make_error_reply(kdc, mem_ctx,
+						KRB5_KPASSWD_ACCESSDENIED,
+						"Not permitted to change password",
+						reply);
+	}
 
-	samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, system_session(kdc->task->lp_ctx));
+	/* Start a SAM with user privileges for the password change */
+	samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx,
+			      session_info);
 	if (!samdb) {
 		return kpasswdd_make_error_reply(kdc, mem_ctx,
 						KRB5_KPASSWD_HARDERROR,
@@ -185,11 +226,11 @@ static bool kpasswdd_change_password(struct kdc_server *kdc,
 		  session_info->server_info->account_name,
 		  dom_sid_string(mem_ctx, session_info->security_token->user_sid)));
 
-	/* User password change */
+	/* Performs the password change */
 	status = samdb_set_password_sid(samdb, mem_ctx,
 					session_info->security_token->user_sid,
 					password, NULL, NULL,
-					true, /* this is a user password change */
+					oldLmHash, oldNtHash, /* this is a user password change */
 					&reject_reason,
 					&dominfo);
 	return kpasswd_make_pwchange_reply(kdc, mem_ctx,
@@ -377,7 +418,7 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
 			status = samdb_set_password(samdb, mem_ctx,
 						    set_password_on_dn, NULL,
 						    &password, NULL, NULL,
-						    false, /* this is not a user password change */
+						    NULL, NULL, /* this is not a user password change */
 						    &reject_reason, &dominfo);
 		}
 
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index 5893bd4..a599822 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -32,6 +32,7 @@
 #include "param/param.h"
 #include "lib/messaging/irpc.h"
 #include "librpc/gen_ndr/ndr_irpc.h"
+#include "../libcli/ldap/ldap_ndr.h"
 #include "cldap_server/cldap_server.h"
 #include "lib/tsocket/tsocket.h"
 
@@ -384,7 +385,11 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call
 {
 	struct netlogon_creds_CredentialState *creds;
 	struct ldb_context *sam_ctx;
+	const char * const attrs[] = { "unicodePwd", NULL };
+	struct ldb_message **res;
+	struct samr_Password *oldNtHash;
 	NTSTATUS nt_status;
+	int ret;
 
 	nt_status = dcesrv_netr_creds_server_step_check(dce_call,
 							mem_ctx,
@@ -400,12 +405,28 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call
 
 	netlogon_creds_des_decrypt(creds, r->in.new_password);
 
+	/* fetch the old password hashes (the NT hash has to exist) */
+
+	ret = gendb_search(sam_ctx, mem_ctx, NULL, &res, attrs,
+			   "(&(objectClass=user)(objectSid=%s))",
+			   ldap_encode_ndr_dom_sid(mem_ctx, creds->sid));
+	if (ret != 1) {
+		return NT_STATUS_WRONG_PASSWORD;
+	}
+
+	nt_status = samdb_result_passwords(mem_ctx,
+					   dce_call->conn->dce_ctx->lp_ctx,
+					   res[0], NULL, &oldNtHash);
+	if (!NT_STATUS_IS_OK(nt_status) || !oldNtHash) {
+		return NT_STATUS_WRONG_PASSWORD;
+	}
+
 	/* Using the sid for the account as the key, set the password */
 	nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
 					   creds->sid,
 					   NULL, /* Don't have plaintext */
 					   NULL, r->in.new_password,
-					   true, /* Password change */
+					   NULL, oldNtHash, /* Password change */
 					   NULL, NULL);
 	return nt_status;
 }
@@ -419,8 +440,12 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
 {
 	struct netlogon_creds_CredentialState *creds;
 	struct ldb_context *sam_ctx;
+	const char * const attrs[] = { "dBCSPwd", "unicodePwd", NULL };
+	struct ldb_message **res;
+	struct samr_Password *oldLmHash, *oldNtHash;
 	NTSTATUS nt_status;
 	DATA_BLOB new_password;
+	int ret;
 
 	struct samr_CryptPassword password_buf;
 
@@ -445,12 +470,28 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
 		return NT_STATUS_WRONG_PASSWORD;
 	}
 
+	/* fetch the old password hashes (at least one of both has to exist) */
+
+	ret = gendb_search(sam_ctx, mem_ctx, NULL, &res, attrs,
+			   "(&(objectClass=user)(objectSid=%s))",
+			   ldap_encode_ndr_dom_sid(mem_ctx, creds->sid));
+	if (ret != 1) {
+		return NT_STATUS_WRONG_PASSWORD;
+	}
+
+	nt_status = samdb_result_passwords(mem_ctx,
+					   dce_call->conn->dce_ctx->lp_ctx,
+					   res[0], &oldLmHash, &oldNtHash);
+	if (!NT_STATUS_IS_OK(nt_status) || (!oldLmHash && !oldNtHash)) {
+		return NT_STATUS_WRONG_PASSWORD;
+	}
+
 	/* Using the sid for the account as the key, set the password */
 	nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
 					   creds->sid,
 					   &new_password, /* we have plaintext */
 					   NULL, NULL,
-					   true, /* Password change */
+					   oldLmHash, oldNtHash, /* Password change */
 					   NULL, NULL);
 	return nt_status;
 }
diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c
index f7aa376..73ccff4 100644
--- a/source4/rpc_server/samr/samr_password.c
+++ b/source4/rpc_server/samr/samr_password.c
@@ -60,23 +60,19 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
 		return NT_STATUS_INVALID_PARAMETER_MIX;
 	}
 
-	/* To change a password we need to open as system */
-	sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(dce_call->conn->dce_ctx->lp_ctx));
+	/* Connect to a SAMDB with system privileges for fetching the old pw
+	 * hashes. */
+	sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+				dce_call->conn->dce_ctx->lp_ctx,
+				system_session(dce_call->conn->dce_ctx->lp_ctx));
 	if (sam_ctx == NULL) {
 		return NT_STATUS_INVALID_SYSTEM_SERVICE;
 	}
 
-	ret = ldb_transaction_start(sam_ctx);
-	if (ret != LDB_SUCCESS) {
-		DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
-		return NT_STATUS_TRANSACTION_ABORTED;
-	}
-
 	/* fetch the old hashes */
 	ret = gendb_search_dn(sam_ctx, mem_ctx,
 			      a_state->account_dn, &res, attrs);
 	if (ret != 1) {
-		ldb_transaction_cancel(sam_ctx);
 		return NT_STATUS_WRONG_PASSWORD;
 	}
 
@@ -84,7 +80,6 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
 					dce_call->conn->dce_ctx->lp_ctx,
 					res[0], &lm_pwd, &nt_pwd);
 	if (!NT_STATUS_IS_OK(status) || !nt_pwd) {
-		ldb_transaction_cancel(sam_ctx);
 		return NT_STATUS_WRONG_PASSWORD;
 	}
 
@@ -93,7 +88,6 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
 		D_P16(lm_pwd->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, lm_pwd, 16) != 0) {
-			ldb_transaction_cancel(sam_ctx);
 			return NT_STATUS_WRONG_PASSWORD;
 		}
 	}
@@ -102,7 +96,6 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
 	D_P16(nt_pwd->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, nt_pwd, 16) != 0) {
-		ldb_transaction_cancel(sam_ctx);
 		return NT_STATUS_WRONG_PASSWORD;
 	}
 	
@@ -111,7 +104,6 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
 	if (r->in.cross1_present && r->in.nt_cross && lm_pwd) {
 		D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
 		if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
-			ldb_transaction_cancel(sam_ctx);
 			return NT_STATUS_WRONG_PASSWORD;
 		}
 	}
@@ -121,18 +113,33 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
 	if (r->in.cross2_present && r->in.lm_cross && lm_pwd) {
 		D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
 		if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
-			ldb_transaction_cancel(sam_ctx);
 			return NT_STATUS_WRONG_PASSWORD;
 		}
 	}
 
-	/* setup password modify mods on the user DN specified.  This may fail
-	 * due to password policies.  */
+	/* Start a SAM with user privileges for the password change */
+	sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+				dce_call->conn->dce_ctx->lp_ctx,
+				dce_call->conn->auth_state.session_info);
+	if (sam_ctx == NULL) {


-- 
Samba Shared Repository


More information about the samba-cvs mailing list