The Samba-Bugzilla – Attachment 16487 Details for
Bug 14611
CVE-2021-20251 [SECURITY] Bad password count not incremented atomically
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
patch for 4.11 (v1)
CVE-2021-20251-bad-pwd-count-atomic-for-4.11.patch (text/plain), 71.29 KB, created by
Andrew Bartlett
on 2021-03-02 02:43:34 UTC
(
hide
)
Description:
patch for 4.11 (v1)
Filename:
MIME Type:
Creator:
Andrew Bartlett
Created:
2021-03-02 02:43:34 UTC
Size:
71.29 KB
patch
obsolete
>From 94d88f50156958ac860d6afaa001be3cc91791e0 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Mon, 11 Jan 2021 12:11:35 -0800 >Subject: [PATCH 1/5] CVE-2021-20251: s3 ensure bad password count atomic > updates > >The bad password count is supposed to limit the number of failed login >attempt a user can make before being temporarily locked out, but race >conditions between processes have allowed determined attackers to make >many more than the specified number of attempts. This is especially >bad on constrained or overcommitted hardware. > >To fix this, after every password is checked (regardless of if a bad >password is detected), we reload the sam account information under a >user-specific mutex, ensuring we have an up to date bad password count. > >This also ensures we have a race-free lockout detection. > >Discovered by Nathaniel W. Turner. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14611 > >Signed-off-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source3/auth/check_samsec.c | 77 +++++++++++++++++++++++++++++++++++++ > 1 file changed, 77 insertions(+) > >diff --git a/source3/auth/check_samsec.c b/source3/auth/check_samsec.c >index 53b6da53dc1..4870fb00de2 100644 >--- a/source3/auth/check_samsec.c >+++ b/source3/auth/check_samsec.c >@@ -379,6 +379,8 @@ NTSTATUS check_sam_security(const DATA_BLOB *challenge, > const uint8_t *nt_pw; > const uint8_t *lm_pw; > uint32_t acct_ctrl; >+ char *mutex_name_by_user = NULL; >+ struct named_mutex *mtx = NULL; > > /* the returned struct gets kept on the server_info, by means > of a steal further down */ >@@ -418,6 +420,79 @@ NTSTATUS check_sam_security(const DATA_BLOB *challenge, > challenge, lm_pw, nt_pw, > user_info, &user_sess_key, &lm_sess_key); > >+ /* >+ * We must re-load the sam acount information under a mutex >+ * lock to ensure we don't miss any concurrent account lockout >+ * changes. >+ */ >+ >+ /* Clear out old sampass info. */ >+ TALLOC_FREE(sampass); >+ acct_ctrl = 0; >+ username = NULL; >+ nt_pw = NULL; >+ lm_pw = NULL; >+ >+ sampass = samu_new(mem_ctx); >+ if (sampass == NULL) { >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ mutex_name_by_user = talloc_asprintf(mem_ctx, >+ "check_sam_security_mutex_%s", >+ user_info->mapped.account_name); >+ if (mutex_name_by_user == NULL) { >+ nt_status = NT_STATUS_NO_MEMORY; >+ goto done; >+ } >+ >+ /* Grab the named mutex under root with 30 second timeout. */ >+ become_root(); >+ mtx = grab_named_mutex(mem_ctx, mutex_name_by_user, 30); >+ if (mtx != NULL) { >+ /* Re-load the account information if we got the mutex. */ >+ ret = pdb_getsampwnam(sampass, user_info->mapped.account_name); >+ } >+ unbecome_root(); >+ >+ /* Everything from here on until mtx is freed is done under the mutex.*/ >+ >+ if (mtx == NULL) { >+ DBG_ERR("Acquisition of mutex %s failed " >+ "for user %s\n", >+ mutex_name_by_user, >+ user_info->mapped.account_name); >+ nt_status = NT_STATUS_INTERNAL_ERROR; >+ goto done; >+ } >+ >+ if (!ret) { >+ /* >+ * Re-load of account failed. This could only happen if the >+ * user was deleted in the meantime. >+ */ >+ DBG_NOTICE("reload of user '%s' in passdb failed.\n", >+ user_info->mapped.account_name); >+ nt_status = NT_STATUS_NO_SUCH_USER; >+ goto done; >+ } >+ >+ /* Re-load the account control info. */ >+ acct_ctrl = pdb_get_acct_ctrl(sampass); >+ username = pdb_get_username(sampass); >+ >+ /* >+ * Check if the account is now locked out - now under the mutex. >+ * This can happen if the server is under >+ * a password guess attack and the ACB_AUTOLOCK is set by >+ * another process. >+ */ >+ if (acct_ctrl & ACB_AUTOLOCK) { >+ DBG_NOTICE("Account for user %s was locked out.\n", username); >+ nt_status = NT_STATUS_ACCOUNT_LOCKED_OUT; >+ goto done; >+ } >+ > /* Notify passdb backend of login success/failure. If not > NT_STATUS_OK the backend doesn't like the login */ > >@@ -510,6 +585,8 @@ done: > TALLOC_FREE(sampass); > data_blob_free(&user_sess_key); > data_blob_free(&lm_sess_key); >+ TALLOC_FREE(mutex_name_by_user); >+ TALLOC_FREE(mtx); > return nt_status; > } > >-- >2.25.1 > > >From bb37148dae5518100cd704a6214a3630706f3045 Mon Sep 17 00:00:00 2001 >From: Gary Lockyer <gary@catalyst.net.nz> >Date: Mon, 1 Feb 2021 15:39:00 +1300 >Subject: [PATCH 2/5] CVE-2021-20251: s4 rpc samr: Read the objectGUID > >Ensure that the objectGUID is read, it will be used to re-read the user >details inside a transaction by authsam_update_bad_pwd_count and >authsam_logon_success_accounting > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14611 > >Signed-off-by: Gary Lockyer <gary@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/rpc_server/samr/samr_password.c | 4 +++- > 1 file changed, 3 insertions(+), 1 deletion(-) > >diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c >index 7c441f38ce2..4ae69a4893a 100644 >--- a/source4/rpc_server/samr/samr_password.c >+++ b/source4/rpc_server/samr/samr_password.c >@@ -313,7 +313,9 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, > "msDS-ResultantPSO", > "msDS-User-Account-Control-Computed", > "badPwdCount", "badPasswordTime", >- "objectSid", NULL }; >+ "objectSid", >+ "objectGUID", >+ NULL }; > struct samr_Password *nt_pwd, *lm_pwd; > DATA_BLOB nt_pwd_blob; > struct samr_DomInfo1 *dominfo = NULL; >-- >2.25.1 > > >From f74d605615d3e7137da6a697010530a2d4b24e52 Mon Sep 17 00:00:00 2001 >From: Gary Lockyer <gary@catalyst.net.nz> >Date: Wed, 27 Jan 2021 14:24:58 +1300 >Subject: [PATCH 3/5] CVE-2021-20251: s4 auth: make bad password count > increment atomic > >Ensure that the bad password count is incremented atomically. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14611 > >Signed-off-by: Gary Lockyer <gary@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/auth/sam.c | 224 ++++++++++++++++++++++++++++++++++++++++----- > 1 file changed, 202 insertions(+), 22 deletions(-) > >diff --git a/source4/auth/sam.c b/source4/auth/sam.c >index 39e48c26b52..effcda79b21 100644 >--- a/source4/auth/sam.c >+++ b/source4/auth/sam.c >@@ -813,6 +813,82 @@ static int authsam_get_user_pso(struct ldb_context *sam_ctx, > return LDB_SUCCESS; > } > >+/* >+ * Re-read the bad password and successful logon data for a user. >+ * >+ * the passed user record, must contain the "objectGUID", as this is >+ * used as the re-read key >+ */ >+static NTSTATUS authsam_reread_user_logon_data( >+ struct ldb_context *sam_ctx, >+ TALLOC_CTX *mem_ctx, >+ const struct ldb_message *user_msg, >+ struct ldb_message **current, >+ struct GUID *guid) >+{ >+ const struct ldb_val *v = NULL; >+ NTSTATUS status = NT_STATUS_OK; >+ struct ldb_result *res = NULL; >+ uint16_t acct_flags = 0; >+ const char *attr_name = "msDS-User-Account-Control-Computed"; >+ >+ int ret; >+ >+ /* >+ * Get the GUID of the user account >+ */ >+ v = ldb_msg_find_ldb_val(user_msg, "objectGUID"); >+ if (v == NULL) { >+ DBG_ERR("Unable to get objectGUID for %s\n", >+ ldb_dn_get_linearized(user_msg->dn)); >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ >+ status = GUID_from_ndr_blob(v, guid); >+ if (!NT_STATUS_IS_OK(status)) { >+ DBG_ERR("Unable to extract objectGUID for %s\n", >+ ldb_dn_get_linearized(user_msg->dn)); >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ >+ /* >+ * Re-read the account details, using the GUID in case the DN >+ * is being changed. >+ * >+ * We re read all the attributes in user_attrs, rather than using a >+ * subset to ensure that we can reuse existing validation code. >+ */ >+ ret = dsdb_search_by_dn_guid( >+ sam_ctx, mem_ctx, &res, guid, user_attrs, 0); >+ if (ret != LDB_SUCCESS) { >+ DBG_ERR("Unable to re-read account control data for %s\n", >+ ldb_dn_get_linearized(user_msg->dn)); >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ >+ /* >+ * Ensure the account has not been locked out by another request >+ */ >+ v = ldb_msg_find_ldb_val(res->msgs[0], attr_name); >+ if (v == NULL || v->data == NULL) { >+ DBG_ERR("No %s attribute for %s\n", >+ attr_name, >+ ldb_dn_get_linearized(user_msg->dn)); >+ TALLOC_FREE(res); >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ acct_flags = samdb_result_acct_flags(res->msgs[0], attr_name); >+ if (acct_flags & ACB_AUTOLOCK) { >+ DBG_WARNING( >+ "Account for user %s was locked out.\n", >+ ldb_dn_get_linearized(user_msg->dn)); >+ TALLOC_FREE(res); >+ return NT_STATUS_ACCOUNT_LOCKED_OUT; >+ } >+ *current = res->msgs[0]; >+ return NT_STATUS_OK; >+} >+ > NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, > struct ldb_message *msg, > struct ldb_dn *domain_dn) >@@ -823,10 +899,13 @@ NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, > "pwdProperties", > NULL }; > int ret; >- NTSTATUS status; >- struct ldb_result *domain_res; >+ NTSTATUS status = NT_STATUS_OK; >+ struct ldb_result *domain_res = NULL; > struct ldb_message *msg_mod = NULL; >+ struct ldb_message *current = NULL; > struct ldb_message *pso_msg = NULL; >+ bool txn_active = false; >+ struct GUID guid; > TALLOC_CTX *mem_ctx; > > mem_ctx = talloc_new(msg); >@@ -847,20 +926,77 @@ NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, > * fallback to using the domain defaults so that we still > * record the bad password attempt > */ >- DBG_ERR("Error (%d) checking PSO for %s", >+ DBG_ERR("Error (%d) checking PSO for %s\n", > ret, ldb_dn_get_linearized(msg->dn)); > } > >- status = dsdb_update_bad_pwd_count(mem_ctx, sam_ctx, >- msg, domain_res->msgs[0], pso_msg, >- &msg_mod); >+ /* >+ * To ensure that the bad password count is updated atomically, >+ * we need to: >+ * begin a transaction >+ * re-read the account details, using the objectGUID >+ * update the bad password count >+ * commit the transaction. >+ */ >+ >+ /* >+ * Start a new transaction >+ */ >+ ret = ldb_transaction_start(sam_ctx); >+ if (ret != LDB_SUCCESS) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; >+ } >+ txn_active = true; >+ >+ /* >+ * Re-read the account details, using the GUID in case the DN >+ * is being changed. >+ */ >+ status = authsam_reread_user_logon_data( >+ sam_ctx, mem_ctx, msg, ¤t, &guid); > if (!NT_STATUS_IS_OK(status)) { >- TALLOC_FREE(mem_ctx); >- return status; >+ /* The re-read can return account locked out, as well >+ * as an internal error >+ */ >+ goto error; > } > >+ /* >+ * Update the bad password count and if required lock the account >+ */ >+ status = dsdb_update_bad_pwd_count( >+ mem_ctx, >+ sam_ctx, >+ current, >+ domain_res->msgs[0], >+ pso_msg, >+ &msg_mod); >+ if (!NT_STATUS_IS_OK(status)) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; >+ } >+ >+ /* >+ * Write the data back to disk if required. >+ */ > if (msg_mod != NULL) { > struct ldb_request *req; >+ struct ldb_dn *dn; >+ >+ /* >+ * Use the GUID, in case the DN has been changed >+ */ >+ dn = ldb_dn_new_fmt( >+ mem_ctx, >+ sam_ctx, >+ "<GUID=%s>", >+ GUID_string(mem_ctx, &guid)); >+ if (dn == NULL) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; >+ } >+ msg_mod->dn = ldb_dn_copy(msg_mod, dn); > > ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx, > msg_mod, >@@ -869,35 +1005,79 @@ NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, > ldb_op_default_callback, > NULL); > if (ret != LDB_SUCCESS) { >- goto done; >+ TALLOC_FREE(msg_mod); >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; > } > >- ret = ldb_request_add_control(req, >- DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE, >- false, NULL); >+ ret = ldb_request_add_control( >+ req, >+ DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE, >+ false, >+ NULL); > if (ret != LDB_SUCCESS) { > talloc_free(req); >- goto done; >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; > } > >- ret = dsdb_autotransaction_request(sam_ctx, req); >+ /* >+ * As we're in a transaction, make the ldb request directly >+ * to avoid the nested transaction that would result if we >+ * called dsdb_autotransaction_request >+ */ >+ ret = ldb_request(sam_ctx, req); >+ if (ret == LDB_SUCCESS) { >+ ret = ldb_wait(req->handle, LDB_WAIT_ALL); >+ } > talloc_free(req); >+ if (ret != LDB_SUCCESS) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; >+ } > } > >-done: >+ /* >+ * Note that we may not have updated the user record, but >+ * committing the transaction in that case is still the correct >+ * thing to do. >+ * If the transaction was cancelled, this would be logged by >+ * the dsdb audit log as a failure. When in fact it is expected >+ * behaviour. >+ */ >+ TALLOC_FREE(mem_ctx); >+ ret = ldb_transaction_commit(sam_ctx); > if (ret != LDB_SUCCESS) { >- DBG_ERR("Failed to update badPwdCount, badPasswordTime or " >- "set lockoutTime on %s: %s\n", >- ldb_dn_get_linearized(msg->dn), >- ldb_errstring(sam_ctx)); >- TALLOC_FREE(mem_ctx); >+ DBG_ERR("Error (%d) %s, committing transaction," >+ " while updating bad password count" >+ " for (%s)\n", >+ ret, >+ ldb_errstring(sam_ctx), >+ ldb_dn_get_linearized(msg->dn)); > return NT_STATUS_INTERNAL_ERROR; > } >+ return NT_STATUS_OK; > >+error: >+ DBG_ERR("Failed to update badPwdCount, badPasswordTime or " >+ "set lockoutTime on %s: %s\n", >+ ldb_dn_get_linearized(msg->dn), >+ ldb_errstring(sam_ctx) != NULL ? >+ ldb_errstring(sam_ctx) :nt_errstr(status)); >+ if (txn_active) { >+ ret = ldb_transaction_cancel(sam_ctx); >+ if (ret != LDB_SUCCESS) { >+ DBG_ERR("Error rolling back transaction," >+ " while updating bad password count" >+ " on %s: %s\n", >+ ldb_dn_get_linearized(msg->dn), >+ ldb_errstring(sam_ctx)); >+ } >+ } > TALLOC_FREE(mem_ctx); >- return NT_STATUS_OK; >-} >+ return status; > >+} > > static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx, > struct ldb_message *msg_mod, >-- >2.25.1 > > >From c5659c495e736df03d011e32e6f29dc79b1217d0 Mon Sep 17 00:00:00 2001 >From: Gary Lockyer <gary@catalyst.net.nz> >Date: Tue, 2 Feb 2021 12:06:16 +1300 >Subject: [PATCH 4/5] CVE-2021-20251: s4 auth: make logon success accounting > update atomic > >Ensure that the successful logon accounting data is updated atomically. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14611 > >Signed-off-by: Gary Lockyer <gary@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/auth/sam.c | 194 +++++++++++++++++++++++++++++++++------------ > 1 file changed, 142 insertions(+), 52 deletions(-) > >diff --git a/source4/auth/sam.c b/source4/auth/sam.c >index effcda79b21..804290abed8 100644 >--- a/source4/auth/sam.c >+++ b/source4/auth/sam.c >@@ -1215,7 +1215,7 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > struct netr_SendToSamBase **send_to_sam) > { > int ret; >- NTSTATUS status; >+ NTSTATUS status = NT_STATUS_OK; > int badPwdCount; > int dbBadPwdCount; > int64_t lockoutTime; >@@ -1225,30 +1225,66 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > NTTIME now; > NTTIME lastLogonTimestamp; > bool am_rodc = false; >+ bool txn_active = false; >+ struct ldb_message *current = NULL; >+ struct GUID guid; > > mem_ctx = talloc_new(msg); > if (mem_ctx == NULL) { > return NT_STATUS_NO_MEMORY; > } > >- lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0); >- dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0); >+ /* >+ * Any update of the last logon data, needs to be done inside a >+ * transaction. >+ * And the user data needs to be re-read, and the account re-checked >+ * for lockout >+ */ >+ >+ /* >+ * Start a new transaction >+ */ >+ ret = ldb_transaction_start(sam_ctx); >+ if (ret != LDB_SUCCESS) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; >+ } >+ txn_active = true; >+ >+ /* >+ * Re-read the account details, using the GUID in case the DN >+ * is being changed. >+ */ >+ status = authsam_reread_user_logon_data( >+ sam_ctx, mem_ctx, msg, ¤t, &guid); >+ if (!NT_STATUS_IS_OK(status)) { >+ /* The re-read can return account locked out, as well >+ * as an internal error >+ */ >+ goto error; >+ } >+ >+ lockoutTime = ldb_msg_find_attr_as_int64( >+ current, "lockoutTime", 0); >+ dbBadPwdCount = ldb_msg_find_attr_as_int( >+ current, "badPwdCount", 0); > if (interactive_or_kerberos) { > badPwdCount = dbBadPwdCount; > } else { >- badPwdCount = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx, >- domain_dn, msg); >+ badPwdCount = samdb_result_effective_badPwdCount( >+ sam_ctx, mem_ctx, domain_dn, current); > } > lastLogonTimestamp = >- ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0); >+ ldb_msg_find_attr_as_int64( >+ current, "lastLogonTimestamp", 0); > >- DEBUG(5, ("lastLogonTimestamp is %lld\n", >- (long long int)lastLogonTimestamp)); >+ DBG_INFO("lastLogonTimestamp is %lld\n", >+ (long long int)lastLogonTimestamp); > > msg_mod = ldb_msg_new(mem_ctx); > if (msg_mod == NULL) { >- TALLOC_FREE(mem_ctx); >- return NT_STATUS_NO_MEMORY; >+ status = NT_STATUS_NO_MEMORY; >+ goto error; > } > msg_mod->dn = msg->dn; > >@@ -1256,16 +1292,18 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > /* > * This implies "badPwdCount" = 0, see samldb_lockout_time() > */ >- ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0); >+ ret = samdb_msg_add_int( >+ sam_ctx, msg_mod, msg_mod, "lockoutTime", 0); > if (ret != LDB_SUCCESS) { >- TALLOC_FREE(mem_ctx); >- return NT_STATUS_NO_MEMORY; >+ status = NT_STATUS_NO_MEMORY; >+ goto error; > } > } else if (badPwdCount != 0) { >- ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0); >+ ret = samdb_msg_add_int( >+ sam_ctx, msg_mod, msg_mod, "badPwdCount", 0); > if (ret != LDB_SUCCESS) { >- TALLOC_FREE(mem_ctx); >- return NT_STATUS_NO_MEMORY; >+ status = NT_STATUS_NO_MEMORY; >+ goto error; > } > } > >@@ -1274,11 +1312,11 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > > if (interactive_or_kerberos || > (badPwdCount != 0 && lockoutTime == 0)) { >- ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod, >- "lastLogon", now); >+ ret = samdb_msg_add_int64( >+ sam_ctx, msg_mod, msg_mod, "lastLogon", now); > if (ret != LDB_SUCCESS) { >- TALLOC_FREE(mem_ctx); >- return NT_STATUS_NO_MEMORY; >+ status = NT_STATUS_NO_MEMORY; >+ goto error; > } > } > >@@ -1289,42 +1327,42 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > > logonCount += 1; > >- ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, >- "logonCount", logonCount); >+ ret = samdb_msg_add_int( >+ sam_ctx, msg_mod, msg_mod, "logonCount", logonCount); > if (ret != LDB_SUCCESS) { >- TALLOC_FREE(mem_ctx); >- return NT_STATUS_NO_MEMORY; >+ status = NT_STATUS_NO_MEMORY; >+ goto error; > } > } else { > /* Set an unset logonCount to 0 on first successful login */ > if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) { >- ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, >- "logonCount", 0); >+ ret = samdb_msg_add_int( >+ sam_ctx, msg_mod, msg_mod, "logonCount", 0); > if (ret != LDB_SUCCESS) { >- TALLOC_FREE(mem_ctx); >- return NT_STATUS_NO_MEMORY; >+ status = NT_STATUS_NO_MEMORY; >+ goto error; > } > } > } > > ret = samdb_rodc(sam_ctx, &am_rodc); > if (ret != LDB_SUCCESS) { >- TALLOC_FREE(mem_ctx); >- return NT_STATUS_INTERNAL_ERROR; >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; > } > > if (!am_rodc) { >- status = authsam_update_lastlogon_timestamp(sam_ctx, msg_mod, domain_dn, >- lastLogonTimestamp, now); >+ status = authsam_update_lastlogon_timestamp( >+ sam_ctx, msg_mod, domain_dn, lastLogonTimestamp, now); > if (!NT_STATUS_IS_OK(status)) { >- TALLOC_FREE(mem_ctx); >- return NT_STATUS_NO_MEMORY; >+ status = NT_STATUS_NO_MEMORY; >+ goto error; > } > } else { > /* Perform the (async) SendToSAM calls for MS-SAMS */ > if (dbBadPwdCount != 0 && send_to_sam != NULL) { > struct netr_SendToSamBase *base_msg; >- struct GUID guid = samdb_result_guid(msg, "objectGUID"); >+ guid = samdb_result_guid(msg, "objectGUID"); > base_msg = talloc_zero(msg, struct netr_SendToSamBase); > > base_msg->message_type = SendToSamResetBadPasswordCount; >@@ -1337,6 +1375,21 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > if (msg_mod->num_elements > 0) { > unsigned int i; > struct ldb_request *req; >+ struct ldb_dn *dn; >+ >+ /* >+ * Use the GUID, in case the DN has been changed >+ */ >+ dn = ldb_dn_new_fmt( >+ mem_ctx, >+ sam_ctx, >+ "<GUID=%s>", >+ GUID_string(mem_ctx, &guid)); >+ if (dn == NULL) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; >+ } >+ msg_mod->dn = ldb_dn_copy(msg_mod, dn); > > /* mark all the message elements as LDB_FLAG_MOD_REPLACE */ > for (i=0;i<msg_mod->num_elements;i++) { >@@ -1350,32 +1403,69 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > ldb_op_default_callback, > NULL); > if (ret != LDB_SUCCESS) { >- goto done; >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; > } > >- ret = ldb_request_add_control(req, >- DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE, >- false, NULL); >+ ret = ldb_request_add_control( >+ req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE, false, NULL); > if (ret != LDB_SUCCESS) { >- talloc_free(req); >- goto done; >+ TALLOC_FREE(req); >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; >+ } >+ /* >+ * As we're in a transaction, make the ldb request directly >+ * to avoid the nested transaction that would result if we >+ * called dsdb_autotransaction_request */ >+ ret = ldb_request(sam_ctx, req); >+ if (ret == LDB_SUCCESS) { >+ ret = ldb_wait(req->handle, LDB_WAIT_ALL); >+ } >+ TALLOC_FREE(req); >+ if (ret != LDB_SUCCESS) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; > } >- >- ret = dsdb_autotransaction_request(sam_ctx, req); >- talloc_free(req); > } > >-done: >+ /* >+ * Note that we may not have updated the user record, but >+ * committing the transaction in that case is still the correct >+ * thing to do. >+ * If the transaction was cancelled, this would be logged by >+ * the dsdb audit log as a failure. When in fact it is expected >+ * behaviour. >+ */ >+ TALLOC_FREE(mem_ctx); >+ ret = ldb_transaction_commit(sam_ctx); > if (ret != LDB_SUCCESS) { >- DEBUG(0, ("Failed to set badPwdCount and lockoutTime " >- "to 0 and/or lastlogon to now (%lld) " >- "%s: %s\n", (long long int)now, >- ldb_dn_get_linearized(msg_mod->dn), >- ldb_errstring(sam_ctx))); >- TALLOC_FREE(mem_ctx); >+ DBG_ERR("Error (%d) %s, committing transaction," >+ " while updating successful logon accounting" >+ " for (%s)\n", >+ ret, >+ ldb_errstring(sam_ctx), >+ ldb_dn_get_linearized(msg->dn)); > return NT_STATUS_INTERNAL_ERROR; > } >+ return NT_STATUS_OK; > >+error: >+ DBG_ERR("Failed to update badPwdCount, badPasswordTime or " >+ "set lockoutTime on %s: %s\n", >+ ldb_dn_get_linearized(msg->dn), >+ ldb_errstring(sam_ctx) != NULL ? >+ ldb_errstring(sam_ctx) :nt_errstr(status)); >+ if (txn_active) { >+ ret = ldb_transaction_cancel(sam_ctx); >+ if (ret != LDB_SUCCESS) { >+ DBG_ERR("Error rolling back transaction," >+ " while updating bad password count" >+ " on %s: %s\n", >+ ldb_dn_get_linearized(msg->dn), >+ ldb_errstring(sam_ctx)); >+ } >+ } > TALLOC_FREE(mem_ctx); >- return NT_STATUS_OK; >+ return status; > } >-- >2.25.1 > > >From f5593bef38ee5ffde4029b44de8af52e612081d5 Mon Sep 17 00:00:00 2001 >From: Gary Lockyer <gary@catalyst.net.nz> >Date: Tue, 9 Feb 2021 11:59:05 +1300 >Subject: [PATCH 5/5] CVE-2021-20251: s4 auth test: Unit tests for > source4/auth/sam.c > >cmocka unit tests for the authsam_reread_user_logon_data in >source4/auth/sam.c >The tests exercise the error paths to ensure that the error handling >functions correctly. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14611 > >(backported from commit in master) >[abartlet@samba.org: Resolved conflicts in tests.py due to other >tests - mdsparser_es - not being in 4.11] > >Signed-off-by: Gary Lockyer <gary@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/tests.py | 2 + > source4/auth/tests/sam.c | 1946 ++++++++++++++++++++++++++++++++++++ > source4/auth/wscript_build | 7 + > 3 files changed, 1955 insertions(+) > create mode 100644 source4/auth/tests/sam.c > >diff --git a/selftest/tests.py b/selftest/tests.py >index 7589566ec14..59070aa435d 100644 >--- a/selftest/tests.py >+++ b/selftest/tests.py >@@ -401,3 +401,5 @@ plantestsuite("samba.unittests.test_registry_regfio", "none", > [os.path.join(bindir(), "default/source3/test_registry_regfio")]) > plantestsuite("samba.unittests.test_oLschema2ldif", "none", > [os.path.join(bindir(), "default/source4/utils/oLschema2ldif/test_oLschema2ldif")]) >+plantestsuite("samba.unittests.auth.sam", "none", >+ [os.path.join(bindir(), "test_auth_sam")]) >diff --git a/source4/auth/tests/sam.c b/source4/auth/tests/sam.c >new file mode 100644 >index 00000000000..d9a376d6002 >--- /dev/null >+++ b/source4/auth/tests/sam.c >@@ -0,0 +1,1946 @@ >+/* >+ * Unit tests for source4/auth/sam.c >+ * >+ * Copyright (C) Catalyst.NET Ltd 2021 >+ * >+ * 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 3 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, see <http://www.gnu.org/licenses/>. >+ * >+ */ >+ >+/* >+ * from cmocka.c: >+ * These headers or their equivalents should be included prior to >+ * including >+ * this header file. >+ * >+ * #include <stdarg.h> >+ * #include <stddef.h> >+ * #include <setjmp.h> >+ * >+ * This allows test applications to use custom definitions of C standard >+ * library functions and types. >+ * >+ */ >+ >+#include <time.h> >+#include <stdlib.h> >+#include <stdarg.h> >+#include <stddef.h> >+#include <setjmp.h> >+#include <stdint.h> >+#include <cmocka.h> >+ >+#include "includes.h" >+#include "auth/sam.c" >+#include "ldb.h" >+#include "ntstatus.h" >+ >+/***************************************************************************** >+ * Mock implementations >+ *****************************************************************************/ >+int dsdb_search_by_dn_guid_ret = LDB_SUCCESS; >+struct ldb_result *dsdb_search_by_dn_guid_res = NULL; >+int dsdb_search_by_dn_guid(struct ldb_context *ldb, >+ TALLOC_CTX *mem_ctx, >+ struct ldb_result **_result, >+ const struct GUID *guid, >+ const char * const *attrs, >+ uint32_t dsdb_flags) >+{ >+ *_result = talloc_steal(mem_ctx, dsdb_search_by_dn_guid_res); >+ return dsdb_search_by_dn_guid_ret; >+} >+ >+int dsdb_search_dn_ret = LDB_SUCCESS; >+struct ldb_result *dsdb_search_dn_res = NULL; >+int dsdb_search_dn(struct ldb_context *ldb, >+ TALLOC_CTX *mem_ctx, >+ struct ldb_result **_result, >+ struct ldb_dn *basedn, >+ const char * const *attrs, >+ uint32_t dsdb_flags) >+{ >+ *_result = talloc_steal(mem_ctx, dsdb_search_dn_res); >+ return dsdb_search_dn_ret; >+} >+ >+int ldb_transaction_start_ret = LDB_SUCCESS; >+bool in_transaction = false; >+int ldb_transaction_start(struct ldb_context *ldb) { >+ if (ldb_transaction_start_ret == LDB_SUCCESS) { >+ in_transaction = true; >+ } >+ return ldb_transaction_start_ret; >+} >+ >+int ldb_transaction_cancel_ret = LDB_SUCCESS; >+bool transaction_cancelled = false; >+int ldb_transaction_cancel(struct ldb_context *ldb) { >+ assert_true(in_transaction); >+ if (ldb_transaction_cancel_ret == LDB_SUCCESS) { >+ in_transaction = false; >+ transaction_cancelled = true; >+ } >+ return ldb_transaction_cancel_ret; >+} >+ >+int ldb_transaction_commit_ret = LDB_SUCCESS; >+bool transaction_committed = false; >+int ldb_transaction_commit(struct ldb_context *ldb) { >+ assert_true(in_transaction); >+ if (ldb_transaction_commit_ret == LDB_SUCCESS) { >+ in_transaction = false; >+ transaction_committed = true; >+ } >+ return ldb_transaction_commit_ret; >+} >+ >+NTSTATUS dsdb_update_bad_pwd_count_ret = NT_STATUS_OK; >+struct ldb_message *dsdb_update_bad_pwd_count_res = NULL; >+NTSTATUS dsdb_update_bad_pwd_count(TALLOC_CTX *mem_ctx, >+ struct ldb_context *sam_ctx, >+ struct ldb_message *user_msg, >+ struct ldb_message *domain_msg, >+ struct ldb_message *pso_msg, >+ struct ldb_message **_mod_msg) { >+ >+ *_mod_msg = talloc_steal(mem_ctx, dsdb_update_bad_pwd_count_res); >+ return dsdb_update_bad_pwd_count_ret; >+} >+ >+bool ldb_dn_new_fmt_fail = false; >+struct ldb_dn *ldb_dn_new_fmt(TALLOC_CTX *mem_ctx, >+ struct ldb_context *ldb, >+ const char *new_fmt, ...) >+{ >+ char *strdn; >+ const char *guid = "975ac5fa-35d9-431d-b86a-845bcd34fff9"; >+ >+ if (ldb_dn_new_fmt_fail) { >+ return NULL; >+ } >+ strdn = talloc_asprintf(mem_ctx, "GUID=<%s>", guid); >+ >+ if (strdn) { >+ struct ldb_dn *dn = ldb_dn_new(mem_ctx, ldb, strdn); >+ talloc_free(strdn); >+ return dn; >+ } >+ >+ return NULL; >+} >+ >+int ldb_build_mod_req_ret = LDB_SUCCESS; >+struct ldb_request *ldb_build_mod_req_res = NULL; >+int ldb_build_mod_req(struct ldb_request **ret_req, >+ struct ldb_context *ldb, >+ TALLOC_CTX *mem_ctx, >+ const struct ldb_message *message, >+ struct ldb_control **controls, >+ void *context, >+ ldb_request_callback_t callback, >+ struct ldb_request *parent) >+{ >+ *ret_req = talloc_steal(mem_ctx, ldb_build_mod_req_res); >+ return ldb_build_mod_req_ret; >+} >+ >+int ldb_request_add_control_ret = LDB_SUCCESS; >+int ldb_request_add_control(struct ldb_request *req, >+ const char *oid, >+ bool critical, >+ void *data) >+{ >+ return ldb_request_add_control_ret; >+} >+ >+int ldb_request_ret = LDB_SUCCESS; >+int ldb_request(struct ldb_context *ldb, >+ struct ldb_request *req) >+{ >+ return ldb_request_ret; >+} >+ >+int ldb_wait_ret = LDB_SUCCESS; >+int ldb_wait(struct ldb_handle *handle, >+ enum ldb_wait_type type) >+{ >+ return ldb_wait_ret; >+} >+bool ldb_msg_new_fail = false; >+struct ldb_message *ldb_msg_new(TALLOC_CTX *mem_ctx) >+{ >+ if (ldb_msg_new_fail) { >+ return NULL; >+ } else { >+ return talloc_zero(mem_ctx, struct ldb_message); >+ } >+} >+ >+int samdb_rodc_ret = LDB_SUCCESS; >+bool samdb_rodc_res = false; >+ >+int samdb_rodc( >+ struct ldb_context *sam_ctx, >+ bool *am_rodc) >+{ >+ >+ *am_rodc = samdb_rodc_res; >+ return samdb_rodc_ret; >+} >+ >+/* >+ * Set the globals used by the mocked functions to a known and consistent state >+ * >+ * return codes set to success >+ * pointers set to NULL >+ * >+ */ >+static void init_mock_results(void) >+{ >+ dsdb_search_dn_ret = LDB_SUCCESS; >+ dsdb_search_dn_res = NULL; >+ >+ ldb_transaction_start_ret = LDB_SUCCESS; >+ in_transaction = false; >+ >+ ldb_transaction_cancel_ret = LDB_SUCCESS; >+ transaction_cancelled = false; >+ >+ ldb_transaction_commit_ret = LDB_SUCCESS; >+ transaction_committed = false; >+ >+ dsdb_update_bad_pwd_count_ret = NT_STATUS_OK; >+ dsdb_update_bad_pwd_count_res = NULL; >+ >+ ldb_dn_new_fmt_fail = false; >+ >+ ldb_build_mod_req_ret = LDB_SUCCESS; >+ ldb_build_mod_req_res = NULL; >+ >+ ldb_request_add_control_ret = LDB_SUCCESS; >+ ldb_request_ret = LDB_SUCCESS; >+ ldb_wait_ret = LDB_SUCCESS; >+ >+ dsdb_search_by_dn_guid_ret = LDB_SUCCESS; >+ dsdb_search_by_dn_guid_res = NULL; >+ >+ ldb_msg_new_fail = false; >+ >+ samdb_rodc_ret = LDB_SUCCESS; >+ samdb_rodc_res = false; >+ >+} >+ >+/***************************************************************************** >+ * Unit test set up and tear down >+ *****************************************************************************/ >+static int setup(void **state) { >+ init_mock_results(); >+ return 0; >+} >+ >+static int teardown(void **state) { >+ init_mock_results(); >+ return 0; >+} >+ >+/****************************************************************************** >+ * Helper functions >+ ******************************************************************************/ >+ >+/* >+ * Build the "Original" user details record, i.e. the user being >+ * authenticated >+ */ >+static struct ldb_message* create_message(TALLOC_CTX *ctx) >+{ >+ >+ int ret; >+ struct timeval tv_now = timeval_current(); >+ NTTIME now = timeval_to_nttime(&tv_now); >+ >+ struct ldb_message *msg = ldb_msg_new(ctx); >+ >+ assert_non_null(msg); >+ ret = samdb_msg_add_int(ctx, msg, msg, "badPwdCount", 10); >+ assert_int_equal(LDB_SUCCESS, ret); >+ ret = samdb_msg_add_int64(ctx, msg, msg, "badPasswordTime", now); >+ assert_int_equal(LDB_SUCCESS, ret); >+ ret = samdb_msg_add_int64(ctx, msg, msg, "lockoutTime", now); >+ assert_int_equal(LDB_SUCCESS, ret); >+ return msg; >+} >+ >+/* >+ * Add the objectGUID element to a message >+ */ >+static void add_object_guid(struct ldb_message *msg, >+ struct GUID *guid) >+{ >+ >+ NTSTATUS status; >+ int ret; >+ DATA_BLOB guid_blob; >+ struct ldb_val *new_val; >+ >+ status = GUID_to_ndr_blob(guid, msg, &guid_blob); >+ assert_true(NT_STATUS_IS_OK(status)); >+ new_val = talloc(msg, struct ldb_val); >+ assert_non_null(new_val); >+ new_val->data = talloc_steal(new_val, guid_blob.data); >+ new_val->length = guid_blob.length; >+ ret = ldb_msg_add_value(msg, "objectGUID", new_val, NULL); >+ assert_int_equal(LDB_SUCCESS, ret); >+} >+ >+/* >+ * Build an ldb_result, for the re-reading of a user record >+ * >+ * if account_control < 0 then the msDS-User-Account-Control-Computed >+ * element is not included >+ * otherwise it is set to the value passed in account_control. >+ * >+ */ >+static struct ldb_result *build_reread_result( >+ struct ldb_context *ldb, >+ TALLOC_CTX *ctx, >+ int account_control) >+{ >+ struct ldb_message *msg = NULL; >+ int ret; >+ >+ struct ldb_result *res = talloc_zero(ctx, struct ldb_result); >+ >+ assert_non_null(res); >+ res->count = 1; >+ res->msgs = talloc_array(res, struct ldb_message *, 1); >+ >+ msg = create_message(res); >+ if (account_control >= 0) { >+ ret = samdb_msg_add_int( >+ ldb, >+ msg, >+ msg, >+ "msDS-User-Account-Control-Computed", >+ account_control); >+ assert_int_equal(LDB_SUCCESS, ret); >+ } >+ >+ res->msgs[0] = msg; >+ return res; >+} >+ >+/* >+ * Build a mock domain pso ldb_result >+ */ >+static struct ldb_result *build_domain_pso_result( >+ struct ldb_context *ldb, >+ TALLOC_CTX *ctx) >+{ >+ struct ldb_message *msg = NULL; >+ struct ldb_result *res = talloc_zero(ctx, struct ldb_result); >+ >+ assert_non_null(res); >+ res->count = 1; >+ res->msgs = talloc_array(res, struct ldb_message *, 1); >+ assert_non_null(res->msgs); >+ msg = talloc_zero(res, struct ldb_message); >+ assert_non_null(msg); >+ res->msgs[0] = msg; >+ return res; >+} >+ >+/***************************************************************************** >+ * authsam_reread_user_logon_data unit tests >+ *****************************************************************************/ >+/* >+ * Pass an empty user message to authsam_reread_user_logon_data >+ * >+ */ >+static void test_reread_empty_msg(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_message *cur = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = ldb_msg_new(ldb); >+ assert_non_null(msg); >+ >+ before = talloc_total_size(ctx); >+ >+ status = authsam_reread_user_logon_data(ldb, ctx, msg, &cur, &guid); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * Pass a user message to authsam_reread_user_logon_data that does not have >+ * a objectGUID element >+ * >+ */ >+static void test_reread_no_objectGUID(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_message *cur = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ >+ before = talloc_total_size(ctx); >+ >+ status = authsam_reread_user_logon_data(ldb, ctx, msg, &cur, &guid); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * Pass a user message to authsam_reread_user_logon_data that has an invalid >+ * objectGUID element >+ * >+ */ >+static void test_reread_invalid_objectGUID(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_message *cur = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ int ret; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ ret = ldb_msg_add_string(msg, "objectGUID", "wibble"); >+ assert_int_equal(LDB_SUCCESS, ret); >+ >+ before = talloc_total_size(ctx); >+ >+ status = authsam_reread_user_logon_data(ldb, ctx, msg, &cur, &guid); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_reread_user_logon_data unable to re-read the user record. >+ * >+ */ >+static void test_reread_read_failure(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_message *cur = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_ret = LDB_ERR_NO_SUCH_OBJECT; >+ >+ status = authsam_reread_user_logon_data(ldb, ctx, msg, &cur, &guid); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_reread_user_logon_data account control flags missing from >+ * re-read data >+ * >+ */ >+static void test_reread_missing_account_control(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_message *cur = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, -1); >+ dsdb_search_by_dn_guid_ret = LDB_SUCCESS; >+ >+ status = authsam_reread_user_logon_data(ldb, ctx, msg, &cur, &guid); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_reread_user_logon_data account locked >+ * re-read data >+ * >+ */ >+static void test_reread_account_locked(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_message *cur = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, UF_LOCKOUT); >+ >+ status = authsam_reread_user_logon_data(ldb, ctx, msg, &cur, &guid); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_reread_user_logon_data account is not locked >+ * re-read data >+ * >+ */ >+static void test_reread_account_not_locked(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_message *cur = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ /* >+ * Function will succeed so the re-read result is the callers >+ * responsibility >+ */ >+ before = talloc_total_size(ctx); >+ >+ status = authsam_reread_user_logon_data(ldb, ctx, msg, &cur, &guid); >+ assert_true(NT_STATUS_IS_OK(status)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+ >+/***************************************************************************** >+ * authsam_update_bad_pwd_count unit tests >+ *****************************************************************************/ >+ >+/* >+ * authsam_update_bad_pwd_account >+ * >+ * Unable to read the domain_dn record >+ * >+ */ >+static void test_update_bad_domain_dn_search_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = talloc_zero(ctx, struct ldb_message); >+ assert_non_null(msg); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_dn_ret = LDB_ERR_NO_SUCH_OBJECT; >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_DB_CORRUPTION)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_update_bad_pwd_account >+ * >+ * start_transaction failure >+ * >+ */ >+static void test_update_bad_start_txn_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = talloc_zero(ctx, struct ldb_message); >+ assert_non_null(msg); >+ >+ before = talloc_total_size(ctx); >+ >+ ldb_transaction_start_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_update_bad_pwd_account >+ * >+ * User details re-read failed >+ * >+ */ >+static void test_update_bad_reread_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = talloc_zero(ctx, struct ldb_message); >+ assert_non_null(msg); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_ret = LDB_ERR_NO_SUCH_OBJECT; >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_update_bad_pwd_account >+ * >+ * Transaction cancel failure >+ */ >+static void test_update_bad_txn_cancel_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = talloc_zero(ctx, struct ldb_message); >+ assert_non_null(msg); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_ret = LDB_ERR_NO_SUCH_OBJECT; >+ ldb_transaction_cancel_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_false(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_update_bad_pwd_account >+ * >+ * dsdb_update_bad_pwd_count failure >+ * >+ */ >+static void test_update_bad_update_count_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ dsdb_update_bad_pwd_count_ret = NT_STATUS_INTERNAL_ERROR; >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_update_bad_pwd_account >+ * >+ * No need to update the bad password stats >+ * >+ */ >+static void test_update_bad_no_update_required(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_IS_OK(status)); >+ assert_true(transaction_committed); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_update_bad_pwd_account >+ * >+ * Transaction commit failure >+ * >+ */ >+static void test_update_bad_commit_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ ldb_transaction_commit_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_false(transaction_committed); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+/* >+ * authsam_update_bad_pwd_account >+ * >+ * ldb_dn_new_fmt_failed building the GUID when updating >+ * the password stats >+ * >+ */ >+static void test_update_bad_ldb_dn_new_fmt_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ dsdb_update_bad_pwd_count_res = >+ talloc_zero(ctx, struct ldb_message); >+ ldb_dn_new_fmt_fail = true; >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_update_bad_pwd_account >+ * >+ * ldb_build_mod_req failed building the user update details >+ * >+ */ >+static void test_update_bad_build_mod_request_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ dsdb_update_bad_pwd_count_res = >+ talloc_zero(ctx, struct ldb_message); >+ ldb_build_mod_req_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_update_bad_pwd_account >+ * >+ * ldb_request_add_control failed to add DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE >+ * to the user update record. >+ * >+ */ >+static void test_update_bad_add_control_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ dsdb_update_bad_pwd_count_res = >+ talloc_zero(dsdb_search_dn_res, struct ldb_message); >+ ldb_build_mod_req_res = >+ talloc_zero(dsdb_search_dn_res, struct ldb_request); >+ ldb_request_add_control_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_update_bad_pwd_account >+ * >+ * call to ldb_request failed >+ * >+ */ >+static void test_update_bad_ldb_request_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ dsdb_update_bad_pwd_count_res = >+ talloc_zero(dsdb_search_dn_res, struct ldb_message); >+ ldb_build_mod_req_res = >+ talloc_zero(dsdb_search_dn_res, struct ldb_request); >+ ldb_request_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_update_bad_pwd_account >+ * >+ * call to ldb_wait failed >+ * >+ */ >+static void test_update_bad_ldb_wait_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ dsdb_update_bad_pwd_count_res = >+ talloc_zero(dsdb_search_dn_res, struct ldb_message); >+ ldb_build_mod_req_res = >+ talloc_zero(dsdb_search_dn_res, struct ldb_request); >+ ldb_wait_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/***************************************************************************** >+ * authsam_logon_success_accounting unit tests >+ *****************************************************************************/ >+/* >+ * authsam_logon_success_accounting >+ * >+ * start_transaction failure >+ * >+ */ >+static void test_success_accounting_start_txn_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ ldb_transaction_start_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_logon_success_accounting( >+ ldb, msg, domain_dn, true, NULL); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_logon_success_accounting >+ * >+ * User details re-read failed >+ * >+ */ >+static void test_success_accounting_reread_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_ret = LDB_ERR_NO_SUCH_OBJECT; >+ >+ status = authsam_logon_success_accounting( >+ ldb, msg, domain_dn, true, NULL); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_logon_success_accounting >+ * >+ * ldb_msg_new failed >+ * >+ */ >+static void test_success_accounting_ldb_msg_new_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ ldb_msg_new_fail = true; >+ >+ status = authsam_logon_success_accounting( >+ ldb, msg, domain_dn, true, NULL); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_logon_success_accounting >+ * >+ * samdb_rodc failed >+ * >+ */ >+static void test_success_accounting_samdb_rodc_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ samdb_rodc_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_logon_success_accounting( >+ ldb, msg, domain_dn, true, NULL); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_logon_success_accounting >+ * >+ * authsam_update_lastlogon_timestamp failed >+ * >+ */ >+static void test_success_accounting_update_lastlogon_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_ret = LDB_ERR_OPERATIONS_ERROR; >+ /* >+ * Note: authsam_update_lastlogon_timestamp returns NT_STATUS_NO_MEMORY >+ * in this case. >+ */ >+ >+ status = authsam_logon_success_accounting( >+ ldb, msg, domain_dn, true, NULL); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_logon_success_accounting >+ * >+ * ldb_dn_new_fmt failed >+ * >+ */ >+static void test_success_accounting_dn_new_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ ldb_dn_new_fmt_fail = true; >+ >+ status = authsam_logon_success_accounting( >+ ldb, msg, domain_dn, true, NULL); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_logon_success_accounting >+ * >+ * ldb_dn_new_fmt failed >+ * >+ */ >+static void test_success_accounting_build_mod_req_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ ldb_build_mod_req_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_logon_success_accounting( >+ ldb, msg, domain_dn, true, NULL); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_logon_success_accounting >+ * >+ * ldb_request_add_control failed >+ * >+ */ >+static void test_success_accounting_add_control_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ ldb_build_mod_req_res = >+ talloc_zero(ldb, struct ldb_request); >+ ldb_request_add_control_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_logon_success_accounting( >+ ldb, msg, domain_dn, true, NULL); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_logon_success_accounting >+ * >+ * ldb_request failed >+ * >+ */ >+static void test_success_accounting_ldb_request_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ ldb_build_mod_req_res = >+ talloc_zero(ldb, struct ldb_request); >+ ldb_request_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_logon_success_accounting( >+ ldb, msg, domain_dn, true, NULL); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_logon_success_accounting >+ * >+ * ldb_wait failed >+ * >+ */ >+static void test_success_accounting_ldb_wait_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ ldb_build_mod_req_res = >+ talloc_zero(ldb, struct ldb_request); >+ ldb_wait_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_logon_success_accounting( >+ ldb, msg, domain_dn, true, NULL); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_true(transaction_cancelled); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_logon_success_accounting >+ * >+ * ldb_transaction_commit failed >+ * >+ */ >+static void test_success_accounting_commit_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ ldb_build_mod_req_res = >+ talloc_zero(ldb, struct ldb_request); >+ ldb_transaction_commit_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_logon_success_accounting( >+ ldb, msg, domain_dn, true, NULL); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_false(transaction_cancelled); >+ assert_false(transaction_committed); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * authsam_logon_success_accounting >+ * >+ * ldb_wait failed and then ldb_transaction_cancel failed >+ * >+ */ >+static void test_success_accounting_rollback_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *domain_dn = NULL; >+ struct GUID guid; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(NULL); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ >+ before = talloc_total_size(ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ dsdb_search_dn_res = build_domain_pso_result(ldb, ctx); >+ ldb_build_mod_req_res = >+ talloc_zero(ldb, struct ldb_request); >+ ldb_wait_ret = LDB_ERR_OPERATIONS_ERROR; >+ ldb_transaction_cancel_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ status = authsam_logon_success_accounting( >+ ldb, msg, domain_dn, true, NULL); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)); >+ assert_false(transaction_cancelled); >+ assert_false(transaction_committed); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+int main(int argc, const char **argv) >+{ >+ const struct CMUnitTest tests[] = { >+ cmocka_unit_test_setup_teardown( >+ test_reread_empty_msg, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_reread_no_objectGUID, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_reread_invalid_objectGUID, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_reread_read_failure, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_reread_missing_account_control, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_reread_account_locked, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_reread_account_not_locked, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_update_bad_domain_dn_search_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_update_bad_start_txn_failed, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_update_bad_reread_failed, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_update_bad_update_count_failed, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_update_bad_no_update_required, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_update_bad_ldb_dn_new_fmt_failed, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_update_bad_build_mod_request_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_update_bad_add_control_failed, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_update_bad_ldb_request_failed, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_update_bad_ldb_wait_failed, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_update_bad_txn_cancel_failed, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_update_bad_commit_failed, setup, teardown), >+ cmocka_unit_test_setup_teardown( >+ test_success_accounting_start_txn_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_success_accounting_reread_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_success_accounting_ldb_msg_new_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_success_accounting_samdb_rodc_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_success_accounting_update_lastlogon_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_success_accounting_dn_new_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_success_accounting_build_mod_req_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_success_accounting_add_control_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_success_accounting_ldb_request_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_success_accounting_ldb_wait_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_success_accounting_commit_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_success_accounting_rollback_failed, >+ setup, >+ teardown), >+ }; >+ >+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT); >+ return cmocka_run_group_tests(tests, NULL, NULL); >+} >diff --git a/source4/auth/wscript_build b/source4/auth/wscript_build >index daf5c8a422d..599fb2a48a0 100644 >--- a/source4/auth/wscript_build >+++ b/source4/auth/wscript_build >@@ -49,6 +49,13 @@ bld.SAMBA_BINARY('test_kerberos', > install=False > ) > >+bld.SAMBA_BINARY('test_auth_sam', >+ source='tests/sam.c', >+ deps='cmocka samdb samba-security ldb tevent', >+ local_include=False, >+ for_selftest=True >+ ) >+ > pytalloc_util = bld.pyembed_libname('pytalloc-util') > pyparam_util = bld.pyembed_libname('pyparam_util') > pyldb_util = bld.pyembed_libname('pyldb-util') >-- >2.25.1 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 14611
:
16386
|
16390
|
16391
|
16392
|
16393
|
16411
|
16474
|
16483
|
16484
|
16485
|
16486
|
16487
|
16573
|
17410
|
17411
|
17413
|
17436
|
17452
|
17453
|
17456
|
17513
|
17521
|
17733
|
17734