LCOV - code coverage report
Current view: top level - source4/auth - sam.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 445 665 66.9 %
Date: 2024-06-13 04:01:37 Functions: 19 19 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Password and authentication handling
       4             :    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2010
       5             :    Copyright (C) Gerald Carter                             2003
       6             :    Copyright (C) Stefan Metzmacher                         2005
       7             :    Copyright (C) Matthias Dieter Wallnöfer                 2009
       8             : 
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU General Public License
      20             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : #include "includes.h"
      24             : #include "system/time.h"
      25             : #include "auth/auth.h"
      26             : #include <ldb.h>
      27             : #include "dsdb/samdb/samdb.h"
      28             : #include "libcli/security/security.h"
      29             : #include "auth/auth_sam.h"
      30             : #include "dsdb/common/util.h"
      31             : #include "libcli/ldap/ldap_ndr.h"
      32             : #include "param/param.h"
      33             : #include "librpc/gen_ndr/ndr_winbind_c.h"
      34             : #include "lib/dbwrap/dbwrap.h"
      35             : #include "cluster/cluster.h"
      36             : 
      37             : #undef DBGC_CLASS
      38             : #define DBGC_CLASS DBGC_AUTH
      39             : 
      40             : #define KRBTGT_ATTRS                            \
      41             :         /* required for the krb5 kdc */         \
      42             :         "objectClass",                                \
      43             :         "sAMAccountName",                     \
      44             :         "userPrincipalName",                  \
      45             :         "servicePrincipalName",                       \
      46             :         "msDS-KeyVersionNumber",              \
      47             :         "msDS-SecondaryKrbTgtNumber",         \
      48             :         "msDS-SupportedEncryptionTypes",      \
      49             :         "supplementalCredentials",            \
      50             :         "msDS-AllowedToDelegateTo",           \
      51             :         "msDS-AllowedToActOnBehalfOfOtherIdentity", \
      52             :                                                 \
      53             :         /* passwords */                         \
      54             :         "unicodePwd",                         \
      55             :                                                 \
      56             :         "userAccountControl",                 \
      57             :         "msDS-User-Account-Control-Computed", \
      58             :         "objectSid",                          \
      59             :                                                 \
      60             :         "pwdLastSet",                         \
      61             :         "msDS-UserPasswordExpiryTimeComputed",        \
      62             :         "accountExpires",                     \
      63             :                                                 \
      64             :         /* Needed for RODC rule processing */   \
      65             :         "msDS-KrbTgtLinkBL"
      66             : 
      67             : const char *krbtgt_attrs[] = {
      68             :         KRBTGT_ATTRS, NULL
      69             : };
      70             : 
      71             : const char *server_attrs[] = {
      72             :         KRBTGT_ATTRS, NULL
      73             : };
      74             : 
      75             : const char *user_attrs[] = {
      76             :         /*
      77             :          * This ordering (having msDS-ResultantPSO first) is
      78             :          * important.  By processing this attribute first it is
      79             :          * available in the operational module for the other PSO
      80             :          * attribute calcuations to use.
      81             :          */
      82             :         "msDS-ResultantPSO",
      83             : 
      84             :         KRBTGT_ATTRS,
      85             : 
      86             :         "logonHours",
      87             : 
      88             :         /*
      89             :          * To allow us to zero the badPwdCount and lockoutTime on
      90             :          * successful logon, without database churn
      91             :          */
      92             :         "lockoutTime",
      93             : 
      94             :         /*
      95             :          * Needed for SendToSAM requests
      96             :          */
      97             :         "objectGUID",
      98             : 
      99             :         /* check 'allowed workstations' */
     100             :         "userWorkstations",
     101             : 
     102             :         /* required for user_info_dc, not access control: */
     103             :         "displayName",
     104             :         "scriptPath",
     105             :         "profilePath",
     106             :         "homeDirectory",
     107             :         "homeDrive",
     108             :         "lastLogon",
     109             :         "lastLogonTimestamp",
     110             :         "lastLogoff",
     111             :         "accountExpires",
     112             :         "badPwdCount",
     113             :         "logonCount",
     114             :         "primaryGroupID",
     115             :         "memberOf",
     116             :         "badPasswordTime",
     117             :         "lmPwdHistory",
     118             :         "ntPwdHistory",
     119             :         NULL,
     120             : };
     121             : 
     122             : /****************************************************************************
     123             :  Check if a user is allowed to logon at this time. Note this is the
     124             :  servers local time, as logon hours are just specified as a weekly
     125             :  bitmask.
     126             : ****************************************************************************/
     127             : 
     128       34414 : static bool logon_hours_ok(struct ldb_message *msg, const char *name_for_logs)
     129             : {
     130             :         /* In logon hours first bit is Sunday from 12AM to 1AM */
     131             :         const struct ldb_val *hours;
     132             :         struct tm *utctime;
     133             :         time_t lasttime;
     134             :         const char *asct;
     135             :         uint8_t bitmask, bitpos;
     136             : 
     137       34414 :         hours = ldb_msg_find_ldb_val(msg, "logonHours");
     138       34414 :         if (!hours) {
     139       34414 :                 DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n", name_for_logs));
     140       34414 :                 return true;
     141             :         }
     142             : 
     143           0 :         if (hours->length != 168/8) {
     144           0 :                 DEBUG(5,("logon_hours_ok: malformed logon hours restrictions for user %s\n", name_for_logs));
     145           0 :                 return true;
     146             :         }
     147             : 
     148           0 :         lasttime = time(NULL);
     149           0 :         utctime = gmtime(&lasttime);
     150           0 :         if (!utctime) {
     151           0 :                 DEBUG(1, ("logon_hours_ok: failed to get gmtime. Failing logon for user %s\n",
     152             :                         name_for_logs));
     153           0 :                 return false;
     154             :         }
     155             : 
     156             :         /* find the corresponding byte and bit */
     157           0 :         bitpos = (utctime->tm_wday * 24 + utctime->tm_hour) % 168;
     158           0 :         bitmask = 1 << (bitpos % 8);
     159             : 
     160           0 :         if (! (hours->data[bitpos/8] & bitmask)) {
     161           0 :                 struct tm *t = localtime(&lasttime);
     162           0 :                 if (!t) {
     163           0 :                         asct = "INVALID TIME";
     164             :                 } else {
     165           0 :                         asct = asctime(t);
     166           0 :                         if (!asct) {
     167           0 :                                 asct = "INVALID TIME";
     168             :                         }
     169             :                 }
     170             : 
     171           0 :                 DEBUG(1, ("logon_hours_ok: Account for user %s not allowed to "
     172             :                           "logon at this time (%s).\n",
     173             :                           name_for_logs, asct ));
     174           0 :                 return false;
     175             :         }
     176             : 
     177           0 :         asct = asctime(utctime);
     178           0 :         DEBUG(5,("logon_hours_ok: user %s allowed to logon at this time (%s)\n",
     179             :                 name_for_logs, asct ? asct : "UNKNOWN TIME" ));
     180             : 
     181           0 :         return true;
     182             : }
     183             : 
     184             : /****************************************************************************
     185             :  Do a specific test for a SAM_ACCOUNT being valid for this connection
     186             :  (ie not disabled, expired and the like).
     187             : ****************************************************************************/
     188       35034 : _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
     189             :                                      struct ldb_context *sam_ctx,
     190             :                                      uint32_t logon_parameters,
     191             :                                      struct ldb_dn *domain_dn,
     192             :                                      struct ldb_message *msg,
     193             :                                      const char *logon_workstation,
     194             :                                      const char *name_for_logs,
     195             :                                      bool allow_domain_trust,
     196             :                                      bool password_change)
     197             : {
     198             :         uint16_t acct_flags;
     199             :         const char *workstation_list;
     200             :         NTTIME acct_expiry;
     201             :         NTTIME must_change_time;
     202       35034 :         struct timeval tv_now = timeval_current();
     203       35034 :         NTTIME now = timeval_to_nttime(&tv_now);
     204             : 
     205       35034 :         DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
     206             : 
     207       35034 :         acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
     208             : 
     209       35034 :         acct_expiry = samdb_result_account_expires(msg);
     210             : 
     211             :         /* Check for when we must change this password, taking the
     212             :          * userAccountControl flags into account */
     213       35034 :         must_change_time = samdb_result_nttime(msg,
     214             :                         "msDS-UserPasswordExpiryTimeComputed", 0);
     215             : 
     216       35034 :         workstation_list = ldb_msg_find_attr_as_string(msg, "userWorkstations", NULL);
     217             : 
     218             :         /* Quit if the account was disabled. */
     219       35034 :         if (acct_flags & ACB_DISABLED) {
     220         266 :                 DEBUG(2,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
     221         266 :                 return NT_STATUS_ACCOUNT_DISABLED;
     222             :         }
     223             : 
     224             :         /* Quit if the account was locked out. */
     225       34768 :         if (acct_flags & ACB_AUTOLOCK) {
     226           0 :                 DEBUG(2,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
     227           0 :                 return NT_STATUS_ACCOUNT_LOCKED_OUT;
     228             :         }
     229             : 
     230             :         /* Test account expire time */
     231       34768 :         if (now > acct_expiry) {
     232           0 :                 DEBUG(2,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
     233           0 :                 DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
     234             :                          nt_time_string(mem_ctx, acct_expiry)));
     235           0 :                 return NT_STATUS_ACCOUNT_EXPIRED;
     236             :         }
     237             : 
     238             :         /* check for immediate expiry "must change at next logon" (but not if this is a password change request) */
     239       34768 :         if ((must_change_time == 0) && !password_change) {
     240          11 :                 DEBUG(2,("sam_account_ok: Account for user '%s' password must change!.\n",
     241             :                          name_for_logs));
     242          11 :                 return NT_STATUS_PASSWORD_MUST_CHANGE;
     243             :         }
     244             : 
     245             :         /* check for expired password (but not if this is a password change request) */
     246       34757 :         if ((must_change_time < now) && !password_change) {
     247           0 :                 DEBUG(2,("sam_account_ok: Account for user '%s' password expired!.\n",
     248             :                          name_for_logs));
     249           0 :                 DEBUG(2,("sam_account_ok: Password expired at '%s' unix time.\n",
     250             :                          nt_time_string(mem_ctx, must_change_time)));
     251           0 :                 return NT_STATUS_PASSWORD_EXPIRED;
     252             :         }
     253             : 
     254             :         /* Test workstation. Workstation list is comma separated. */
     255       34757 :         if (logon_workstation && workstation_list && *workstation_list) {
     256         343 :                 bool invalid_ws = true;
     257             :                 int i;
     258         343 :                 char **workstations = str_list_make(mem_ctx, workstation_list, ",");
     259             : 
     260         686 :                 for (i = 0; workstations && workstations[i]; i++) {
     261         343 :                         DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
     262             :                                   workstations[i], logon_workstation));
     263             : 
     264         343 :                         if (strequal(workstations[i], logon_workstation)) {
     265           0 :                                 invalid_ws = false;
     266           0 :                                 break;
     267             :                         }
     268             :                 }
     269             : 
     270         343 :                 talloc_free(workstations);
     271             : 
     272         343 :                 if (invalid_ws) {
     273         343 :                         return NT_STATUS_INVALID_WORKSTATION;
     274             :                 }
     275             :         }
     276             : 
     277       34414 :         if (!logon_hours_ok(msg, name_for_logs)) {
     278           0 :                 return NT_STATUS_INVALID_LOGON_HOURS;
     279             :         }
     280             : 
     281       34414 :         if (!allow_domain_trust) {
     282       16005 :                 if (acct_flags & ACB_DOMTRUST) {
     283           0 :                         DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
     284           0 :                         return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
     285             :                 }
     286             :         }
     287       34414 :         if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
     288        6704 :                 if (acct_flags & ACB_SVRTRUST) {
     289           0 :                         DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
     290           0 :                         return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
     291             :                 }
     292             :         }
     293       34414 :         if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
     294             :                 /* TODO: this fails with current solaris client. We
     295             :                    need to work with Gordon to work out why */
     296        5678 :                 if (acct_flags & ACB_WSTRUST) {
     297         342 :                         DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
     298         342 :                         return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
     299             :                 }
     300             :         }
     301             : 
     302       34072 :         return NT_STATUS_OK;
     303             : }
     304             : 
     305       88435 : static NTSTATUS authsam_domain_group_filter(TALLOC_CTX *mem_ctx,
     306             :                                             char **_filter)
     307             : {
     308       88435 :         char *filter = NULL;
     309             : 
     310       88435 :         *_filter = NULL;
     311             : 
     312       88435 :         filter = talloc_strdup(mem_ctx, "(&(objectClass=group)");
     313       88435 :         if (filter == NULL) {
     314           0 :                 return NT_STATUS_NO_MEMORY;
     315             :         }
     316             : 
     317             :         /*
     318             :          * Skip all builtin groups, they're added later.
     319             :          */
     320       88435 :         filter = talloc_asprintf_append_buffer(filter,
     321             :                                 "(!(groupType:1.2.840.113556.1.4.803:=%u))",
     322             :                                 GROUP_TYPE_BUILTIN_LOCAL_GROUP);
     323       88435 :         if (filter == NULL) {
     324           0 :                 return NT_STATUS_NO_MEMORY;
     325             :         }
     326             :         /*
     327             :          * Only include security groups.
     328             :          */
     329       88435 :         filter = talloc_asprintf_append_buffer(filter,
     330             :                                 "(groupType:1.2.840.113556.1.4.803:=%u))",
     331             :                                 GROUP_TYPE_SECURITY_ENABLED);
     332       88435 :         if (filter == NULL) {
     333           0 :                 return NT_STATUS_NO_MEMORY;
     334             :         }
     335             : 
     336       88435 :         *_filter = filter;
     337       88435 :         return NT_STATUS_OK;
     338             : }
     339             : 
     340       52180 : _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
     341             :                                            struct ldb_context *sam_ctx,
     342             :                                            const char *netbios_name,
     343             :                                            const char *domain_name,
     344             :                                            const char *dns_domain_name,
     345             :                                            struct ldb_dn *domain_dn,
     346             :                                            struct ldb_message *msg,
     347             :                                            DATA_BLOB user_sess_key,
     348             :                                            DATA_BLOB lm_sess_key,
     349             :                                            struct auth_user_info_dc **_user_info_dc)
     350             : {
     351             :         NTSTATUS status;
     352             :         struct auth_user_info_dc *user_info_dc;
     353             :         struct auth_user_info *info;
     354       52180 :         const char *str = NULL;
     355       52180 :         char *filter = NULL;
     356             :         /* SIDs for the account and his primary group */
     357             :         struct dom_sid *account_sid;
     358             :         struct dom_sid_buf buf;
     359             :         const char *primary_group_dn;
     360             :         DATA_BLOB primary_group_blob;
     361             :         /* SID structures for the expanded group memberships */
     362       52180 :         struct dom_sid *sids = NULL;
     363       52180 :         unsigned int num_sids = 0, i;
     364             :         struct dom_sid *domain_sid;
     365             :         TALLOC_CTX *tmp_ctx;
     366             :         struct ldb_message_element *el;
     367             : 
     368       52180 :         user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
     369       52180 :         NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
     370             : 
     371       52180 :         tmp_ctx = talloc_new(user_info_dc);
     372       52180 :         if (tmp_ctx == NULL) {
     373           0 :                 TALLOC_FREE(user_info_dc);
     374           0 :                 return NT_STATUS_NO_MEMORY;
     375             :         }
     376             : 
     377       52180 :         sids = talloc_array(user_info_dc, struct dom_sid, 2);
     378       52180 :         if (sids == NULL) {
     379           0 :                 TALLOC_FREE(user_info_dc);
     380           0 :                 return NT_STATUS_NO_MEMORY;
     381             :         }
     382             : 
     383       52180 :         num_sids = 2;
     384             : 
     385       52180 :         account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
     386       52180 :         if (account_sid == NULL) {
     387           0 :                 TALLOC_FREE(user_info_dc);
     388           0 :                 return NT_STATUS_NO_MEMORY;
     389             :         }
     390             : 
     391       52180 :         status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
     392       52180 :         if (!NT_STATUS_IS_OK(status)) {
     393           0 :                 talloc_free(user_info_dc);
     394           0 :                 return status;
     395             :         }
     396             : 
     397       52180 :         sids[PRIMARY_USER_SID_INDEX] = *account_sid;
     398       52180 :         sids[PRIMARY_GROUP_SID_INDEX] = *domain_sid;
     399       52180 :         sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX], ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
     400             : 
     401             :         /*
     402             :          * Filter out builtin groups from this token. We will search
     403             :          * for builtin groups later, and not include them in the PAC
     404             :          * or SamLogon validation info.
     405             :          */
     406       52180 :         status = authsam_domain_group_filter(tmp_ctx, &filter);
     407       52180 :         if (!NT_STATUS_IS_OK(status)) {
     408           0 :                 TALLOC_FREE(user_info_dc);
     409           0 :                 return status;
     410             :         }
     411             : 
     412      101630 :         primary_group_dn = talloc_asprintf(
     413             :                 tmp_ctx,
     414             :                 "<SID=%s>",
     415       52180 :                 dom_sid_str_buf(&sids[PRIMARY_GROUP_SID_INDEX], &buf));
     416       52180 :         if (primary_group_dn == NULL) {
     417           0 :                 TALLOC_FREE(user_info_dc);
     418           0 :                 return NT_STATUS_NO_MEMORY;
     419             :         }
     420             : 
     421       52180 :         primary_group_blob = data_blob_string_const(primary_group_dn);
     422             : 
     423             :         /* Expands the primary group - this function takes in
     424             :          * memberOf-like values, so we fake one up with the
     425             :          * <SID=S-...> format of DN and then let it expand
     426             :          * them, as long as they meet the filter - so only
     427             :          * domain groups, not builtin groups
     428             :          *
     429             :          * The primary group is still treated specially, so we set the
     430             :          * 'only childs' flag to true
     431             :          */
     432       52180 :         status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
     433             :                                            user_info_dc, &sids, &num_sids);
     434       52180 :         if (!NT_STATUS_IS_OK(status)) {
     435           0 :                 talloc_free(user_info_dc);
     436           0 :                 return status;
     437             :         }
     438             : 
     439             :         /* Expands the additional groups */
     440       52180 :         el = ldb_msg_find_element(msg, "memberOf");
     441      210599 :         for (i = 0; el && i < el->num_values; i++) {
     442             :                 /* This function takes in memberOf values and expands
     443             :                  * them, as long as they meet the filter - so only
     444             :                  * domain groups, not builtin groups */
     445      158419 :                 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
     446             :                                                    user_info_dc, &sids, &num_sids);
     447      158419 :                 if (!NT_STATUS_IS_OK(status)) {
     448           0 :                         talloc_free(user_info_dc);
     449           0 :                         return status;
     450             :                 }
     451             :         }
     452             : 
     453       52180 :         user_info_dc->sids = sids;
     454       52180 :         user_info_dc->num_sids = num_sids;
     455             : 
     456       52180 :         user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
     457       52180 :         NT_STATUS_HAVE_NO_MEMORY(user_info_dc->info);
     458             : 
     459       52180 :         str = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
     460       52180 :         info->account_name = talloc_strdup(info, str);
     461       52180 :         if (info->account_name == NULL) {
     462           0 :                 TALLOC_FREE(user_info_dc);
     463           0 :                 return NT_STATUS_NO_MEMORY;
     464             :         }
     465             : 
     466       52180 :         str = ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL);
     467       52180 :         if (str == NULL && dns_domain_name != NULL) {
     468       43887 :                 info->user_principal_name = talloc_asprintf(info, "%s@%s",
     469             :                                         info->account_name,
     470             :                                         dns_domain_name);
     471       43887 :                 if (info->user_principal_name == NULL) {
     472           0 :                         TALLOC_FREE(user_info_dc);
     473           0 :                         return NT_STATUS_NO_MEMORY;
     474             :                 }
     475       43887 :                 info->user_principal_constructed = true;
     476        8293 :         } else if (str != NULL) {
     477        8195 :                 info->user_principal_name = talloc_strdup(info, str);
     478        8195 :                 if (info->user_principal_name == NULL) {
     479           0 :                         TALLOC_FREE(user_info_dc);
     480           0 :                         return NT_STATUS_NO_MEMORY;
     481             :                 }
     482             :         }
     483             : 
     484       52180 :         info->domain_name = talloc_strdup(info, domain_name);
     485       52180 :         if (info->domain_name == NULL) {
     486           0 :                 TALLOC_FREE(user_info_dc);
     487           0 :                 return NT_STATUS_NO_MEMORY;
     488             :         }
     489             : 
     490       52180 :         if (dns_domain_name != NULL) {
     491       52082 :                 info->dns_domain_name = talloc_strdup(info, dns_domain_name);
     492       52082 :                 if (info->dns_domain_name == NULL) {
     493           0 :                         TALLOC_FREE(user_info_dc);
     494           0 :                         return NT_STATUS_NO_MEMORY;
     495             :                 }
     496             :         }
     497             : 
     498       52180 :         str = ldb_msg_find_attr_as_string(msg, "displayName", "");
     499       52180 :         info->full_name = talloc_strdup(info, str);
     500       52180 :         if (info->full_name == NULL) {
     501           0 :                 TALLOC_FREE(user_info_dc);
     502           0 :                 return NT_STATUS_NO_MEMORY;
     503             :         }
     504             : 
     505       52180 :         str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
     506       52180 :         info->logon_script = talloc_strdup(info, str);
     507       52180 :         if (info->logon_script == NULL) {
     508           0 :                 TALLOC_FREE(user_info_dc);
     509           0 :                 return NT_STATUS_NO_MEMORY;
     510             :         }
     511             : 
     512       52180 :         str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
     513       52180 :         info->profile_path = talloc_strdup(info, str);
     514       52180 :         if (info->profile_path == NULL) {
     515           0 :                 TALLOC_FREE(user_info_dc);
     516           0 :                 return NT_STATUS_NO_MEMORY;
     517             :         }
     518             : 
     519       52180 :         str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
     520       52180 :         info->home_directory = talloc_strdup(info, str);
     521       52180 :         if (info->home_directory == NULL) {
     522           0 :                 TALLOC_FREE(user_info_dc);
     523           0 :                 return NT_STATUS_NO_MEMORY;
     524             :         }
     525             : 
     526       52180 :         str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
     527       52180 :         info->home_drive = talloc_strdup(info, str);
     528       52180 :         if (info->home_drive == NULL) {
     529           0 :                 TALLOC_FREE(user_info_dc);
     530           0 :                 return NT_STATUS_NO_MEMORY;
     531             :         }
     532             : 
     533       52180 :         info->logon_server = talloc_strdup(info, netbios_name);
     534       52180 :         if (info->logon_server == NULL) {
     535           0 :                 TALLOC_FREE(user_info_dc);
     536           0 :                 return NT_STATUS_NO_MEMORY;
     537             :         }
     538             : 
     539       52180 :         info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
     540       52180 :         info->last_logoff = samdb_result_last_logoff(msg);
     541       52180 :         info->acct_expiry = samdb_result_account_expires(msg);
     542       52180 :         info->last_password_change = samdb_result_nttime(msg,
     543             :                 "pwdLastSet", 0);
     544             :         info->allow_password_change
     545       52180 :                 = samdb_result_allow_password_change(sam_ctx, mem_ctx,
     546             :                         domain_dn, msg, "pwdLastSet");
     547       52180 :         info->force_password_change = samdb_result_nttime(msg,
     548             :                 "msDS-UserPasswordExpiryTimeComputed", 0);
     549       52180 :         info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
     550       52180 :         info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
     551             :                 0);
     552             : 
     553       52180 :         info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
     554             : 
     555       52180 :         user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
     556             :                                                          user_sess_key.data,
     557             :                                                          user_sess_key.length);
     558       52180 :         if (user_sess_key.data) {
     559           0 :                 if (user_info_dc->user_session_key.data == NULL) {
     560           0 :                         TALLOC_FREE(user_info_dc);
     561           0 :                         return NT_STATUS_NO_MEMORY;
     562             :                 }
     563             :         }
     564       52180 :         user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
     565             :                                                        lm_sess_key.data,
     566             :                                                        lm_sess_key.length);
     567       52180 :         if (lm_sess_key.data) {
     568           0 :                 if (user_info_dc->lm_session_key.data == NULL) {
     569           0 :                         TALLOC_FREE(user_info_dc);
     570           0 :                         return NT_STATUS_NO_MEMORY;
     571             :                 }
     572             :         }
     573             : 
     574       52180 :         if (info->acct_flags & ACB_SVRTRUST) {
     575             :                 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
     576             :                    PAC */
     577        2541 :                 user_info_dc->sids = talloc_realloc(user_info_dc,
     578             :                                                    user_info_dc->sids,
     579             :                                                    struct dom_sid,
     580             :                                                    user_info_dc->num_sids+1);
     581        2541 :                 if (user_info_dc->sids == NULL) {
     582           0 :                         TALLOC_FREE(user_info_dc);
     583           0 :                         return NT_STATUS_NO_MEMORY;
     584             :                 }
     585        2541 :                 user_info_dc->sids[user_info_dc->num_sids] = global_sid_Enterprise_DCs;
     586        2541 :                 user_info_dc->num_sids++;
     587             :         }
     588             : 
     589       52180 :         if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
     590             :             (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
     591             :                 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
     592         684 :                 user_info_dc->sids = talloc_realloc(user_info_dc,
     593             :                                                    user_info_dc->sids,
     594             :                                                    struct dom_sid,
     595             :                                                    user_info_dc->num_sids+1);
     596         684 :                 if (user_info_dc->sids == NULL) {
     597           0 :                         TALLOC_FREE(user_info_dc);
     598           0 :                         return NT_STATUS_NO_MEMORY;
     599             :                 }
     600         684 :                 user_info_dc->sids[user_info_dc->num_sids] = *domain_sid;
     601         684 :                 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids],
     602             :                             DOMAIN_RID_ENTERPRISE_READONLY_DCS);
     603         684 :                 user_info_dc->num_sids++;
     604             :         }
     605             : 
     606       52180 :         info->authenticated = true;
     607             : 
     608       52180 :         talloc_free(tmp_ctx);
     609       52180 :         *_user_info_dc = user_info_dc;
     610             : 
     611       52180 :         return NT_STATUS_OK;
     612             : }
     613             : 
     614       36255 : _PUBLIC_ NTSTATUS authsam_update_user_info_dc(TALLOC_CTX *mem_ctx,
     615             :                         struct ldb_context *sam_ctx,
     616             :                         struct auth_user_info_dc *user_info_dc)
     617             : {
     618       36255 :         char *filter = NULL;
     619             :         NTSTATUS status;
     620             :         uint32_t i;
     621       36255 :         uint32_t n = 0;
     622             : 
     623             :         /*
     624             :          * This function exists to expand group memberships
     625             :          * in the local domain (forest), as the token
     626             :          * may come from a different domain.
     627             :          */
     628             : 
     629             :         /*
     630             :          * Filter out builtin groups from this token. We will search
     631             :          * for builtin groups later.
     632             :          */
     633       36255 :         status = authsam_domain_group_filter(mem_ctx, &filter);
     634       36255 :         if (!NT_STATUS_IS_OK(status)) {
     635           0 :                 TALLOC_FREE(user_info_dc);
     636           0 :                 return status;
     637             :         }
     638             : 
     639             :         /*
     640             :          * We loop only over the existing number of
     641             :          * sids.
     642             :          */
     643       36255 :         n = user_info_dc->num_sids;
     644      282069 :         for (i = 0; i < n; i++) {
     645      245814 :                 struct dom_sid *sid = &user_info_dc->sids[i];
     646             :                 struct dom_sid_buf sid_buf;
     647             :                 char dn_str[sizeof(sid_buf.buf)*2];
     648      245814 :                 DATA_BLOB dn_blob = data_blob_null;
     649             : 
     650      245814 :                 snprintf(dn_str,
     651             :                         sizeof(dn_str),
     652             :                         "<SID=%s>",
     653             :                         dom_sid_str_buf(sid, &sid_buf));
     654      245814 :                 dn_blob = data_blob_string_const(dn_str);
     655             : 
     656             :                 /*
     657             :                  * We already have the SID in the token, so set
     658             :                  * 'only childs' flag to true and add all
     659             :                  * groups which match the filter.
     660             :                  */
     661      245814 :                 status = dsdb_expand_nested_groups(sam_ctx, &dn_blob,
     662             :                                                    true, filter,
     663             :                                                    user_info_dc,
     664             :                                                    &user_info_dc->sids,
     665      245814 :                                                    &user_info_dc->num_sids);
     666      245814 :                 if (!NT_STATUS_IS_OK(status)) {
     667           0 :                         return status;
     668             :                 }
     669             :         }
     670             : 
     671       36255 :         return NT_STATUS_OK;
     672             : }
     673             : 
     674       71696 : NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
     675             :                                    TALLOC_CTX *mem_ctx, const char *principal,
     676             :                                    const char **attrs,
     677             :                                    struct ldb_dn **domain_dn,
     678             :                                    struct ldb_message **msg)
     679             : {
     680             :         struct ldb_dn *user_dn;
     681             :         NTSTATUS nt_status;
     682       71696 :         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
     683             :         int ret;
     684             : 
     685       71696 :         if (!tmp_ctx) {
     686           0 :                 return NT_STATUS_NO_MEMORY;
     687             :         }
     688             : 
     689       71696 :         nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
     690             :                                               &user_dn, domain_dn);
     691       71696 :         if (!NT_STATUS_IS_OK(nt_status)) {
     692        2426 :                 talloc_free(tmp_ctx);
     693        2426 :                 return nt_status;
     694             :         }
     695             : 
     696             :         /* pull the user attributes */
     697       69270 :         ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
     698             :                               LDB_SCOPE_BASE, attrs,
     699             :                               DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
     700             :                               "(objectClass=*)");
     701       69270 :         if (ret != LDB_SUCCESS) {
     702           0 :                 talloc_free(tmp_ctx);
     703           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
     704             :         }
     705       69270 :         talloc_steal(mem_ctx, *msg);
     706       69270 :         talloc_steal(mem_ctx, *domain_dn);
     707       69270 :         talloc_free(tmp_ctx);
     708             : 
     709       69270 :         return NT_STATUS_OK;
     710             : }
     711             : 
     712             : /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
     713             : 
     714             :  Supply either a principal or a DN
     715             : */
     716         159 : NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
     717             :                                            struct loadparm_context *lp_ctx,
     718             :                                            struct ldb_context *sam_ctx,
     719             :                                            const char *principal,
     720             :                                            struct ldb_dn *user_dn,
     721             :                                            struct auth_user_info_dc **user_info_dc)
     722             : {
     723             :         NTSTATUS nt_status;
     724         159 :         DATA_BLOB user_sess_key = data_blob(NULL, 0);
     725         159 :         DATA_BLOB lm_sess_key = data_blob(NULL, 0);
     726             : 
     727             :         struct ldb_message *msg;
     728             :         struct ldb_dn *domain_dn;
     729             : 
     730         159 :         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
     731         159 :         if (!tmp_ctx) {
     732           0 :                 return NT_STATUS_NO_MEMORY;
     733             :         }
     734             : 
     735         159 :         if (principal) {
     736           0 :                 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
     737             :                                                       user_attrs, &domain_dn, &msg);
     738           0 :                 if (!NT_STATUS_IS_OK(nt_status)) {
     739           0 :                         talloc_free(tmp_ctx);
     740           0 :                         return nt_status;
     741             :                 }
     742         159 :         } else if (user_dn) {
     743             :                 struct dom_sid *user_sid, *domain_sid;
     744             :                 int ret;
     745             :                 /* pull the user attributes */
     746         159 :                 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
     747             :                                       LDB_SCOPE_BASE, user_attrs,
     748             :                                       DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
     749             :                                       "(objectClass=*)");
     750         159 :                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
     751           0 :                         talloc_free(tmp_ctx);
     752           0 :                         return NT_STATUS_NO_SUCH_USER;
     753         159 :                 } else if (ret != LDB_SUCCESS) {
     754           0 :                         talloc_free(tmp_ctx);
     755           0 :                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
     756             :                 }
     757             : 
     758         159 :                 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
     759             : 
     760         159 :                 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
     761         159 :                 if (!NT_STATUS_IS_OK(nt_status)) {
     762           0 :                         return nt_status;
     763             :                 }
     764             : 
     765         159 :                 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
     766             :                                           "(&(objectSid=%s)(objectClass=domain))",
     767             :                                             ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
     768         159 :                 if (!domain_dn) {
     769             :                         struct dom_sid_buf buf;
     770           0 :                         DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
     771             :                                   dom_sid_str_buf(domain_sid, &buf)));
     772           0 :                         return NT_STATUS_NO_SUCH_USER;
     773             :                 }
     774             : 
     775             :         } else {
     776           0 :                 return NT_STATUS_INVALID_PARAMETER;
     777             :         }
     778             : 
     779         159 :         nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
     780             :                                              lpcfg_netbios_name(lp_ctx),
     781             :                                              lpcfg_sam_name(lp_ctx),
     782             :                                              lpcfg_sam_dnsname(lp_ctx),
     783             :                                              domain_dn,
     784             :                                              msg,
     785             :                                              user_sess_key, lm_sess_key,
     786             :                                              user_info_dc);
     787         159 :         if (!NT_STATUS_IS_OK(nt_status)) {
     788           0 :                 talloc_free(tmp_ctx);
     789           0 :                 return nt_status;
     790             :         }
     791             : 
     792         159 :         talloc_steal(mem_ctx, *user_info_dc);
     793         159 :         talloc_free(tmp_ctx);
     794             : 
     795         159 :         return NT_STATUS_OK;
     796             : }
     797             : 
     798             : /*
     799             :  * Returns the details for the Password Settings Object (PSO), if one applies
     800             :  * the user.
     801             :  */
     802        3503 : static int authsam_get_user_pso(struct ldb_context *sam_ctx,
     803             :                                 TALLOC_CTX *mem_ctx,
     804             :                                 struct ldb_message *user_msg,
     805             :                                 struct ldb_message **pso_msg)
     806             : {
     807        3503 :         const char *attrs[] = { "msDS-LockoutThreshold",
     808             :                                 "msDS-LockoutObservationWindow",
     809             :                                 NULL };
     810        3503 :         struct ldb_dn *pso_dn = NULL;
     811        3503 :         struct ldb_result *res = NULL;
     812             :         int ret;
     813             : 
     814             :         /* check if the user has a PSO that applies to it */
     815        3503 :         pso_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, user_msg,
     816             :                                          "msDS-ResultantPSO");
     817             : 
     818        3503 :         if (pso_dn != NULL) {
     819          25 :                 ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, pso_dn, attrs, 0);
     820          25 :                 if (ret != LDB_SUCCESS) {
     821           0 :                         return ret;
     822             :                 }
     823             : 
     824          25 :                 *pso_msg = res->msgs[0];
     825             :         }
     826             : 
     827        3503 :         return LDB_SUCCESS;
     828             : }
     829             : 
     830             : /*
     831             :  * Re-read the bad password and successful logon data for a user.
     832             :  *
     833             :  * The DN in the passed user record should contain the "objectGUID" in case the
     834             :  * object DN has changed.
     835             :  */
     836       27986 : NTSTATUS authsam_reread_user_logon_data(
     837             :         struct ldb_context *sam_ctx,
     838             :         TALLOC_CTX *mem_ctx,
     839             :         const struct ldb_message *user_msg,
     840             :         struct ldb_message **current)
     841             : {
     842       27986 :         const struct ldb_val *v = NULL;
     843       27986 :         struct ldb_result *res = NULL;
     844       27986 :         uint16_t acct_flags = 0;
     845       27986 :         const char *attr_name = "msDS-User-Account-Control-Computed";
     846             : 
     847             :         int ret;
     848             : 
     849             :         /*
     850             :          * Re-read the account details, using the GUID in case the DN
     851             :          * is being changed (this is automatic in LDB because the
     852             :          * original search also used DSDB_SEARCH_SHOW_EXTENDED_DN)
     853             :          *
     854             :          * We re read all the attributes in user_attrs, rather than using a
     855             :          * subset to ensure that we can reuse existing validation code.
     856             :          */
     857       27986 :         ret = dsdb_search_dn(sam_ctx,
     858             :                              mem_ctx,
     859             :                              &res,
     860        2375 :                              user_msg->dn,
     861             :                              user_attrs,
     862             :                              DSDB_SEARCH_SHOW_EXTENDED_DN);
     863       27986 :         if (ret != LDB_SUCCESS) {
     864           0 :                 DBG_ERR("Unable to re-read account control data for %s\n",
     865             :                         ldb_dn_get_linearized(user_msg->dn));
     866           0 :                 return NT_STATUS_INTERNAL_ERROR;
     867             :         }
     868             : 
     869             :         /*
     870             :          * Ensure the account has not been locked out by another request
     871             :          */
     872       27986 :         v = ldb_msg_find_ldb_val(res->msgs[0], attr_name);
     873       27986 :         if (v == NULL || v->data == NULL) {
     874           0 :                 DBG_ERR("No %s attribute for %s\n",
     875             :                         attr_name,
     876             :                         ldb_dn_get_linearized(user_msg->dn));
     877           0 :                 TALLOC_FREE(res);
     878           0 :                 return NT_STATUS_INTERNAL_ERROR;
     879             :         }
     880       27986 :         acct_flags = samdb_result_acct_flags(res->msgs[0], attr_name);
     881       27986 :         if (acct_flags & ACB_AUTOLOCK) {
     882           0 :                 DBG_WARNING(
     883             :                         "Account for user %s was locked out.\n",
     884             :                         ldb_dn_get_linearized(user_msg->dn));
     885           0 :                 TALLOC_FREE(res);
     886           0 :                 return NT_STATUS_ACCOUNT_LOCKED_OUT;
     887             :         }
     888       27986 :         *current = talloc_steal(mem_ctx, res->msgs[0]);
     889       27986 :         TALLOC_FREE(res);
     890       27986 :         return NT_STATUS_OK;
     891             : }
     892             : 
     893       68911 : static struct db_context *authsam_get_bad_password_db(
     894             :         TALLOC_CTX *mem_ctx,
     895             :         struct ldb_context *sam_ctx)
     896             : {
     897       68911 :         struct loadparm_context *lp_ctx = NULL;
     898       68911 :         const char *db_name = "bad_password";
     899       68911 :         struct db_context *db_ctx =  NULL;
     900             : 
     901       68911 :         lp_ctx = ldb_get_opaque(sam_ctx, "loadparm");
     902       68911 :         if (lp_ctx == NULL) {
     903           0 :                 DBG_ERR("Unable to get loadparm_context\n");
     904           0 :                 return NULL;
     905             :         }
     906             : 
     907       68911 :         db_ctx = cluster_db_tmp_open(mem_ctx, lp_ctx, db_name, TDB_DEFAULT);
     908       68911 :         if (db_ctx == NULL) {
     909           0 :                 DBG_ERR("Unable to open bad password attempts database\n");
     910           0 :                 return NULL;
     911             :         }
     912       68911 :         return db_ctx;
     913             : }
     914             : 
     915       68911 : static NTSTATUS get_object_sid_as_tdb_data(
     916             :         TALLOC_CTX *mem_ctx,
     917             :         const struct ldb_message *msg,
     918             :         struct dom_sid_buf *buf,
     919             :         TDB_DATA *key)
     920             : {
     921       68911 :         struct dom_sid *objectsid = NULL;
     922             : 
     923             :         /*
     924             :          * Convert the objectSID to a human readable form to
     925             :          * make debugging easier
     926             :          */
     927       68911 :         objectsid = samdb_result_dom_sid(mem_ctx, msg, "objectSID");
     928       68911 :         if (objectsid == NULL) {
     929           0 :                 DBG_ERR("Unable to extract objectSID\n");
     930           0 :                 return NT_STATUS_INTERNAL_ERROR;
     931             :         }
     932       68911 :         dom_sid_str_buf(objectsid, buf);
     933       68911 :         key->dptr = (unsigned char *)buf->buf;
     934       68911 :         key->dsize = strlen(buf->buf);
     935             : 
     936       68911 :         talloc_free(objectsid);
     937             : 
     938       68911 :         return NT_STATUS_OK;
     939             : }
     940             : 
     941             : /*
     942             :  * Add the users objectSID to the bad password attempt database
     943             :  * to indicate that last authentication failed due to a bad password
     944             :  */
     945         521 : static NTSTATUS authsam_set_bad_password_indicator(
     946             :         struct ldb_context *sam_ctx,
     947             :         TALLOC_CTX *mem_ctx,
     948             :         const struct ldb_message *msg)
     949             : {
     950         521 :         NTSTATUS status = NT_STATUS_OK;
     951             :         struct dom_sid_buf buf;
     952         521 :         TDB_DATA key = {0};
     953         521 :         TDB_DATA value = {0};
     954         521 :         struct db_context *db = NULL;
     955             : 
     956         521 :         TALLOC_CTX *ctx = talloc_new(mem_ctx);
     957         521 :         if (ctx == NULL) {
     958           0 :                 return NT_STATUS_NO_MEMORY;
     959             :         }
     960             : 
     961         521 :         db = authsam_get_bad_password_db(ctx, sam_ctx);
     962         521 :         if (db == NULL) {
     963           0 :                 status = NT_STATUS_INTERNAL_ERROR;
     964           0 :                 goto exit;
     965             :         }
     966             : 
     967         521 :         status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
     968         521 :         if (!NT_STATUS_IS_OK(status)) {
     969           0 :                 goto exit;
     970             :         }
     971             : 
     972         521 :         status = dbwrap_store(db, key, value, 0);
     973         521 :         if (!NT_STATUS_IS_OK(status)) {
     974           0 :                 DBG_ERR("Unable to store bad password indicator\n");
     975             :         }
     976        1042 : exit:
     977         521 :         talloc_free(ctx);
     978         521 :         return status;
     979             : }
     980             : 
     981             : /*
     982             :  * see if the users objectSID is in the bad password attempt database
     983             :  */
     984       34195 : static NTSTATUS authsam_check_bad_password_indicator(
     985             :         struct ldb_context *sam_ctx,
     986             :         TALLOC_CTX *mem_ctx,
     987             :         bool *exists,
     988             :         const struct ldb_message *msg)
     989             : {
     990       34195 :         NTSTATUS status = NT_STATUS_OK;
     991             :         struct dom_sid_buf buf;
     992       34195 :         TDB_DATA key = {0};
     993       34195 :         struct db_context *db = NULL;
     994             : 
     995       34195 :         TALLOC_CTX *ctx = talloc_new(mem_ctx);
     996       34195 :         if (ctx == NULL) {
     997           0 :                 return NT_STATUS_NO_MEMORY;
     998             :         }
     999             : 
    1000       34195 :         db = authsam_get_bad_password_db(ctx, sam_ctx);
    1001       34195 :         if (db == NULL) {
    1002           0 :                 status = NT_STATUS_INTERNAL_ERROR;
    1003           0 :                 goto exit;
    1004             :         }
    1005             : 
    1006       34195 :         status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
    1007       34195 :         if (!NT_STATUS_IS_OK(status)) {
    1008           0 :                 goto exit;
    1009             :         }
    1010             : 
    1011       34195 :         *exists = dbwrap_exists(db, key);
    1012       34195 : exit:
    1013       34195 :         talloc_free(ctx);
    1014       34195 :         return status;
    1015             : }
    1016             : 
    1017             : /*
    1018             :  * Remove the users objectSID to the bad password attempt database
    1019             :  * to indicate that last authentication succeeded.
    1020             :  */
    1021       34195 : static NTSTATUS authsam_clear_bad_password_indicator(
    1022             :         struct ldb_context *sam_ctx,
    1023             :         TALLOC_CTX *mem_ctx,
    1024             :         const struct ldb_message *msg)
    1025             : {
    1026       34195 :         NTSTATUS status = NT_STATUS_OK;
    1027             :         struct dom_sid_buf buf;
    1028       34195 :         TDB_DATA key = {0};
    1029       34195 :         struct db_context *db = NULL;
    1030             : 
    1031       34195 :         TALLOC_CTX *ctx = talloc_new(mem_ctx);
    1032       34195 :         if (ctx == NULL) {
    1033           0 :                 return NT_STATUS_NO_MEMORY;
    1034             :         }
    1035             : 
    1036       34195 :         db = authsam_get_bad_password_db(ctx, sam_ctx);
    1037       34195 :         if (db == NULL) {
    1038           0 :                 status = NT_STATUS_INTERNAL_ERROR;
    1039           0 :                 goto exit;
    1040             :         }
    1041             : 
    1042       34195 :         status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
    1043       34195 :         if (!NT_STATUS_IS_OK(status)) {
    1044           0 :                 goto exit;
    1045             :         }
    1046             : 
    1047       34195 :         status = dbwrap_delete(db, key);
    1048       34195 :         if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, status)) {
    1049             :                 /*
    1050             :                  * Ok there was no bad password indicator this is expected
    1051             :                  */
    1052       33861 :                 status = NT_STATUS_OK;
    1053             :         }
    1054       34195 :         if (NT_STATUS_IS_ERR(status)) {
    1055           0 :                 DBG_ERR("Unable to delete bad password indicator, %s %s\n",
    1056             :                         nt_errstr(status),
    1057             :                         get_friendly_nt_error_msg(status));
    1058             :         }
    1059       65733 : exit:
    1060       34195 :         talloc_free(ctx);
    1061       34195 :         return status;
    1062             : }
    1063             : 
    1064        3503 : NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
    1065             :                                       struct ldb_message *msg,
    1066             :                                       struct ldb_dn *domain_dn)
    1067             : {
    1068        3503 :         const char *attrs[] = { "lockoutThreshold",
    1069             :                                 "lockOutObservationWindow",
    1070             :                                 "lockoutDuration",
    1071             :                                 "pwdProperties",
    1072             :                                 NULL };
    1073             :         int ret;
    1074             :         NTSTATUS status;
    1075             :         struct ldb_result *domain_res;
    1076        3503 :         struct ldb_message *msg_mod = NULL;
    1077        3503 :         struct ldb_message *current = NULL;
    1078        3503 :         struct ldb_message *pso_msg = NULL;
    1079        3503 :         bool txn_active = false;
    1080             :         TALLOC_CTX *mem_ctx;
    1081             : 
    1082        3503 :         mem_ctx = talloc_new(msg);
    1083        3503 :         if (mem_ctx == NULL) {
    1084           0 :                 return NT_STATUS_NO_MEMORY;
    1085             :         }
    1086             : 
    1087        3503 :         ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
    1088        3503 :         if (ret != LDB_SUCCESS) {
    1089           0 :                 TALLOC_FREE(mem_ctx);
    1090           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
    1091             :         }
    1092             : 
    1093        3503 :         ret = authsam_get_user_pso(sam_ctx, mem_ctx, msg, &pso_msg);
    1094        3503 :         if (ret != LDB_SUCCESS) {
    1095             : 
    1096             :                 /*
    1097             :                  * fallback to using the domain defaults so that we still
    1098             :                  * record the bad password attempt
    1099             :                  */
    1100           0 :                 DBG_ERR("Error (%d) checking PSO for %s\n",
    1101             :                         ret, ldb_dn_get_linearized(msg->dn));
    1102             :         }
    1103             : 
    1104             :         /*
    1105             :          * To ensure that the bad password count is updated atomically,
    1106             :          * we need to:
    1107             :          *    begin a transaction
    1108             :          *       re-read the account details,
    1109             :          *         using the <GUID= part of the DN
    1110             :          *       update the bad password count
    1111             :          *    commit the transaction.
    1112             :          */
    1113             : 
    1114             :         /*
    1115             :          * Start a new transaction
    1116             :          */
    1117        3503 :         ret = ldb_transaction_start(sam_ctx);
    1118        3503 :         if (ret != LDB_SUCCESS) {
    1119           0 :                 status = NT_STATUS_INTERNAL_ERROR;
    1120           0 :                 goto error;
    1121             :         }
    1122        3503 :         txn_active = true;
    1123             : 
    1124             :         /*
    1125             :          * Re-read the account details, using the GUID in case the DN
    1126             :          * is being changed.
    1127             :          */
    1128        3503 :         status = authsam_reread_user_logon_data(
    1129             :                 sam_ctx, mem_ctx, msg, &current);
    1130        3503 :         if (!NT_STATUS_IS_OK(status)) {
    1131             :                 /* The re-read can return account locked out, as well
    1132             :                  * as an internal error
    1133             :                  */
    1134           0 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
    1135             :                         /*
    1136             :                          * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
    1137             :                          * the transaction. Again to avoid cluttering the
    1138             :                          * audit logs with spurious errors
    1139             :                          */
    1140           0 :                         goto exit;
    1141             :                 }
    1142           0 :                 goto error;
    1143             :         }
    1144             : 
    1145             :         /*
    1146             :          * Update the bad password count and if required lock the account
    1147             :          */
    1148        6748 :         status = dsdb_update_bad_pwd_count(
    1149             :                 mem_ctx,
    1150             :                 sam_ctx,
    1151             :                 current,
    1152        3503 :                 domain_res->msgs[0],
    1153             :                 pso_msg,
    1154             :                 &msg_mod);
    1155        3503 :         if (!NT_STATUS_IS_OK(status)) {
    1156           0 :                 status = NT_STATUS_INTERNAL_ERROR;
    1157           0 :                 goto error;
    1158             :         }
    1159             : 
    1160             :         /*
    1161             :          * Write the data back to disk if required.
    1162             :          */
    1163        3503 :         if (msg_mod != NULL) {
    1164             :                 struct ldb_request *req;
    1165             : 
    1166         521 :                 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
    1167             :                                         msg_mod,
    1168             :                                         NULL,
    1169             :                                         NULL,
    1170             :                                         ldb_op_default_callback,
    1171             :                                         NULL);
    1172         521 :                 if (ret != LDB_SUCCESS) {
    1173           0 :                         TALLOC_FREE(msg_mod);
    1174           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1175           0 :                         goto error;
    1176             :                 }
    1177             : 
    1178         521 :                 ret = ldb_request_add_control(req,
    1179             :                                               DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
    1180             :                                               false, NULL);
    1181         521 :                 if (ret != LDB_SUCCESS) {
    1182           0 :                         talloc_free(req);
    1183           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1184           0 :                         goto error;
    1185             :                 }
    1186             : 
    1187             :                 /*
    1188             :                  * As we're in a transaction, make the ldb request directly
    1189             :                  * to avoid the nested transaction that would result if we
    1190             :                  * called dsdb_autotransaction_request
    1191             :                  */
    1192         521 :                 ret = ldb_request(sam_ctx, req);
    1193         521 :                 if (ret == LDB_SUCCESS) {
    1194         521 :                         ret = ldb_wait(req->handle, LDB_WAIT_ALL);
    1195             :                 }
    1196         521 :                 talloc_free(req);
    1197         521 :                 if (ret != LDB_SUCCESS) {
    1198           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1199           0 :                         goto error;
    1200             :                 }
    1201         521 :                 status = authsam_set_bad_password_indicator(
    1202             :                         sam_ctx, mem_ctx, msg);
    1203         521 :                 if (!NT_STATUS_IS_OK(status)) {
    1204           0 :                         goto error;
    1205             :                 }
    1206             :         }
    1207             :         /*
    1208             :          * Note that we may not have updated the user record, but
    1209             :          * committing the transaction in that case is still the correct
    1210             :          * thing to do.
    1211             :          * If the transaction was cancelled, this would be logged by
    1212             :          * the dsdb audit log as a failure. When in fact it is expected
    1213             :          * behaviour.
    1214             :          */
    1215        2982 : exit:
    1216        3503 :         TALLOC_FREE(mem_ctx);
    1217        3503 :         ret = ldb_transaction_commit(sam_ctx);
    1218        3503 :         if (ret != LDB_SUCCESS) {
    1219           0 :                 DBG_ERR("Error (%d) %s, committing transaction,"
    1220             :                         " while updating bad password count"
    1221             :                         " for (%s)\n",
    1222             :                         ret,
    1223             :                         ldb_errstring(sam_ctx),
    1224             :                         ldb_dn_get_linearized(msg->dn));
    1225           0 :                 return NT_STATUS_INTERNAL_ERROR;
    1226             :         }
    1227        3503 :         return status;
    1228             : 
    1229           0 : error:
    1230           0 :         DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
    1231             :                 "set lockoutTime on %s: %s\n",
    1232             :                 ldb_dn_get_linearized(msg->dn),
    1233             :                 ldb_errstring(sam_ctx) != NULL ?
    1234             :                         ldb_errstring(sam_ctx) :nt_errstr(status));
    1235           0 :         if (txn_active) {
    1236           0 :                 ret = ldb_transaction_cancel(sam_ctx);
    1237           0 :                 if (ret != LDB_SUCCESS) {
    1238           0 :                         DBG_ERR("Error rolling back transaction,"
    1239             :                                 " while updating bad password count"
    1240             :                                 " on %s: %s\n",
    1241             :                                 ldb_dn_get_linearized(msg->dn),
    1242             :                                 ldb_errstring(sam_ctx));
    1243             :                 }
    1244             :         }
    1245           0 :         TALLOC_FREE(mem_ctx);
    1246           0 :         return status;
    1247             : 
    1248             : }
    1249             : 
    1250             : /*
    1251             :  * msDS-LogonTimeSyncInterval is an int32_t number of days.
    1252             :  *
    1253             :  * The docs say: "the initial update, after the domain functional
    1254             :  * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
    1255             :  * 14 days minus a random percentage of 5 days", but we aren't doing
    1256             :  * that. The blogosphere seems to think that this randomised update
    1257             :  * happens everytime, but [MS-ADA1] doesn't agree.
    1258             :  *
    1259             :  * Dochelp referred us to the following blog post:
    1260             :  * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
    1261             :  *
    1262             :  * when msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
    1263             :  * not changed.
    1264             :  */
    1265             : 
    1266       33571 : static NTSTATUS authsam_calculate_lastlogon_sync_interval(
    1267             :         struct ldb_context *sam_ctx,
    1268             :         TALLOC_CTX *ctx,
    1269             :         struct ldb_dn *domain_dn,
    1270             :         NTTIME *sync_interval_nt)
    1271             : {
    1272             :         static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
    1273             :                                         NULL };
    1274             :         int ret;
    1275       33571 :         struct ldb_result *domain_res = NULL;
    1276       33571 :         TALLOC_CTX *mem_ctx = NULL;
    1277             :         uint32_t sync_interval;
    1278             : 
    1279       33571 :         mem_ctx = talloc_new(ctx);
    1280       33571 :         if (mem_ctx == NULL) {
    1281           0 :                 return NT_STATUS_NO_MEMORY;
    1282             :         }
    1283             : 
    1284       33571 :         ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
    1285             :                              0);
    1286       33571 :         if (ret != LDB_SUCCESS || domain_res->count != 1) {
    1287           0 :                 TALLOC_FREE(mem_ctx);
    1288           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
    1289             :         }
    1290             : 
    1291       33571 :         sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
    1292             :                                                  "msDS-LogonTimeSyncInterval",
    1293             :                                                  14);
    1294       33571 :         DEBUG(5, ("sync interval is %d\n", sync_interval));
    1295       33571 :         if (sync_interval >= 5){
    1296             :                 /*
    1297             :                  * Subtract "a random percentage of 5" days. Presumably this
    1298             :                  * percentage is between 0 and 100, and modulus is accurate
    1299             :                  * enough.
    1300             :                  */
    1301       33571 :                 uint32_t r = generate_random() % 6;
    1302       33571 :                 sync_interval -= r;
    1303       33571 :                 DBG_INFO("randomised sync interval is %d (-%d)\n", sync_interval, r);
    1304             :         }
    1305             :         /* In the case where sync_interval < 5 there is no randomisation */
    1306             : 
    1307             :         /*
    1308             :          * msDS-LogonTimeSyncInterval is an int32_t number of days,
    1309             :          * while lastLogonTimestamp (to be updated) is in the 64 bit
    1310             :          * 100ns NTTIME format so we must convert.
    1311             :          */
    1312       33571 :         *sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
    1313       33571 :         TALLOC_FREE(mem_ctx);
    1314       33571 :         return NT_STATUS_OK;
    1315             : }
    1316             : 
    1317             : 
    1318             : /*
    1319             :  * We only set lastLogonTimestamp if the current value is older than
    1320             :  * now - msDS-LogonTimeSyncInterval days.
    1321             :  *
    1322             :  * lastLogonTimestamp is in the 64 bit 100ns NTTIME format
    1323             :  */
    1324       56969 : static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
    1325             :                                                    struct ldb_message *msg_mod,
    1326             :                                                    struct ldb_dn *domain_dn,
    1327             :                                                    NTTIME old_timestamp,
    1328             :                                                    NTTIME now,
    1329             :                                                    NTTIME sync_interval_nt)
    1330             : {
    1331             :         int ret;
    1332       56969 :         DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
    1333             :                   (long long int)old_timestamp,
    1334             :                   (long long int)(now - sync_interval_nt),
    1335             :                   (long long int)(old_timestamp - now + sync_interval_nt)));
    1336             : 
    1337       56969 :         if (sync_interval_nt == 0){
    1338             :                 /*
    1339             :                  * Setting msDS-LogonTimeSyncInterval to zero is how you ask
    1340             :                  * that nothing happens here.
    1341             :                  */
    1342           0 :                 return NT_STATUS_OK;
    1343             :         }
    1344       56969 :         if (old_timestamp > now){
    1345           0 :                 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
    1346             :                           (long long int)old_timestamp, (long long int)now));
    1347             :                 /* then what? */
    1348             : 
    1349       56969 :         } else if (old_timestamp < now - sync_interval_nt){
    1350       12059 :                 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
    1351             :                           (long long int)now));
    1352             : 
    1353             :                 /* The time has come to update lastLogonTimestamp */
    1354       12059 :                 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
    1355             :                                           "lastLogonTimestamp", now);
    1356             : 
    1357       12059 :                 if (ret != LDB_SUCCESS) {
    1358           0 :                         return NT_STATUS_NO_MEMORY;
    1359             :                 }
    1360             :         }
    1361       56969 :         return NT_STATUS_OK;
    1362             : }
    1363             : 
    1364             : /****************************************************************************
    1365             :  Look for the specified user in the sam, return ldb result structures
    1366             : ****************************************************************************/
    1367             : 
    1368       21296 : NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
    1369             :                                          const char *account_name,
    1370             :                                          struct ldb_dn *domain_dn,
    1371             :                                          struct ldb_message **ret_msg)
    1372             : {
    1373             :         int ret;
    1374             : 
    1375             :         /* pull the user attributes */
    1376       21296 :         ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
    1377             :                               user_attrs,
    1378             :                               DSDB_SEARCH_SHOW_EXTENDED_DN,
    1379             :                               "(&(sAMAccountName=%s)(objectclass=user))",
    1380             :                               ldb_binary_encode_string(mem_ctx, account_name));
    1381       21296 :         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
    1382         434 :                 DEBUG(3,("sam_search_user: Couldn't find user [%s] in samdb, under %s\n",
    1383             :                          account_name, ldb_dn_get_linearized(domain_dn)));
    1384         434 :                 return NT_STATUS_NO_SUCH_USER;
    1385             :         }
    1386       20862 :         if (ret != LDB_SUCCESS) {
    1387           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
    1388             :         }
    1389             : 
    1390       20862 :         return NT_STATUS_OK;
    1391             : }
    1392             : 
    1393             : 
    1394             : /* Reset the badPwdCount to zero and update the lastLogon time. */
    1395       34195 : NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
    1396             :                                           const struct ldb_message *msg,
    1397             :                                           struct ldb_dn *domain_dn,
    1398             :                                           bool interactive_or_kerberos,
    1399             :                                           TALLOC_CTX *send_to_sam_mem_ctx,
    1400             :                                           struct netr_SendToSamBase **send_to_sam)
    1401             : {
    1402             :         int ret;
    1403             :         NTSTATUS status;
    1404             :         int badPwdCount;
    1405             :         int dbBadPwdCount;
    1406             :         int64_t lockoutTime;
    1407             :         struct ldb_message *msg_mod;
    1408             :         TALLOC_CTX *mem_ctx;
    1409             :         struct timeval tv_now;
    1410             :         NTTIME now;
    1411             :         NTTIME lastLogonTimestamp;
    1412             :         int64_t lockOutObservationWindow;
    1413       34195 :         NTTIME sync_interval_nt = 0;
    1414       34195 :         bool am_rodc = false;
    1415       34195 :         bool txn_active = false;
    1416             :         bool need_db_reread;
    1417             : 
    1418       34195 :         mem_ctx = talloc_new(msg);
    1419       34195 :         if (mem_ctx == NULL) {
    1420           0 :                 return NT_STATUS_NO_MEMORY;
    1421             :         }
    1422             : 
    1423             :         /*
    1424             :          * Any update of the last logon data, needs to be done inside a
    1425             :          * transaction.
    1426             :          * And the user data needs to be re-read, and the account re-checked
    1427             :          * for lockout.
    1428             :          *
    1429             :          * Howevver we have long-running transactions like replication
    1430             :          * that could otherwise grind the system to a halt so we first
    1431             :          * determine if *this* account has seen a bad password,
    1432             :          * otherwise we only start a transaction if there was a need
    1433             :          * (because a change was to be made).
    1434             :          */
    1435             : 
    1436       34195 :         status = authsam_check_bad_password_indicator(
    1437             :                 sam_ctx, mem_ctx, &need_db_reread, msg);
    1438       34195 :         if (!NT_STATUS_IS_OK(status)) {
    1439           0 :                 return status;
    1440             :         }
    1441             : 
    1442       34195 :         if (interactive_or_kerberos == false) {
    1443             :                 /*
    1444             :                  * Avoid calculating this twice, it reads the PSO.  A
    1445             :                  * race on this is unimportant.
    1446             :                  */
    1447             :                 lockOutObservationWindow
    1448       15528 :                         = samdb_result_msds_LockoutObservationWindow(
    1449             :                                 sam_ctx, mem_ctx, domain_dn, msg);
    1450             :         }
    1451             : 
    1452       34195 :         ret = samdb_rodc(sam_ctx, &am_rodc);
    1453       34195 :         if (ret != LDB_SUCCESS) {
    1454           0 :                 status = NT_STATUS_INTERNAL_ERROR;
    1455           0 :                 goto error;
    1456             :         }
    1457             : 
    1458       34195 :         if (!am_rodc) {
    1459             :                 /*
    1460             :                  * Avoid reading the main domain DN twice.  A race on
    1461             :                  * this is unimportant.
    1462             :                  */
    1463       33571 :                 status = authsam_calculate_lastlogon_sync_interval(
    1464             :                         sam_ctx, mem_ctx, domain_dn, &sync_interval_nt);
    1465             : 
    1466       33571 :                 if (!NT_STATUS_IS_OK(status)) {
    1467           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1468           0 :                         goto error;
    1469             :                 }
    1470             :         }
    1471             : 
    1472       34195 : get_transaction:
    1473             : 
    1474       58074 :         if (need_db_reread) {
    1475       24213 :                 struct ldb_message *current = NULL;
    1476             : 
    1477             :                 /*
    1478             :                  * Start a new transaction
    1479             :                  */
    1480       24213 :                 ret = ldb_transaction_start(sam_ctx);
    1481       24213 :                 if (ret != LDB_SUCCESS) {
    1482           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1483           0 :                         goto error;
    1484             :                 }
    1485             : 
    1486       24213 :                 txn_active = true;
    1487             : 
    1488             :                 /*
    1489             :                  * Re-read the account details, using the GUID
    1490             :                  * embedded in DN so this is safe against a race where
    1491             :                  * it is being renamed.
    1492             :                  */
    1493       24213 :                 status = authsam_reread_user_logon_data(
    1494             :                         sam_ctx, mem_ctx, msg, &current);
    1495       24213 :                 if (!NT_STATUS_IS_OK(status)) {
    1496             :                         /*
    1497             :                          * The re-read can return account locked out, as well
    1498             :                          * as an internal error
    1499             :                          */
    1500           0 :                         if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
    1501             :                                 /*
    1502             :                                  * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
    1503             :                                  * the transaction. Again to avoid cluttering the
    1504             :                                  * audit logs with spurious errors
    1505             :                                  */
    1506           0 :                                 goto exit;
    1507             :                         }
    1508           0 :                         goto error;
    1509             :                 }
    1510       24213 :                 msg = current;
    1511             :         }
    1512             : 
    1513       58074 :         lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
    1514       58074 :         dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
    1515       58074 :         tv_now = timeval_current();
    1516       58074 :         now = timeval_to_nttime(&tv_now);
    1517             : 
    1518       58074 :         if (interactive_or_kerberos) {
    1519       37201 :                 badPwdCount = dbBadPwdCount;
    1520             :         } else {
    1521             :                 /*
    1522             :                  * We get lockOutObservationWindow above, before the
    1523             :                  * transaction
    1524             :                  */
    1525       20873 :                 badPwdCount = dsdb_effective_badPwdCount(
    1526             :                         msg, lockOutObservationWindow, now);
    1527             :         }
    1528       58074 :         lastLogonTimestamp =
    1529       58074 :                 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
    1530             : 
    1531       58074 :         DEBUG(5, ("lastLogonTimestamp is %lld\n",
    1532             :                   (long long int)lastLogonTimestamp));
    1533             : 
    1534       58074 :         msg_mod = ldb_msg_new(mem_ctx);
    1535       58074 :         if (msg_mod == NULL) {
    1536           0 :                 status = NT_STATUS_NO_MEMORY;
    1537           0 :                 goto error;
    1538             :         }
    1539             : 
    1540             :         /*
    1541             :          * By using the DN from msg->dn directly, we allow LDB to
    1542             :          * prefer the embedded GUID form, so this is actually quite
    1543             :          * safe even in the case where DN has been changed
    1544             :          */
    1545       58074 :         msg_mod->dn = msg->dn;
    1546             : 
    1547       58074 :         if (lockoutTime != 0) {
    1548             :                 /*
    1549             :                  * This implies "badPwdCount" = 0, see samldb_lockout_time()
    1550             :                  */
    1551          36 :                 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
    1552          36 :                 if (ret != LDB_SUCCESS) {
    1553           0 :                         status = NT_STATUS_NO_MEMORY;
    1554           0 :                         goto error;
    1555             :                 }
    1556       58038 :         } else if (badPwdCount != 0) {
    1557         304 :                 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
    1558         304 :                 if (ret != LDB_SUCCESS) {
    1559           0 :                         status = NT_STATUS_NO_MEMORY;
    1560           0 :                         goto error;
    1561             :                 }
    1562             :         }
    1563             : 
    1564       58074 :         if (interactive_or_kerberos ||
    1565         185 :             (badPwdCount != 0 && lockoutTime == 0)) {
    1566       37386 :                 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
    1567             :                                           "lastLogon", now);
    1568       37386 :                 if (ret != LDB_SUCCESS) {
    1569           0 :                         status = NT_STATUS_NO_MEMORY;
    1570           0 :                         goto error;
    1571             :                 }
    1572             :         }
    1573             : 
    1574       58074 :         if (interactive_or_kerberos) {
    1575             :                 int logonCount;
    1576             : 
    1577       37201 :                 logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
    1578             : 
    1579       37201 :                 logonCount += 1;
    1580             : 
    1581       37201 :                 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
    1582             :                                         "logonCount", logonCount);
    1583       37201 :                 if (ret != LDB_SUCCESS) {
    1584           0 :                         status = NT_STATUS_NO_MEMORY;
    1585           0 :                         goto error;
    1586             :                 }
    1587             :         } else {
    1588             :                 /* Set an unset logonCount to 0 on first successful login */
    1589       20873 :                 if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
    1590          30 :                         ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
    1591             :                                                 "logonCount", 0);
    1592          30 :                         if (ret != LDB_SUCCESS) {
    1593           0 :                                 TALLOC_FREE(mem_ctx);
    1594           0 :                                 return NT_STATUS_NO_MEMORY;
    1595             :                         }
    1596             :                 }
    1597             :         }
    1598             : 
    1599       58074 :         if (!am_rodc) {
    1600       56969 :                 status = authsam_update_lastlogon_timestamp(
    1601             :                         sam_ctx,
    1602             :                         msg_mod,
    1603             :                         domain_dn,
    1604             :                         lastLogonTimestamp,
    1605             :                         now,
    1606             :                         sync_interval_nt);
    1607       56969 :                 if (!NT_STATUS_IS_OK(status)) {
    1608           0 :                         status = NT_STATUS_NO_MEMORY;
    1609           0 :                         goto error;
    1610             :                 }
    1611             :         } else {
    1612             :                 /* Perform the (async) SendToSAM calls for MS-SAMS */
    1613        1105 :                 if (dbBadPwdCount != 0 && send_to_sam != NULL) {
    1614             :                         struct netr_SendToSamBase *base_msg;
    1615          17 :                         struct GUID guid = samdb_result_guid(msg, "objectGUID");
    1616             : 
    1617          17 :                         base_msg = talloc_zero(send_to_sam_mem_ctx,
    1618             :                                                struct netr_SendToSamBase);
    1619          17 :                         if (base_msg == NULL) {
    1620           0 :                                 status = NT_STATUS_NO_MEMORY;
    1621           0 :                                 goto error;
    1622             :                         }
    1623             : 
    1624          17 :                         base_msg->message_type = SendToSamResetBadPasswordCount;
    1625          17 :                         base_msg->message_size = 16;
    1626          17 :                         base_msg->message.reset_bad_password.guid = guid;
    1627          17 :                         *send_to_sam = base_msg;
    1628             :                 }
    1629             :         }
    1630             : 
    1631       58074 :         if (msg_mod->num_elements > 0) {
    1632             :                 unsigned int i;
    1633             :                 struct ldb_request *req;
    1634             : 
    1635             :                 /*
    1636             :                  * If it turns out we are going to update the DB, go
    1637             :                  * back to the start, get a transaction and the
    1638             :                  * current DB state and try again
    1639             :                  */
    1640       48092 :                 if (txn_active == false) {
    1641       23879 :                         need_db_reread = true;
    1642       23879 :                         goto get_transaction;
    1643             :                 }
    1644             : 
    1645             :                 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
    1646       68236 :                 for (i=0;i<msg_mod->num_elements;i++) {
    1647       44023 :                         msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
    1648             :                 }
    1649             : 
    1650       24213 :                 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
    1651             :                                         msg_mod,
    1652             :                                         NULL,
    1653             :                                         NULL,
    1654             :                                         ldb_op_default_callback,
    1655             :                                         NULL);
    1656       24213 :                 if (ret != LDB_SUCCESS) {
    1657           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1658           0 :                         goto error;
    1659             :                 }
    1660             : 
    1661       24213 :                 ret = ldb_request_add_control(req,
    1662             :                                               DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
    1663             :                                               false, NULL);
    1664       24213 :                 if (ret != LDB_SUCCESS) {
    1665           0 :                         TALLOC_FREE(req);
    1666           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1667           0 :                         goto error;
    1668             :                 }
    1669             :                 /*
    1670             :                  * As we're in a transaction, make the ldb request directly
    1671             :                  * to avoid the nested transaction that would result if we
    1672             :                  * called dsdb_autotransaction_request
    1673             :                  */
    1674       24213 :                 ret = ldb_request(sam_ctx, req);
    1675       24213 :                 if (ret == LDB_SUCCESS) {
    1676       24213 :                         ret = ldb_wait(req->handle, LDB_WAIT_ALL);
    1677             :                 }
    1678       24213 :                 TALLOC_FREE(req);
    1679       24213 :                 if (ret != LDB_SUCCESS) {
    1680           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1681           0 :                         goto error;
    1682             :                 }
    1683             :         }
    1684       34195 :         status = authsam_clear_bad_password_indicator(sam_ctx, mem_ctx, msg);
    1685       34195 :         if (!NT_STATUS_IS_OK(status)) {
    1686           0 :                 goto error;
    1687             :         }
    1688             : 
    1689             :         /*
    1690             :          * Note that we may not have updated the user record, but
    1691             :          * committing the transaction in that case is still the correct
    1692             :          * thing to do.
    1693             :          * If the transaction was cancelled, this would be logged by
    1694             :          * the dsdb audit log as a failure. When in fact it is expected
    1695             :          * behaviour.
    1696             :          *
    1697             :          * Thankfully both TDB and LMDB seem to optimise for the empty
    1698             :          * transaction case
    1699             :          */
    1700       34195 : exit:
    1701       34195 :         TALLOC_FREE(mem_ctx);
    1702             : 
    1703       34195 :         if (txn_active == false) {
    1704        9982 :                 return status;
    1705             :         }
    1706             : 
    1707       24213 :         ret = ldb_transaction_commit(sam_ctx);
    1708       24213 :         if (ret != LDB_SUCCESS) {
    1709           0 :                 DBG_ERR("Error (%d) %s, committing transaction,"
    1710             :                         " while updating successful logon accounting"
    1711             :                         " for (%s)\n",
    1712             :                         ret,
    1713             :                         ldb_errstring(sam_ctx),
    1714             :                         ldb_dn_get_linearized(msg->dn));
    1715           0 :                 return NT_STATUS_INTERNAL_ERROR;
    1716             :         }
    1717       24213 :         return status;
    1718             : 
    1719           0 : error:
    1720           0 :         DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
    1721             :                 "set lockoutTime on %s: %s\n",
    1722             :                 ldb_dn_get_linearized(msg->dn),
    1723             :                 ldb_errstring(sam_ctx) != NULL ?
    1724             :                         ldb_errstring(sam_ctx) :nt_errstr(status));
    1725           0 :         if (txn_active) {
    1726           0 :                 ret = ldb_transaction_cancel(sam_ctx);
    1727           0 :                 if (ret != LDB_SUCCESS) {
    1728           0 :                         DBG_ERR("Error rolling back transaction,"
    1729             :                                 " while updating bad password count"
    1730             :                                 " on %s: %s\n",
    1731             :                                 ldb_dn_get_linearized(msg->dn),
    1732             :                                 ldb_errstring(sam_ctx));
    1733             :                 }
    1734             :         }
    1735           0 :         TALLOC_FREE(mem_ctx);
    1736           0 :         return status;
    1737             : }

Generated by: LCOV version 1.13