The Samba-Bugzilla – Attachment 16573 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]
WIP patch for master
auth-bug-14611-master.patch (text/plain), 115.23 KB, created by
Andrew Bartlett
on 2021-03-30 09:57:04 UTC
(
hide
)
Description:
WIP patch for master
Filename:
MIME Type:
Creator:
Andrew Bartlett
Created:
2021-03-30 09:57:04 UTC
Size:
115.23 KB
patch
obsolete
>From bd21bc9ce4076fa41991abd6f8bf02a71d88938d Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Tue, 30 Mar 2021 10:51:26 +1300 >Subject: [PATCH 01/11] s4-rpc_server: Use authsam_search_account() to find the > user > >This helps the bad password and audit log handling code as it >allows assumptions to be made about the attributes found in >the variable "msg", such as that DSDB_SEARCH_SHOW_EXTENDED_DN >was used. > >This ensures we can re-search on the DN via the embedded GUID, >which in in turn rename-proof. > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/rpc_server/samr/samr_password.c | 85 +++++++++++-------------- > 1 file changed, 38 insertions(+), 47 deletions(-) > >diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c >index 83b104fbd0e..caa9fc14090 100644 >--- a/source4/rpc_server/samr/samr_password.c >+++ b/source4/rpc_server/samr/samr_password.c >@@ -114,14 +114,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, > struct ldb_context *sam_ctx; > struct ldb_dn *user_dn; > int ret; >- struct ldb_message **res; >- const char * const attrs[] = { "objectSid", "dBCSPwd", >- "userAccountControl", >- "msDS-ResultantPSO", >- "msDS-User-Account-Control-Computed", >- "badPwdCount", "badPasswordTime", >- "samAccountName", >- NULL }; >+ struct ldb_message *msg; > struct samr_Password *lm_pwd; > uint8_t new_lm_hash[16]; > struct samr_Password lm_verifier; >@@ -166,25 +159,27 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, > return NT_STATUS_INVALID_SYSTEM_SERVICE; > } > >- /* we need the users dn and the domain dn (derived from the >- user SID). We also need the current lm password hash in >- order to decrypt the incoming password */ >- ret = gendb_search(sam_ctx, >- mem_ctx, NULL, &res, attrs, >- "(&(sAMAccountName=%s)(objectclass=user))", >- ldb_binary_encode_string(mem_ctx, r->in.account->string)); >- if (ret != 1) { >- status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */ >+ /* >+ * We use authsam_search_account() to be consistant with the >+ * other callers in the bad password and audit log handling >+ * systems. It ensures we get DSDB_SEARCH_SHOW_EXTENDED_DN. >+ */ >+ status = authsam_search_account(mem_ctx, >+ sam_ctx, >+ r->in.account->string, >+ ldb_get_default_basedn(sam_ctx), >+ &msg); >+ if (!NT_STATUS_IS_OK(status)) { > goto failed; > } > >- user_dn = res[0]->dn; >+ user_dn = msg->dn; > >- user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL); >- user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid"); >+ user_samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL); >+ user_objectSid = samdb_result_dom_sid(msg, msg, "objectSid"); > > status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, >- res[0], &lm_pwd, NULL); >+ msg, &lm_pwd, NULL); > if (!NT_STATUS_IS_OK(status)) { > goto failed; > } else if (!lm_pwd) { >@@ -221,7 +216,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, > > if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) { > DEBUG(3,("samr: failed to decode password buffer\n")); >- authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx)); >+ authsam_update_bad_pwd_count(sam_ctx, msg, ldb_get_default_basedn(sam_ctx)); > status = NT_STATUS_WRONG_PASSWORD; > goto failed; > } >@@ -232,7 +227,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, > new_password.length, > (void **)&new_pass, &converted_size)) { > DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n")); >- authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx)); >+ authsam_update_bad_pwd_count(sam_ctx, msg, ldb_get_default_basedn(sam_ctx)); > status = NT_STATUS_WRONG_PASSWORD; > goto failed; > } >@@ -243,7 +238,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, > new_password.length, > (void **)&new_unicode_password.data, &unicode_pw_len)) { > DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n")); >- authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx)); >+ authsam_update_bad_pwd_count(sam_ctx, msg, ldb_get_default_basedn(sam_ctx)); > status = NT_STATUS_WRONG_PASSWORD; > goto failed; > } >@@ -256,7 +251,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, > goto failed; > } > if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) { >- authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx)); >+ authsam_update_bad_pwd_count(sam_ctx, msg, ldb_get_default_basedn(sam_ctx)); > status = NT_STATUS_WRONG_PASSWORD; > goto failed; > } >@@ -323,7 +318,7 @@ failed: > } > /* Only update the badPwdCount if we found the user */ > if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { >- authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx)); >+ authsam_update_bad_pwd_count(sam_ctx, msg, ldb_get_default_basedn(sam_ctx)); > } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) { > /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */ > status = NT_STATUS_WRONG_PASSWORD; >@@ -349,13 +344,7 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, > struct ldb_context *sam_ctx = NULL; > struct ldb_dn *user_dn = NULL; > int ret; >- struct ldb_message **res; >- const char * const attrs[] = { "unicodePwd", "dBCSPwd", >- "userAccountControl", >- "msDS-ResultantPSO", >- "msDS-User-Account-Control-Computed", >- "badPwdCount", "badPasswordTime", >- "objectSid", NULL }; >+ struct ldb_message *msg; > struct samr_Password *nt_pwd, *lm_pwd; > struct samr_DomInfo1 *dominfo = NULL; > struct userPwdChangeFailureInformation *reject = NULL; >@@ -397,24 +386,26 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, > return NT_STATUS_INVALID_SYSTEM_SERVICE; > } > >- /* we need the users dn and the domain dn (derived from the >- user SID). We also need the current lm and nt password hashes >- in order to decrypt the incoming passwords */ >- ret = gendb_search(sam_ctx, >- mem_ctx, NULL, &res, attrs, >- "(&(sAMAccountName=%s)(objectclass=user))", >- ldb_binary_encode_string(mem_ctx, r->in.account->string)); >- if (ret != 1) { >- status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */ >+ /* >+ * We use authsam_search_account() to be consistant with the >+ * other callers in the bad password and audit log handling >+ * systems. It ensures we get DSDB_SEARCH_SHOW_EXTENDED_DN. >+ */ >+ status = authsam_search_account(mem_ctx, >+ sam_ctx, >+ r->in.account->string, >+ ldb_get_default_basedn(sam_ctx), >+ &msg); >+ if (!NT_STATUS_IS_OK(status)) { > goto failed; > } > >- user_dn = res[0]->dn; >- user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL); >- user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid"); >+ user_dn = msg->dn; >+ user_samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL); >+ user_objectSid = samdb_result_dom_sid(mem_ctx, msg, "objectSid"); > > status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, >- res[0], &lm_pwd, &nt_pwd); >+ msg, &lm_pwd, &nt_pwd); > if (!NT_STATUS_IS_OK(status) ) { > goto failed; > } >@@ -560,7 +551,7 @@ failed: > > /* Only update the badPwdCount if we found the user */ > if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { >- authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx)); >+ authsam_update_bad_pwd_count(sam_ctx, msg, ldb_get_default_basedn(sam_ctx)); > } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) { > /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */ > status = NT_STATUS_WRONG_PASSWORD; >-- >2.25.1 > > >From d800461f696b1259f9b0af32d29e7fd2d7631a15 Mon Sep 17 00:00:00 2001 >From: Gary Lockyer <gary@catalyst.net.nz> >Date: Tue, 16 Mar 2021 10:52:58 +1300 >Subject: [PATCH 02/11] auth4: split > samdb_result_msds_LockoutObservationWindow() out > >samdb_result_msds_LockoutObservationWindow() is split out of >samdb_result_effective_badPwdCount() > >Signed-off-by: Gary Lockyer <gary@catalyst.net.nz> >--- > source4/dsdb/common/util.c | 50 ++++++++++++++++++++++++++------------ > 1 file changed, 34 insertions(+), 16 deletions(-) > >diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c >index 769e589e265..26305212cd1 100644 >--- a/source4/dsdb/common/util.c >+++ b/source4/dsdb/common/util.c >@@ -5298,9 +5298,9 @@ int dsdb_create_partial_replica_NC(struct ldb_context *ldb, struct ldb_dn *dn) > * This also requires that the domain_msg have (if present): > * - lockOutObservationWindow > */ >-static int dsdb_effective_badPwdCount(const struct ldb_message *user_msg, >- int64_t lockOutObservationWindow, >- NTTIME now) >+int dsdb_effective_badPwdCount(const struct ldb_message *user_msg, >+ int64_t lockOutObservationWindow, >+ NTTIME now) > { > int64_t badPasswordTime; > badPasswordTime = ldb_msg_find_attr_as_int64(user_msg, "badPasswordTime", 0); >@@ -5347,27 +5347,25 @@ static struct ldb_result *lookup_user_pso(struct ldb_context *sam_ldb, > } > > /* >- * Return the effective badPwdCount >+ * Return the msDS-LockoutObservationWindow for a user message > * > * This requires that the user_msg have (if present): >- * - badPasswordTime >- * - badPwdCount > * - msDS-ResultantPSO > */ >-int samdb_result_effective_badPwdCount(struct ldb_context *sam_ldb, >- TALLOC_CTX *mem_ctx, >- struct ldb_dn *domain_dn, >- const struct ldb_message *user_msg) >+int64_t samdb_result_msds_LockoutObservationWindow( >+ struct ldb_context *sam_ldb, >+ TALLOC_CTX *mem_ctx, >+ struct ldb_dn *domain_dn, >+ const struct ldb_message *user_msg) > { >- struct timeval tv_now = timeval_current(); >- NTTIME now = timeval_to_nttime(&tv_now); > int64_t lockOutObservationWindow; > struct ldb_result *res = NULL; > const char *attrs[] = { "msDS-LockoutObservationWindow", > NULL }; >- >+ if (!domain_dn) { >+ smb_panic("domain dn is NULL"); >+ } > res = lookup_user_pso(sam_ldb, mem_ctx, user_msg, attrs); >- > if (res != NULL) { > lockOutObservationWindow = > ldb_msg_find_attr_as_int64(res->msgs[0], >@@ -5375,14 +5373,34 @@ int samdb_result_effective_badPwdCount(struct ldb_context *sam_ldb, > DEFAULT_OBSERVATION_WINDOW); > talloc_free(res); > } else { >- > /* no PSO was found, lookup the default domain setting */ > lockOutObservationWindow = > samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, > "lockOutObservationWindow", NULL); > } >+ return lockOutObservationWindow; >+} > >- return dsdb_effective_badPwdCount(user_msg, lockOutObservationWindow, now); >+/* >+ * Return the effective badPwdCount >+ * >+ * This requires that the user_msg have (if present): >+ * - badPasswordTime >+ * - badPwdCount >+ * - msDS-ResultantPSO >+ */ >+int samdb_result_effective_badPwdCount(struct ldb_context *sam_ldb, >+ TALLOC_CTX *mem_ctx, >+ struct ldb_dn *domain_dn, >+ const struct ldb_message *user_msg) >+{ >+ struct timeval tv_now = timeval_current(); >+ NTTIME now = timeval_to_nttime(&tv_now); >+ int64_t lockOutObservationWindow = >+ samdb_result_msds_LockoutObservationWindow( >+ sam_ldb, mem_ctx, domain_dn, user_msg); >+ return dsdb_effective_badPwdCount( >+ user_msg, lockOutObservationWindow, now); > } > > /* >-- >2.25.1 > > >From 989b6775238dfb8dbef13b657422898d1157d908 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 03/11] s4 auth: Prepare to make bad password count increment > atomic > >To ensure that the bad password count is incremented atomically, >and that the successful logon accounting data is updated atomically, >without always opening a transaction, we will need to make a note >of all bad and successful passwords in a side-DB outside the >transaction lock. > >This provides the functions needed for that and hooks them in >(future commits will handle errors and use the results). > >Based on patches by Gary Lockyer <gary@catalyst.net.nz> > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14611 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/auth/sam.c | 182 +++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 182 insertions(+) > >diff --git a/source4/auth/sam.c b/source4/auth/sam.c >index 39e48c26b52..ca7b8509f40 100644 >--- a/source4/auth/sam.c >+++ b/source4/auth/sam.c >@@ -31,6 +31,8 @@ > #include "libcli/ldap/ldap_ndr.h" > #include "param/param.h" > #include "librpc/gen_ndr/ndr_winbind_c.h" >+#include "lib/dbwrap/dbwrap.h" >+#include "cluster/cluster.h" > > #undef DBGC_CLASS > #define DBGC_CLASS DBGC_AUTH >@@ -813,6 +815,172 @@ static int authsam_get_user_pso(struct ldb_context *sam_ctx, > return LDB_SUCCESS; > } > >+static struct db_context *authsam_get_bad_password_db( >+ TALLOC_CTX *mem_ctx, >+ struct ldb_context *sam_ctx) >+{ >+ struct loadparm_context *lp_ctx = NULL; >+ const char *db_name = "bad_password"; >+ struct db_context *db_ctx = NULL; >+ >+ lp_ctx = ldb_get_opaque(sam_ctx, "loadparm"); >+ if (lp_ctx == NULL) { >+ DBG_ERR("Unable to get loadparm_context\n"); >+ return NULL; >+ } >+ >+ db_ctx = cluster_db_tmp_open(mem_ctx, lp_ctx, db_name, 0); >+ if (db_ctx == NULL) { >+ DBG_ERR("Unable to open bad password attempts database\n"); >+ return NULL; >+ } >+ return db_ctx; >+} >+ >+static NTSTATUS get_object_sid_as_tdb_data( >+ TALLOC_CTX *mem_ctx, >+ const struct ldb_message *msg, >+ TDB_DATA *key) >+{ >+ struct dom_sid *objectsid; >+ struct dom_sid_buf sid_buf; >+ >+ /* >+ * Convert the objectSID to a human readable form to >+ * make debugging easier >+ */ >+ objectsid = samdb_result_dom_sid(mem_ctx, msg, "objectSID"); >+ if (objectsid == NULL) { >+ DBG_ERR("Unable to extract objectSID\n"); >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ dom_sid_str_buf(objectsid, &sid_buf); >+ key->dptr = (unsigned char*) &sid_buf.buf; >+ key->dsize = strlen(sid_buf.buf); >+ >+ return NT_STATUS_OK; >+} >+ >+/* >+ * Add the users objectSID to the bad password attempt database >+ * to indicate that last authentication failed due to a bad password >+ */ >+static NTSTATUS authsam_set_bad_password_indicator( >+ struct ldb_context *sam_ctx, >+ TALLOC_CTX *mem_ctx, >+ const struct ldb_message *msg) >+{ >+ NTSTATUS status = NT_STATUS_OK; >+ TDB_DATA key = {0}; >+ TDB_DATA value = {0}; >+ struct db_context *db = NULL; >+ >+ TALLOC_CTX *ctx = talloc_new(mem_ctx); >+ if (ctx == NULL) { >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ db = authsam_get_bad_password_db(mem_ctx, sam_ctx); >+ if (db == NULL) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto exit; >+ } >+ >+ status = get_object_sid_as_tdb_data(ctx, msg, &key); >+ if (!NT_STATUS_IS_OK(status)) { >+ goto exit; >+ } >+ >+ status = dbwrap_store(db, key, value, 0); >+ if (!NT_STATUS_IS_OK(status)) { >+ DBG_ERR("Unable to store bad password indicator\n"); >+ } >+exit: >+ talloc_free(ctx); >+ return status; >+} >+ >+/* >+ * see if the users objectSID is in the bad password attempt database >+ */ >+static NTSTATUS authsam_check_bad_password_indicator( >+ struct ldb_context *sam_ctx, >+ TALLOC_CTX *mem_ctx, >+ bool *exists, >+ const struct ldb_message *msg) >+{ >+ NTSTATUS status = NT_STATUS_OK; >+ TDB_DATA key = {0}; >+ struct db_context *db = NULL; >+ >+ TALLOC_CTX *ctx = talloc_new(mem_ctx); >+ if (ctx == NULL) { >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ db = authsam_get_bad_password_db(mem_ctx, sam_ctx); >+ if (db == NULL) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto exit; >+ } >+ >+ status = get_object_sid_as_tdb_data(ctx, msg, &key); >+ if (!NT_STATUS_IS_OK(status)) { >+ goto exit; >+ } >+ >+ *exists = dbwrap_exists(db, key); >+exit: >+ talloc_free(ctx); >+ return status; >+} >+ >+/* >+ * Remove the users objectSID to the bad password attempt database >+ * to indicate that last authentication succeeded. >+ */ >+static NTSTATUS authsam_clear_bad_password_indicator( >+ struct ldb_context *sam_ctx, >+ TALLOC_CTX *mem_ctx, >+ const struct ldb_message *msg) >+{ >+ NTSTATUS status = NT_STATUS_OK; >+ TDB_DATA key = {0}; >+ struct db_context *db = NULL; >+ >+ TALLOC_CTX *ctx = talloc_new(mem_ctx); >+ if (ctx == NULL) { >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ db = authsam_get_bad_password_db(mem_ctx, sam_ctx); >+ if (db == NULL) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto exit; >+ } >+ >+ status = get_object_sid_as_tdb_data(ctx, msg, &key); >+ if (!NT_STATUS_IS_OK(status)) { >+ goto exit; >+ } >+ >+ status = dbwrap_delete(db, key); >+ if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, status)) { >+ /* >+ * Ok there was no bad password indicator this is expected >+ */ >+ status = NT_STATUS_OK; >+ } >+ if (NT_STATUS_IS_ERR(status)) { >+ DBG_ERR("Unable to delete bad password indicator, %s %s\n", >+ nt_errstr(status), >+ get_friendly_nt_error_msg(status)); >+ } >+exit: >+ talloc_free(ctx); >+ return status; >+} >+ > NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, > struct ldb_message *msg, > struct ldb_dn *domain_dn) >@@ -882,6 +1050,10 @@ NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, > > ret = dsdb_autotransaction_request(sam_ctx, req); > talloc_free(req); >+ >+ status = authsam_set_bad_password_indicator( >+ sam_ctx, mem_ctx, msg); >+ /* Failure is ignored for now */ > } > > done: >@@ -1045,12 +1217,19 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > NTTIME now; > NTTIME lastLogonTimestamp; > bool am_rodc = false; >+ bool need_db_reread; > > mem_ctx = talloc_new(msg); > if (mem_ctx == NULL) { > return NT_STATUS_NO_MEMORY; > } > >+ status = authsam_check_bad_password_indicator( >+ sam_ctx, mem_ctx, &need_db_reread, msg); >+ if (!NT_STATUS_IS_OK(status)) { >+ return status; >+ } >+ > lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0); > dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0); > if (interactive_or_kerberos) { >@@ -1185,6 +1364,9 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > talloc_free(req); > } > >+ status = authsam_clear_bad_password_indicator(sam_ctx, mem_ctx, msg); >+ /* Failure is ignored for now */ >+ > done: > if (ret != LDB_SUCCESS) { > DEBUG(0, ("Failed to set badPwdCount and lockoutTime " >-- >2.25.1 > > >From 8ab96fe24ac81522d72b267e11f53322a5214da0 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Tue, 30 Mar 2021 17:57:10 +1300 >Subject: [PATCH 04/11] auth4: Reread the user record if a bad password is > noticed. > >As is, this is pointless, as we need a transaction to make this >any less of a race, but this provides the steps towards that goal. > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/auth/sam.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 82 insertions(+) > >diff --git a/source4/auth/sam.c b/source4/auth/sam.c >index ca7b8509f40..46b825e597d 100644 >--- a/source4/auth/sam.c >+++ b/source4/auth/sam.c >@@ -815,6 +815,68 @@ 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) >+{ >+ const struct ldb_val *v = NULL; >+ struct ldb_result *res = NULL; >+ uint16_t acct_flags = 0; >+ const char *attr_name = "msDS-User-Account-Control-Computed"; >+ >+ int ret; >+ >+ /* >+ * Re-read the account details, using the GUID in case the DN >+ * is being changed (this is automatic in LDB because the >+ * original search also used DSDB_SEARCH_SHOW_EXTENDED_DN) >+ * >+ * 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_dn(sam_ctx, >+ mem_ctx, >+ &res, >+ user_msg->dn, >+ user_attrs, >+ DSDB_SEARCH_SHOW_EXTENDED_DN); >+ 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; >+} >+ > static struct db_context *authsam_get_bad_password_db( > TALLOC_CTX *mem_ctx, > struct ldb_context *sam_ctx) >@@ -1217,6 +1279,7 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > NTTIME now; > NTTIME lastLogonTimestamp; > bool am_rodc = false; >+ struct ldb_message *current = NULL; > bool need_db_reread; > > mem_ctx = talloc_new(msg); >@@ -1230,6 +1293,25 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > return status; > } > >+ if (need_db_reread) { >+ >+ /* >+ * Re-read the account details, using the GUID >+ * embedded in DN so this is safe against a race where >+ * it is being renamed. >+ */ >+ status = authsam_reread_user_logon_data( >+ sam_ctx, mem_ctx, msg, ¤t); >+ if (!NT_STATUS_IS_OK(status)) { >+ /* >+ * The re-read can return account locked out, as well >+ * as an internal error >+ */ >+ return status; >+ } >+ msg = current; >+ } >+ > lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0); > dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0); > if (interactive_or_kerberos) { >-- >2.25.1 > > >From d1511d9ec70a87e059086398c4e9fc81989cb5c3 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Tue, 30 Mar 2021 18:01:39 +1300 >Subject: [PATCH 05/11] s4 auth: make bad password count increment atomic > >Ensure that the bad password count is incremented atomically, >and that the successful logon accounting data is updated atomically. > >Use bad password indicator (in a distinct TDB) to determine if to open a transaction > >We open a transaction when we have seen the hint that this user >has recorded a bad password. This allows us to avoid always >needing one, while not missing a possible lockout. > >We also go back and get a transation if we did not take out >one out but we chose to do a write (eg for lastLogonTimestamp) > >Based on patches by Gary Lockyer <gary@catalyst.net.nz> > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14611 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/auth/sam.c | 300 +++++++++++++++++++++++++++++++++++++-------- > 1 file changed, 247 insertions(+), 53 deletions(-) > >diff --git a/source4/auth/sam.c b/source4/auth/sam.c >index 46b825e597d..58c2393a735 100644 >--- a/source4/auth/sam.c >+++ b/source4/auth/sam.c >@@ -1056,7 +1056,9 @@ NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, > NTSTATUS status; > struct ldb_result *domain_res; > struct ldb_message *msg_mod = NULL; >+ struct ldb_message *current = NULL; > struct ldb_message *pso_msg = NULL; >+ bool txn_active = false; > TALLOC_CTX *mem_ctx; > > mem_ctx = talloc_new(msg); >@@ -1081,14 +1083,65 @@ NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, > 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 <GUID= part of the DN >+ * 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); > 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 >+ */ >+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) { >+ /* >+ * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit >+ * the transaction. Again to avoid cluttering the >+ * audit logs with spurious errors >+ */ >+ goto exit; >+ } >+ 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; > >@@ -1099,7 +1152,9 @@ 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, >@@ -1107,31 +1162,72 @@ NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, > 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; >+ } > status = authsam_set_bad_password_indicator( > sam_ctx, mem_ctx, msg); >- /* Failure is ignored for now */ >+ if (!NT_STATUS_IS_OK(status)) { >+ 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. >+ */ >+exit: >+ 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 status; > >+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, >@@ -1279,6 +1375,7 @@ 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; > bool need_db_reread; > >@@ -1287,14 +1384,40 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > return NT_STATUS_NO_MEMORY; > } > >+ /* >+ * 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. >+ * >+ * Howevver we have long-running transactions like replication >+ * that could otherwise grind the system to a halt so we first >+ * determine if *this* account has seen a bad password, >+ * otherwise we only start a transaction if there was a need >+ * (because a change was to be made). >+ */ >+ > status = authsam_check_bad_password_indicator( > sam_ctx, mem_ctx, &need_db_reread, msg); > if (!NT_STATUS_IS_OK(status)) { > return status; > } > >+get_transaction: >+ > if (need_db_reread) { > >+ /* >+ * 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 > * embedded in DN so this is safe against a race where >@@ -1307,7 +1430,15 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > * The re-read can return account locked out, as well > * as an internal error > */ >- return status; >+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) { >+ /* >+ * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit >+ * the transaction. Again to avoid cluttering the >+ * audit logs with spurious errors >+ */ >+ goto exit; >+ } >+ goto error; > } > msg = current; > } >@@ -1328,9 +1459,15 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > > 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; > } >+ >+ /* >+ * By using the DN from msg->dn directly, we allow LDB to >+ * prefer the embedded GUID form, so this is actually quite >+ * safe even in the case where DN has been changed >+ */ > msg_mod->dn = msg->dn; > > if (lockoutTime != 0) { >@@ -1339,14 +1476,14 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > */ > 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); > if (ret != LDB_SUCCESS) { >- TALLOC_FREE(mem_ctx); >- return NT_STATUS_NO_MEMORY; >+ status = NT_STATUS_NO_MEMORY; >+ goto error; > } > } > >@@ -1358,8 +1495,8 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > 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; > } > } > >@@ -1373,8 +1510,8 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > 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 */ >@@ -1390,16 +1527,16 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > > 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); > 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 */ >@@ -1419,6 +1556,16 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > unsigned int i; > struct ldb_request *req; > >+ /* >+ * If it turns out we are going to update the DB, go >+ * back to the start, get a transaction and the >+ * current DB state and try again >+ */ >+ if (txn_active == false) { >+ need_db_reread = true; >+ goto get_transaction; >+ } >+ > /* mark all the message elements as LDB_FLAG_MOD_REPLACE */ > for (i=0;i<msg_mod->num_elements;i++) { > msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE; >@@ -1431,35 +1578,82 @@ 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); > } >- > status = authsam_clear_bad_password_indicator(sam_ctx, mem_ctx, msg); >- /* Failure is ignored for now */ >+ if (!NT_STATUS_IS_OK(status)) { >+ goto error; >+ } >+ >+ /* >+ * 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. >+ * >+ * Thankfully both TDB and LMDB seem to optimise for the empty >+ * transaction case >+ */ >+exit: >+ TALLOC_FREE(mem_ctx); >+ >+ if (txn_active == false) { >+ return NT_STATUS_OK; >+ } > >-done: >+ 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 4106b3baa9e151d65546b86baa6e2705c2ad49fa Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Tue, 30 Mar 2021 16:35:44 +1300 >Subject: [PATCH 06/11] auth4: Add missing newline to debug message on PSO read > failure > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/auth/sam.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > >diff --git a/source4/auth/sam.c b/source4/auth/sam.c >index 58c2393a735..5cbbf2f27c1 100644 >--- a/source4/auth/sam.c >+++ b/source4/auth/sam.c >@@ -1079,7 +1079,7 @@ 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)); > } > >-- >2.25.1 > > >From 8ce0eea4f81e3f636515253f0d128d1d12966bef Mon Sep 17 00:00:00 2001 >From: Gary Lockyer <gary@catalyst.net.nz> >Date: Thu, 25 Mar 2021 11:30:59 +1300 >Subject: [PATCH 07/11] auth4: Return only the result message and free the > surrounding result > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/auth/sam.c | 3 ++- > 1 file changed, 2 insertions(+), 1 deletion(-) > >diff --git a/source4/auth/sam.c b/source4/auth/sam.c >index 5cbbf2f27c1..26e81b7aa6a 100644 >--- a/source4/auth/sam.c >+++ b/source4/auth/sam.c >@@ -873,7 +873,8 @@ static NTSTATUS authsam_reread_user_logon_data( > TALLOC_FREE(res); > return NT_STATUS_ACCOUNT_LOCKED_OUT; > } >- *current = res->msgs[0]; >+ *current = talloc_steal(mem_ctx, res->msgs[0]); >+ TALLOC_FREE(res); > return NT_STATUS_OK; > } > >-- >2.25.1 > > >From e5ad1dda5ec0d48bfdb54773c2791490155e8cff Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 25 Mar 2021 14:42:39 +1300 >Subject: [PATCH 08/11] auth4: Split > authsam_calculate_lastlogon_sync_interval() out > >authsam_calculate_lastlogon_sync_interval() is split out of authsam_update_lastlogon_timestamp() > >Based on work by Gary Lockyer <gary@catalyst.net.nz> > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/auth/sam.c | 118 ++++++++++++++++++++++++++++----------------- > 1 file changed, 75 insertions(+), 43 deletions(-) > >diff --git a/source4/auth/sam.c b/source4/auth/sam.c >index 26e81b7aa6a..fb43e0325e3 100644 >--- a/source4/auth/sam.c >+++ b/source4/auth/sam.c >@@ -1230,40 +1230,35 @@ error: > > } > >-static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx, >- struct ldb_message *msg_mod, >- struct ldb_dn *domain_dn, >- NTTIME old_timestamp, >- NTTIME now) >+/* >+ * msDS-LogonTimeSyncInterval is an int32_t number of days. >+ * >+ * The docs say: "the initial update, after the domain functional >+ * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as >+ * 14 days minus a random percentage of 5 days", but we aren't doing >+ * that. The blogosphere seems to think that this randomised update >+ * happens everytime, but [MS-ADA1] doesn't agree. >+ * >+ * Dochelp referred us to the following blog post: >+ * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx >+ * >+ * when msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is >+ * not changed. >+ */ >+ >+static NTSTATUS authsam_calculate_lastlogon_sync_interval( >+ struct ldb_context *sam_ctx, >+ TALLOC_CTX *ctx, >+ struct ldb_dn *domain_dn, >+ NTTIME *sync_interval_nt) > { >- /* >- * We only set lastLogonTimestamp if the current value is older than >- * now - msDS-LogonTimeSyncInterval days. >- * >- * msDS-LogonTimeSyncInterval is an int32_t number of days, while >- * lastLogonTimestamp is in the 64 bit 100ns NTTIME format. >- * >- * The docs say: "the initial update, after the domain functional >- * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as >- * 14 days minus a random percentage of 5 days", but we aren't doing >- * that. The blogosphere seems to think that this randomised update >- * happens everytime, but [MS-ADA1] doesn't agree. >- * >- * Dochelp referred us to the following blog post: >- * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx >- * >- * en msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is >- * not changed. >- */ > static const char *attrs[] = { "msDS-LogonTimeSyncInterval", > NULL }; > int ret; > struct ldb_result *domain_res = NULL; >- TALLOC_CTX *mem_ctx = NULL; >- int32_t sync_interval; >- NTTIME sync_interval_nt; >+ uint32_t sync_interval; > >- mem_ctx = talloc_new(msg_mod); >+ TALLOC_CTX *mem_ctx = talloc_new(ctx); > if (mem_ctx == NULL) { > return NT_STATUS_NO_MEMORY; > } >@@ -1279,15 +1274,7 @@ static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx, > "msDS-LogonTimeSyncInterval", > 14); > DEBUG(5, ("sync interval is %d\n", sync_interval)); >- if (sync_interval == 0){ >- /* >- * Setting msDS-LogonTimeSyncInterval to zero is how you ask >- * that nothing happens here. >- */ >- TALLOC_FREE(mem_ctx); >- return NT_STATUS_OK; >- } >- else if (sync_interval >= 5){ >+ if (sync_interval >= 5){ > /* > * Subtract "a random percentage of 5" days. Presumably this > * percentage is between 0 and 100, and modulus is accurate >@@ -1295,17 +1282,49 @@ static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx, > */ > uint32_t r = generate_random() % 6; > sync_interval -= r; >- DEBUG(5, ("randomised sync interval is %d (-%d)\n", sync_interval, r)); >+ DBG_INFO("randomised sync interval is %d (-%d)\n", >+ sync_interval, >+ r); > } > /* In the case where sync_interval < 5 there is no randomisation */ > >- sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL; >+ /* >+ * msDS-LogonTimeSyncInterval is an int32_t number of days, >+ * while lastLogonTimestamp (to be updated) is in the 64 bit >+ * 100ns NTTIME format so we must convert. >+ */ >+ *sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL; >+ TALLOC_FREE(mem_ctx); >+ return NT_STATUS_OK; >+} >+ > >+/* >+ * We only set lastLogonTimestamp if the current value is older than >+ * now - msDS-LogonTimeSyncInterval days. >+ * >+ * lastLogonTimestamp is in the 64 bit 100ns NTTIME format >+ */ >+static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx, >+ struct ldb_message *msg_mod, >+ struct ldb_dn *domain_dn, >+ NTTIME old_timestamp, >+ NTTIME now, >+ NTTIME sync_interval_nt) >+{ >+ int ret; > DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n", > (long long int)old_timestamp, > (long long int)(now - sync_interval_nt), > (long long int)(old_timestamp - now + sync_interval_nt))); > >+ if (sync_interval_nt == 0){ >+ /* >+ * Setting msDS-LogonTimeSyncInterval to zero is how you ask >+ * that nothing happens here. >+ */ >+ return NT_STATUS_OK; >+ } > if (old_timestamp > now){ > DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n", > (long long int)old_timestamp, (long long int)now)); >@@ -1320,11 +1339,9 @@ static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx, > "lastLogonTimestamp", now); > > if (ret != LDB_SUCCESS) { >- TALLOC_FREE(mem_ctx); > return NT_STATUS_NO_MEMORY; > } > } >- TALLOC_FREE(mem_ctx); > return NT_STATUS_OK; > } > >@@ -1533,8 +1550,23 @@ get_transaction: > } > > if (!am_rodc) { >- status = authsam_update_lastlogon_timestamp(sam_ctx, msg_mod, domain_dn, >- lastLogonTimestamp, now); >+ NTTIME sync_interval_nt; >+ >+ status = authsam_calculate_lastlogon_sync_interval( >+ sam_ctx, mem_ctx, domain_dn, &sync_interval_nt); >+ >+ if (!NT_STATUS_IS_OK(status)) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; >+ } >+ >+ status = authsam_update_lastlogon_timestamp( >+ sam_ctx, >+ msg_mod, >+ domain_dn, >+ lastLogonTimestamp, >+ now, >+ sync_interval_nt); > if (!NT_STATUS_IS_OK(status)) { > status = NT_STATUS_NO_MEMORY; > goto error; >-- >2.25.1 > > >From d5722dd319208edd27a8668bfbebaeff58c83d07 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 25 Mar 2021 15:33:08 +1300 >Subject: [PATCH 09/11] auth4: Inline samdb_result_effective_badPwdCount() in > authsam_logon_success_accounting() > >By bringing this fucntion inline it can then be spit out in a >subsequent commit. > >Based on work by Gary Lockyer <gary@catalyst.net.nz> > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/auth/sam.c | 13 ++++++++----- > 1 file changed, 8 insertions(+), 5 deletions(-) > >diff --git a/source4/auth/sam.c b/source4/auth/sam.c >index fb43e0325e3..341331a2905 100644 >--- a/source4/auth/sam.c >+++ b/source4/auth/sam.c >@@ -1463,11 +1463,17 @@ get_transaction: > > lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0); > dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0); >+ tv_now = timeval_current(); >+ now = timeval_to_nttime(&tv_now); >+ > if (interactive_or_kerberos) { > badPwdCount = dbBadPwdCount; > } else { >- badPwdCount = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx, >- domain_dn, msg); >+ int64_t lockOutObservationWindow = >+ samdb_result_msds_LockoutObservationWindow( >+ sam_ctx, mem_ctx, domain_dn, msg); >+ badPwdCount = dsdb_effective_badPwdCount( >+ msg, lockOutObservationWindow, now); > } > lastLogonTimestamp = > ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0); >@@ -1505,9 +1511,6 @@ get_transaction: > } > } > >- tv_now = timeval_current(); >- now = timeval_to_nttime(&tv_now); >- > if (interactive_or_kerberos || > (badPwdCount != 0 && lockoutTime == 0)) { > ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod, >-- >2.25.1 > > >From c91aaad5cb6653df4565553e473f0532bfd4bc96 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Tue, 30 Mar 2021 16:48:31 +1300 >Subject: [PATCH 10/11] auth4: Avoid reading the database twice by > precaculating some variables > >These variables are not important to protect against a race with >and a double-read can easily be avoided by moving them up the file >a little. > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/auth/sam.c | 55 ++++++++++++++++++++++++++++++---------------- > 1 file changed, 36 insertions(+), 19 deletions(-) > >diff --git a/source4/auth/sam.c b/source4/auth/sam.c >index 341331a2905..bbfc4678d61 100644 >--- a/source4/auth/sam.c >+++ b/source4/auth/sam.c >@@ -1392,6 +1392,8 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > struct timeval tv_now; > NTTIME now; > NTTIME lastLogonTimestamp; >+ int64_t lockOutObservationWindow; >+ NTTIME sync_interval_nt = 0; > bool am_rodc = false; > bool txn_active = false; > struct ldb_message *current = NULL; >@@ -1421,6 +1423,36 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, > return status; > } > >+ if (interactive_or_kerberos == false) { >+ /* >+ * Avoid calculating this twice, it reads the PSO. A >+ * race on this is unimportant. >+ */ >+ lockOutObservationWindow >+ = samdb_result_msds_LockoutObservationWindow( >+ sam_ctx, mem_ctx, domain_dn, msg); >+ } >+ >+ ret = samdb_rodc(sam_ctx, &am_rodc); >+ if (ret != LDB_SUCCESS) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; >+ } >+ >+ if (!am_rodc) { >+ /* >+ * Avoid reading the main domain DN twice. A race on >+ * this is unimportant. >+ */ >+ status = authsam_calculate_lastlogon_sync_interval( >+ sam_ctx, mem_ctx, domain_dn, &sync_interval_nt); >+ >+ if (!NT_STATUS_IS_OK(status)) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto error; >+ } >+ } >+ > get_transaction: > > if (need_db_reread) { >@@ -1469,9 +1501,10 @@ get_transaction: > if (interactive_or_kerberos) { > badPwdCount = dbBadPwdCount; > } else { >- int64_t lockOutObservationWindow = >- samdb_result_msds_LockoutObservationWindow( >- sam_ctx, mem_ctx, domain_dn, msg); >+ /* >+ * We get lockOutObservationWindow above, before the >+ * transaction >+ */ > badPwdCount = dsdb_effective_badPwdCount( > msg, lockOutObservationWindow, now); > } >@@ -1546,23 +1579,7 @@ get_transaction: > } > } > >- ret = samdb_rodc(sam_ctx, &am_rodc); >- if (ret != LDB_SUCCESS) { >- status = NT_STATUS_INTERNAL_ERROR; >- goto error; >- } >- > if (!am_rodc) { >- NTTIME sync_interval_nt; >- >- status = authsam_calculate_lastlogon_sync_interval( >- sam_ctx, mem_ctx, domain_dn, &sync_interval_nt); >- >- if (!NT_STATUS_IS_OK(status)) { >- status = NT_STATUS_INTERNAL_ERROR; >- goto error; >- } >- > status = authsam_update_lastlogon_timestamp( > sam_ctx, > msg_mod, >-- >2.25.1 > > >From baf21c328b1b0c305bfc1a8ef376fff1f9d7799e 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 11/11] 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 > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14611 > >Signed-off-by: Gary Lockyer <gary@catalyst.net.nz> >--- > selftest/tests.py | 2 + > source4/auth/tests/sam.c | 2688 ++++++++++++++++++++++++++++++++++++ > source4/auth/wscript_build | 10 + > 3 files changed, 2700 insertions(+) > create mode 100644 source4/auth/tests/sam.c > >diff --git a/selftest/tests.py b/selftest/tests.py >index 6bf46ae5621..41b7d7905db 100644 >--- a/selftest/tests.py >+++ b/selftest/tests.py >@@ -419,6 +419,8 @@ 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")]) > if with_elasticsearch_backend: > plantestsuite("samba.unittests.mdsparser_es", "none", > [os.path.join(bindir(), "default/source3/test_mdsparser_es")] + [configuration]) >diff --git a/source4/auth/tests/sam.c b/source4/auth/tests/sam.c >new file mode 100644 >index 00000000000..69835cbce75 >--- /dev/null >+++ b/source4/auth/tests/sam.c >@@ -0,0 +1,2688 @@ >+/* >+ * 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" >+#include "librpc/gen_ndr/ndr_security.h" >+ >+/***************************************************************************** >+ * wrapped functions >+ * >+ *****************************************************************************/ >+int __wrap_samdb_msg_add_int64( >+ struct ldb_context *sam_ldb, >+ TALLOC_CTX *mem_ctx, >+ struct ldb_message *msg, >+ const char *attr_name, >+ int64_t v); >+int __real_samdb_msg_add_int64( >+ struct ldb_context *sam_ldb, >+ TALLOC_CTX *mem_ctx, >+ struct ldb_message *msg, >+ const char *attr_name, >+ int64_t v); >+int __wrap_samdb_msg_add_int64( >+ struct ldb_context *sam_ldb, >+ TALLOC_CTX *mem_ctx, >+ struct ldb_message *msg, >+ const char *attr_name, >+ int64_t v) >+{ >+ >+ int ret; >+ ret = (int)mock(); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ return __real_samdb_msg_add_int64(sam_ldb, mem_ctx, msg, attr_name, v); >+} >+/***************************************************************************** >+ * 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 = NULL; >+struct ldb_result **dsdb_search_dn_res = NULL; >+unsigned dsdb_search_dn_ctr = 0; >+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) >+{ >+ int ret = 0; >+ ret = dsdb_search_dn_ret[dsdb_search_dn_ctr]; >+ if (ret == LDB_SUCCESS) { >+ *_result = talloc_steal( >+ mem_ctx, dsdb_search_dn_res[dsdb_search_dn_ctr]); >+ } >+ dsdb_search_dn_ctr++; >+ return 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; >+} >+ >+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; >+} >+ >+struct loadparm_context *ldb_get_opaque_ret = NULL; >+void *ldb_get_opaque(struct ldb_context *ldb, const char *name) >+{ >+ return ldb_get_opaque_ret; >+} >+ >+struct db_context {}; >+struct db_context *cluster_db_tmp_open_ret = NULL; >+struct db_context *cluster_db_tmp_open( >+ TALLOC_CTX *mem_ctx, >+ struct loadparm_context *lp_ctx, >+ const char *dbbase, >+ int flags) >+{ >+ return cluster_db_tmp_open_ret; >+} >+ >+NTSTATUS dbwrap_store_ret = NT_STATUS_OK; >+NTSTATUS dbwrap_store(struct db_context *db, TDB_DATA key, >+ TDB_DATA data, int flags) >+{ >+ return dbwrap_store_ret; >+} >+bool dbwrap_exists_ret = true; >+ >+bool dbwrap_exists(struct db_context *db, TDB_DATA key) >+{ >+ return dbwrap_exists_ret; >+} >+ >+NTSTATUS dbwrap_delete_ret = NT_STATUS_OK; >+NTSTATUS dbwrap_delete(struct db_context *db, TDB_DATA key) >+{ >+ return dbwrap_delete_ret; >+} >+ >+/* >+ * Set the globals used by the mocked functions to a known and consistent state >+ * >+ */ >+static void init_mock_results(TALLOC_CTX *mem_ctx) >+{ >+ dsdb_search_dn_ret = NULL; >+ dsdb_search_dn_res = NULL; >+ dsdb_search_dn_ctr = 0; >+ >+ 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_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; >+ >+ ldb_get_opaque_ret = loadparm_init(mem_ctx); >+ >+ cluster_db_tmp_open_ret = talloc_zero(mem_ctx, struct db_context); >+ >+ dbwrap_store_ret = NT_STATUS_OK; >+ >+ dbwrap_exists_ret = true; >+ >+ dbwrap_delete_ret = NT_STATUS_OK; >+ >+} >+ >+/***************************************************************************** >+ * Unit test set up and tear down >+ *****************************************************************************/ >+struct context { >+}; >+ >+static int setup(void **state) { >+ struct context *ctx = talloc_zero(NULL, struct context); >+ init_mock_results(ctx); >+ >+ *state = ctx; >+ return 0; >+} >+ >+static int teardown(void **state) { >+ struct context *ctx = *state; >+ TALLOC_FREE(ctx); >+ 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 = __real_samdb_msg_add_int64(ctx, msg, msg, "badPasswordTime", now); >+ assert_int_equal(LDB_SUCCESS, ret); >+ ret = __real_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); >+} >+ >+/* >+ * Add an objectSID in string form to the supplied message >+ * >+ * >+ */ >+static void add_sid( >+ struct ldb_message *msg, >+ const char *sid_str) >+{ >+ struct ldb_val v; >+ enum ndr_err_code ndr_err; >+ struct dom_sid *sid = NULL; >+ >+ sid = talloc_zero(msg, struct dom_sid); >+ assert_non_null(sid); >+ assert_true(string_to_sid(sid, sid_str)); >+ ndr_err = ndr_push_struct_blob( >+ &v, msg, sid, (ndr_push_flags_fn_t)ndr_push_dom_sid); >+ assert_true(NDR_ERR_CODE_IS_SUCCESS(ndr_err)); >+ assert_int_equal(0, ldb_msg_add_value(msg, "objectSID", &v, NULL)); >+} >+ >+/* >+ * 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; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(*state); >+ 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); >+ 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; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(*state); >+ 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); >+ 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; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ int ret; >+ >+ ctx = talloc_new(*state); >+ 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); >+ 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; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ 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); >+ 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; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ 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); >+ 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; >+ TALLOC_CTX *ctx = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ 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); >+ 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; >+ size_t result_size = 0; >+ NTSTATUS status; >+ >+ ctx = talloc_new(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ /* >+ * authsam_reread_user_logon_data returns the ldb_message portion >+ * of the ldb_result created by build_reread_result. >+ * So the tests for memory leaks will need to adjust for that >+ */ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ result_size = talloc_total_size(dsdb_search_by_dn_guid_res) - >+ talloc_total_size(dsdb_search_by_dn_guid_res->msgs[0]); >+ before = talloc_total_size(ctx); >+ before -= result_size; >+ >+ status = authsam_reread_user_logon_data(ldb, ctx, msg, &cur); >+ 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(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = talloc_zero(ctx, struct ldb_message); >+ assert_non_null(msg); >+ >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_ERR_NO_SUCH_OBJECT; >+ >+ before = talloc_total_size(ctx); >+ >+ 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 >+ * >+ * authsam_get_user_pso failure >+ * >+ */ >+static void test_update_bad_get_pso_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(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = talloc_zero(ctx, struct ldb_message); >+ assert_non_null(msg); >+ ldb_msg_add_string(msg, "msDS-ResultantPSO", ""); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 2); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 2); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ dsdb_search_dn_ret[1] = LDB_ERR_NO_SUCH_OBJECT; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ fprintf(stderr, "==================== %s\n", nt_errstr(status)); >+ 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_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(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = talloc_zero(ctx, struct ldb_message); >+ assert_non_null(msg); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, 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(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = talloc_zero(ctx, struct ldb_message); >+ assert_non_null(msg); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, 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 >+ * >+ * User details re-read reported locked out. >+ * >+ */ >+static void test_update_bad_reread_locked_out(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; >+ size_t result_size = 0; >+ NTSTATUS status; >+ struct GUID guid; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ assert_non_null(msg); >+ guid = GUID_random(); >+ add_object_guid(msg, &guid); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, UF_LOCKOUT); >+ before -= result_size; >+ >+ status = authsam_update_bad_pwd_count(ldb, msg, domain_dn); >+ assert_true(NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)); >+ assert_false(transaction_cancelled); >+ 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 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(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = talloc_zero(ctx, struct ldb_message); >+ assert_non_null(msg); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ 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_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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ samdb_rodc_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ >+ 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ >+ ldb_build_mod_req_res = >+ talloc_zero(dsdb_search_dn_res, struct ldb_request); >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ will_return(__wrap_samdb_msg_add_int64, LDB_ERR_OPERATIONS_ERROR); >+ >+ 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_build_mod_req 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ ldb_build_mod_req_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ >+ 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ ldb_build_mod_req_res = >+ talloc_zero(ldb, struct ldb_request); >+ ldb_request_add_control_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ >+ 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ ldb_build_mod_req_res = >+ talloc_zero(ldb, struct ldb_request); >+ ldb_request_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ >+ 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ ldb_build_mod_req_res = >+ talloc_zero(ldb, struct ldb_request); >+ ldb_wait_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ >+ 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ ldb_build_mod_req_res = >+ talloc_zero(ldb, struct ldb_request); >+ ldb_transaction_commit_ret = LDB_ERR_OPERATIONS_ERROR; >+ >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ >+ 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(*state); >+ 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); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1000"); >+ >+ dsdb_search_dn_res = talloc_array(ctx, struct ldb_result *, 1); >+ dsdb_search_dn_ret = talloc_array(ctx, int, 1); >+ dsdb_search_dn_ret[0] = LDB_SUCCESS; >+ >+ before = talloc_total_size(ctx); >+ dsdb_search_dn_res[0] = build_domain_pso_result(ldb, ctx); >+ >+ dsdb_search_by_dn_guid_res = build_reread_result(ldb, ctx, 0); >+ >+ 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; >+ >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ will_return(__wrap_samdb_msg_add_int64, LDB_SUCCESS); >+ >+ 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); >+} >+ >+/* >+ * get_bad_password_db >+ * >+ * ldb_get_opaque failure. >+ */ >+static void test_get_bad_password_get_opaque_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ TALLOC_CTX *ctx = NULL; >+ struct db_context *db = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ /* >+ * clear the mock ldb_get_opaque return value, so that we get a null >+ * response. >+ */ >+ TALLOC_FREE(ldb_get_opaque_ret); >+ >+ before = talloc_total_size(ctx); >+ >+ db = authsam_get_bad_password_db(ctx, ldb); >+ assert_null(db); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * get_bad_password_db >+ * >+ * cluster_db_tmp_open failure. >+ */ >+static void test_get_bad_password_db_open_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ TALLOC_CTX *ctx = NULL; >+ struct db_context *db = NULL; >+ size_t before = 0; >+ size_t after = 0; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ /* >+ * Clear the mock cluster_db_tmp_open return value so that >+ * it returns NULL >+ */ >+ TALLOC_FREE(cluster_db_tmp_open_ret); >+ before = talloc_total_size(ctx); >+ >+ db = authsam_get_bad_password_db(ctx, ldb); >+ assert_null(db); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * set_bad_password_indicator >+ * >+ * set_bad_password_indicator failure. >+ */ >+static void test_set_bad_password_indicator_get_db_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ TALLOC_CTX *ctx = NULL; >+ NTSTATUS status; >+ size_t before = 0; >+ size_t after = 0; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ /* >+ * Clear the mock cluster_db_tmp_open return value so that >+ * it returns NULL >+ */ >+ TALLOC_FREE(cluster_db_tmp_open_ret); >+ before = talloc_total_size(ctx); >+ >+ status = authsam_set_bad_password_indicator(ldb, ctx, NULL); >+ assert_true(NT_STATUS_EQUAL(NT_STATUS_INTERNAL_ERROR, status)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * set_bad_password_indicator >+ * >+ * get_object_sid_as_tdb_data failure. >+ */ >+static void test_set_bad_password_indicator_get_object_sid_failed( >+ void **state) >+{ >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ TALLOC_CTX *ctx = NULL; >+ NTSTATUS status; >+ size_t before = 0; >+ size_t after = 0; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ /* >+ * The created message does not contain a GUID, so >+ * get_object_sid_as_tdb_data will fail. >+ */ >+ msg = create_message(ctx); >+ >+ before = talloc_total_size(ctx); >+ >+ status = authsam_set_bad_password_indicator(ldb, ctx, msg); >+ assert_true(NT_STATUS_EQUAL(NT_STATUS_INTERNAL_ERROR, status)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * set_bad_password_indicator >+ * >+ * dbwrap_store failure. >+ */ >+static void test_set_bad_password_indicator_dbwrap_store_failed( >+ void **state) >+{ >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ TALLOC_CTX *ctx = NULL; >+ NTSTATUS status; >+ size_t before = 0; >+ size_t after = 0; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1010"); >+ >+ dbwrap_store_ret = NT_STATUS_INTERNAL_DB_CORRUPTION; >+ >+ before = talloc_total_size(ctx); >+ >+ status = authsam_set_bad_password_indicator(ldb, ctx, msg); >+ assert_true(NT_STATUS_EQUAL(NT_STATUS_INTERNAL_DB_CORRUPTION, status)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * check_bad_password_indicator >+ * >+ * set_bad_password_indicator failure. >+ */ >+static void test_check_bad_password_indicator_get_db_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ TALLOC_CTX *ctx = NULL; >+ NTSTATUS status; >+ size_t before = 0; >+ size_t after = 0; >+ bool exists = false; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ /* >+ * Clear the mock cluster_db_tmp_open return value so that >+ * it returns NULL >+ */ >+ TALLOC_FREE(cluster_db_tmp_open_ret); >+ before = talloc_total_size(ctx); >+ >+ status = authsam_check_bad_password_indicator(ldb, ctx, &exists, NULL); >+ assert_true(NT_STATUS_EQUAL(NT_STATUS_INTERNAL_ERROR, status)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * check_bad_password_indicator >+ * >+ * get_object_sid_as_tdb_data failure. >+ */ >+static void test_check_bad_password_indicator_get_object_sid_failed( >+ void **state) >+{ >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ TALLOC_CTX *ctx = NULL; >+ NTSTATUS status; >+ size_t before = 0; >+ size_t after = 0; >+ bool exists = false; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ /* >+ * The created message does not contain a ObjectSID, so >+ * get_object_sid_as_tdb_data will fail. >+ */ >+ msg = create_message(ctx); >+ >+ before = talloc_total_size(ctx); >+ >+ status = authsam_check_bad_password_indicator(ldb, ctx, &exists, msg); >+ assert_true(NT_STATUS_EQUAL(NT_STATUS_INTERNAL_ERROR, status)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * clear_bad_password_indicator >+ * >+ * set_bad_password_indicator failure. >+ */ >+static void test_clear_bad_password_indicator_get_db_failed(void **state) { >+ struct ldb_context *ldb = NULL; >+ TALLOC_CTX *ctx = NULL; >+ NTSTATUS status; >+ size_t before = 0; >+ size_t after = 0; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ /* >+ * Clear the mock cluster_db_tmp_open return value so that >+ * it returns NULL >+ */ >+ TALLOC_FREE(cluster_db_tmp_open_ret); >+ before = talloc_total_size(ctx); >+ >+ status = authsam_clear_bad_password_indicator(ldb, ctx, NULL); >+ assert_true(NT_STATUS_EQUAL(NT_STATUS_INTERNAL_ERROR, status)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * clear_bad_password_indicator >+ * >+ * get_object_sid_as_tdb_data failure. >+ */ >+static void test_clear_bad_password_indicator_get_object_sid_failed( >+ void **state) >+{ >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ TALLOC_CTX *ctx = NULL; >+ NTSTATUS status; >+ size_t before = 0; >+ size_t after = 0; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ /* >+ * The created message does not contain a objectSID, so >+ * get_object_sid_as_tdb_data will fail. >+ */ >+ msg = create_message(ctx); >+ >+ before = talloc_total_size(ctx); >+ >+ status = authsam_clear_bad_password_indicator(ldb, ctx, msg); >+ assert_true(NT_STATUS_EQUAL(NT_STATUS_INTERNAL_ERROR, status)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * clear_bad_password_indicator >+ * >+ * dbwrap_delete failure. >+ */ >+static void test_clear_bad_password_indicator_dbwrap_store_failed( >+ void **state) >+{ >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ TALLOC_CTX *ctx = NULL; >+ NTSTATUS status; >+ size_t before = 0; >+ size_t after = 0; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1010"); >+ >+ dbwrap_delete_ret = NT_STATUS_INTERNAL_DB_CORRUPTION; >+ >+ before = talloc_total_size(ctx); >+ >+ status = authsam_clear_bad_password_indicator(ldb, ctx, msg); >+ assert_true(NT_STATUS_EQUAL(NT_STATUS_INTERNAL_DB_CORRUPTION, status)); >+ >+ /* >+ * Check that all allocated memory was freed >+ */ >+ after = talloc_total_size(ctx); >+ assert_int_equal(before, after); >+ >+ /* >+ * Clean up >+ */ >+ TALLOC_FREE(ctx); >+} >+ >+/* >+ * clear_bad_password_indicator >+ * >+ * dbwrap_delete returns NT_STATUS_NOT_FOUND. >+ */ >+static void test_clear_bad_pwd_indicator_dbwrap_store_not_found( >+ void **state) >+{ >+ struct ldb_context *ldb = NULL; >+ struct ldb_message *msg = NULL; >+ TALLOC_CTX *ctx = NULL; >+ NTSTATUS status; >+ size_t before = 0; >+ size_t after = 0; >+ >+ ctx = talloc_new(*state); >+ assert_non_null(ctx); >+ >+ ldb = ldb_init(ctx, NULL); >+ assert_non_null(ldb); >+ >+ msg = create_message(ctx); >+ add_sid(msg, "S-1-5-21-2470180966-3899876309-2637894779-1010"); >+ >+ dbwrap_delete_ret = NT_STATUS_NOT_FOUND; >+ >+ before = talloc_total_size(ctx); >+ >+ status = authsam_clear_bad_password_indicator(ldb, ctx, msg); >+ 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); >+} >+ >+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_get_pso_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_reread_locked_out, 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_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_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_unit_test_setup_teardown( >+ test_get_bad_password_get_opaque_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_get_bad_password_db_open_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_set_bad_password_indicator_get_db_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_set_bad_password_indicator_get_object_sid_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_set_bad_password_indicator_dbwrap_store_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_check_bad_password_indicator_get_db_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_check_bad_password_indicator_get_object_sid_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_clear_bad_password_indicator_get_db_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_clear_bad_password_indicator_get_object_sid_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_clear_bad_password_indicator_dbwrap_store_failed, >+ setup, >+ teardown), >+ cmocka_unit_test_setup_teardown( >+ test_clear_bad_pwd_indicator_dbwrap_store_not_found, >+ 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 381a7b19bf0..eb0a26cceae 100644 >--- a/source4/auth/wscript_build >+++ b/source4/auth/wscript_build >@@ -49,6 +49,16 @@ bld.SAMBA_BINARY('test_kerberos', > for_selftest=True > ) > >+bld.SAMBA_BINARY('test_auth_sam', >+ source='tests/sam.c', >+ deps='cmocka samdb samba-security ldb tevent', >+ local_include=False, >+ for_selftest=True, >+ ldflags=''' >+ -Wl,--wrap,samdb_msg_add_int64 >+ ''' >+ ) >+ > 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