LCOV - code coverage report
Current view: top level - source4/auth - sam.c (source / functions) Hit Total Coverage
Test: coverage report for recycleplus df22b230 Lines: 443 661 67.0 %
Date: 2024-02-14 10:14:15 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       36439 : 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       36439 :         hours = ldb_msg_find_ldb_val(msg, "logonHours");
     138       36439 :         if (!hours) {
     139       36439 :                 DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n", name_for_logs));
     140       36439 :                 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       37059 : _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       37059 :         struct timeval tv_now = timeval_current();
     203       37059 :         NTTIME now = timeval_to_nttime(&tv_now);
     204             : 
     205       37059 :         DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
     206             : 
     207       37059 :         acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
     208             : 
     209       37059 :         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       37059 :         must_change_time = samdb_result_nttime(msg,
     214             :                         "msDS-UserPasswordExpiryTimeComputed", 0);
     215             : 
     216       37059 :         workstation_list = ldb_msg_find_attr_as_string(msg, "userWorkstations", NULL);
     217             : 
     218             :         /* Quit if the account was disabled. */
     219       37059 :         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       36793 :         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       36793 :         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       36793 :         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       36782 :         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       36782 :         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       36439 :         if (!logon_hours_ok(msg, name_for_logs)) {
     278           0 :                 return NT_STATUS_INVALID_LOGON_HOURS;
     279             :         }
     280             : 
     281       36439 :         if (!allow_domain_trust) {
     282       18014 :                 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       36439 :         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       36439 :         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       36097 :         return NT_STATUS_OK;
     303             : }
     304             : 
     305       90635 : static NTSTATUS authsam_domain_group_filter(TALLOC_CTX *mem_ctx,
     306             :                                             char **_filter)
     307             : {
     308       90635 :         char *filter = NULL;
     309             : 
     310       90635 :         *_filter = NULL;
     311             : 
     312       90635 :         filter = talloc_strdup(mem_ctx, "(&(objectClass=group)");
     313             : 
     314             :         /*
     315             :          * Skip all builtin groups, they're added later.
     316             :          */
     317       90635 :         talloc_asprintf_addbuf(&filter,
     318             :                                "(!(groupType:1.2.840.113556.1.4.803:=%u))",
     319             :                                GROUP_TYPE_BUILTIN_LOCAL_GROUP);
     320             :         /*
     321             :          * Only include security groups.
     322             :          */
     323       90635 :         talloc_asprintf_addbuf(&filter,
     324             :                                "(groupType:1.2.840.113556.1.4.803:=%u))",
     325             :                                GROUP_TYPE_SECURITY_ENABLED);
     326       90635 :         if (filter == NULL) {
     327           0 :                 return NT_STATUS_NO_MEMORY;
     328             :         }
     329             : 
     330       90635 :         *_filter = filter;
     331       90635 :         return NT_STATUS_OK;
     332             : }
     333             : 
     334       54208 : _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
     335             :                                            struct ldb_context *sam_ctx,
     336             :                                            const char *netbios_name,
     337             :                                            const char *domain_name,
     338             :                                            const char *dns_domain_name,
     339             :                                            struct ldb_dn *domain_dn,
     340             :                                            struct ldb_message *msg,
     341             :                                            DATA_BLOB user_sess_key,
     342             :                                            DATA_BLOB lm_sess_key,
     343             :                                            struct auth_user_info_dc **_user_info_dc)
     344             : {
     345             :         NTSTATUS status;
     346             :         struct auth_user_info_dc *user_info_dc;
     347             :         struct auth_user_info *info;
     348       54208 :         const char *str = NULL;
     349       54208 :         char *filter = NULL;
     350             :         /* SIDs for the account and his primary group */
     351             :         struct dom_sid *account_sid;
     352             :         struct dom_sid_buf buf;
     353             :         const char *primary_group_dn;
     354             :         DATA_BLOB primary_group_blob;
     355             :         /* SID structures for the expanded group memberships */
     356       54208 :         struct dom_sid *sids = NULL;
     357       54208 :         unsigned int num_sids = 0, i;
     358             :         struct dom_sid *domain_sid;
     359             :         TALLOC_CTX *tmp_ctx;
     360             :         struct ldb_message_element *el;
     361             : 
     362       54208 :         user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
     363       54208 :         NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
     364             : 
     365       54208 :         tmp_ctx = talloc_new(user_info_dc);
     366       54208 :         if (tmp_ctx == NULL) {
     367           0 :                 TALLOC_FREE(user_info_dc);
     368           0 :                 return NT_STATUS_NO_MEMORY;
     369             :         }
     370             : 
     371       54208 :         sids = talloc_array(user_info_dc, struct dom_sid, 2);
     372       54208 :         if (sids == NULL) {
     373           0 :                 TALLOC_FREE(user_info_dc);
     374           0 :                 return NT_STATUS_NO_MEMORY;
     375             :         }
     376             : 
     377       54208 :         num_sids = 2;
     378             : 
     379       54208 :         account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
     380       54208 :         if (account_sid == NULL) {
     381           0 :                 TALLOC_FREE(user_info_dc);
     382           0 :                 return NT_STATUS_NO_MEMORY;
     383             :         }
     384             : 
     385       54208 :         status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
     386       54208 :         if (!NT_STATUS_IS_OK(status)) {
     387           0 :                 talloc_free(user_info_dc);
     388           0 :                 return status;
     389             :         }
     390             : 
     391       54208 :         sids[PRIMARY_USER_SID_INDEX] = *account_sid;
     392       54208 :         sids[PRIMARY_GROUP_SID_INDEX] = *domain_sid;
     393       54208 :         sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX], ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
     394             : 
     395             :         /*
     396             :          * Filter out builtin groups from this token. We will search
     397             :          * for builtin groups later, and not include them in the PAC
     398             :          * or SamLogon validation info.
     399             :          */
     400       54208 :         status = authsam_domain_group_filter(tmp_ctx, &filter);
     401       54208 :         if (!NT_STATUS_IS_OK(status)) {
     402           0 :                 TALLOC_FREE(user_info_dc);
     403           0 :                 return status;
     404             :         }
     405             : 
     406       54208 :         primary_group_dn = talloc_asprintf(
     407             :                 tmp_ctx,
     408             :                 "<SID=%s>",
     409       54208 :                 dom_sid_str_buf(&sids[PRIMARY_GROUP_SID_INDEX], &buf));
     410       54208 :         if (primary_group_dn == NULL) {
     411           0 :                 TALLOC_FREE(user_info_dc);
     412           0 :                 return NT_STATUS_NO_MEMORY;
     413             :         }
     414             : 
     415       54208 :         primary_group_blob = data_blob_string_const(primary_group_dn);
     416             : 
     417             :         /* Expands the primary group - this function takes in
     418             :          * memberOf-like values, so we fake one up with the
     419             :          * <SID=S-...> format of DN and then let it expand
     420             :          * them, as long as they meet the filter - so only
     421             :          * domain groups, not builtin groups
     422             :          *
     423             :          * The primary group is still treated specially, so we set the
     424             :          * 'only childs' flag to true
     425             :          */
     426       54208 :         status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
     427             :                                            user_info_dc, &sids, &num_sids);
     428       54208 :         if (!NT_STATUS_IS_OK(status)) {
     429           0 :                 talloc_free(user_info_dc);
     430           0 :                 return status;
     431             :         }
     432             : 
     433             :         /* Expands the additional groups */
     434       54208 :         el = ldb_msg_find_element(msg, "memberOf");
     435      213684 :         for (i = 0; el && i < el->num_values; i++) {
     436             :                 /* This function takes in memberOf values and expands
     437             :                  * them, as long as they meet the filter - so only
     438             :                  * domain groups, not builtin groups */
     439      159476 :                 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
     440             :                                                    user_info_dc, &sids, &num_sids);
     441      159476 :                 if (!NT_STATUS_IS_OK(status)) {
     442           0 :                         talloc_free(user_info_dc);
     443           0 :                         return status;
     444             :                 }
     445             :         }
     446             : 
     447       54208 :         user_info_dc->sids = sids;
     448       54208 :         user_info_dc->num_sids = num_sids;
     449             : 
     450       54208 :         user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
     451       54208 :         NT_STATUS_HAVE_NO_MEMORY(user_info_dc->info);
     452             : 
     453       54208 :         str = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
     454       54208 :         info->account_name = talloc_strdup(info, str);
     455       54208 :         if (info->account_name == NULL) {
     456           0 :                 TALLOC_FREE(user_info_dc);
     457           0 :                 return NT_STATUS_NO_MEMORY;
     458             :         }
     459             : 
     460       54208 :         str = ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL);
     461       54208 :         if (str == NULL && dns_domain_name != NULL) {
     462       43935 :                 info->user_principal_name = talloc_asprintf(info, "%s@%s",
     463             :                                         info->account_name,
     464             :                                         dns_domain_name);
     465       43935 :                 if (info->user_principal_name == NULL) {
     466           0 :                         TALLOC_FREE(user_info_dc);
     467           0 :                         return NT_STATUS_NO_MEMORY;
     468             :                 }
     469       43935 :                 info->user_principal_constructed = true;
     470       10273 :         } else if (str != NULL) {
     471       10175 :                 info->user_principal_name = talloc_strdup(info, str);
     472       10175 :                 if (info->user_principal_name == NULL) {
     473           0 :                         TALLOC_FREE(user_info_dc);
     474           0 :                         return NT_STATUS_NO_MEMORY;
     475             :                 }
     476             :         }
     477             : 
     478       54208 :         info->domain_name = talloc_strdup(info, domain_name);
     479       54208 :         if (info->domain_name == NULL) {
     480           0 :                 TALLOC_FREE(user_info_dc);
     481           0 :                 return NT_STATUS_NO_MEMORY;
     482             :         }
     483             : 
     484       54208 :         if (dns_domain_name != NULL) {
     485       54110 :                 info->dns_domain_name = talloc_strdup(info, dns_domain_name);
     486       54110 :                 if (info->dns_domain_name == NULL) {
     487           0 :                         TALLOC_FREE(user_info_dc);
     488           0 :                         return NT_STATUS_NO_MEMORY;
     489             :                 }
     490             :         }
     491             : 
     492       54208 :         str = ldb_msg_find_attr_as_string(msg, "displayName", "");
     493       54208 :         info->full_name = talloc_strdup(info, str);
     494       54208 :         if (info->full_name == NULL) {
     495           0 :                 TALLOC_FREE(user_info_dc);
     496           0 :                 return NT_STATUS_NO_MEMORY;
     497             :         }
     498             : 
     499       54208 :         str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
     500       54208 :         info->logon_script = talloc_strdup(info, str);
     501       54208 :         if (info->logon_script == NULL) {
     502           0 :                 TALLOC_FREE(user_info_dc);
     503           0 :                 return NT_STATUS_NO_MEMORY;
     504             :         }
     505             : 
     506       54208 :         str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
     507       54208 :         info->profile_path = talloc_strdup(info, str);
     508       54208 :         if (info->profile_path == NULL) {
     509           0 :                 TALLOC_FREE(user_info_dc);
     510           0 :                 return NT_STATUS_NO_MEMORY;
     511             :         }
     512             : 
     513       54208 :         str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
     514       54208 :         info->home_directory = talloc_strdup(info, str);
     515       54208 :         if (info->home_directory == NULL) {
     516           0 :                 TALLOC_FREE(user_info_dc);
     517           0 :                 return NT_STATUS_NO_MEMORY;
     518             :         }
     519             : 
     520       54208 :         str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
     521       54208 :         info->home_drive = talloc_strdup(info, str);
     522       54208 :         if (info->home_drive == NULL) {
     523           0 :                 TALLOC_FREE(user_info_dc);
     524           0 :                 return NT_STATUS_NO_MEMORY;
     525             :         }
     526             : 
     527       54208 :         info->logon_server = talloc_strdup(info, netbios_name);
     528       54208 :         if (info->logon_server == NULL) {
     529           0 :                 TALLOC_FREE(user_info_dc);
     530           0 :                 return NT_STATUS_NO_MEMORY;
     531             :         }
     532             : 
     533       54208 :         info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
     534       54208 :         info->last_logoff = samdb_result_last_logoff(msg);
     535       54208 :         info->acct_expiry = samdb_result_account_expires(msg);
     536       54208 :         info->last_password_change = samdb_result_nttime(msg,
     537             :                 "pwdLastSet", 0);
     538             :         info->allow_password_change
     539       54208 :                 = samdb_result_allow_password_change(sam_ctx, mem_ctx,
     540             :                         domain_dn, msg, "pwdLastSet");
     541       54208 :         info->force_password_change = samdb_result_nttime(msg,
     542             :                 "msDS-UserPasswordExpiryTimeComputed", 0);
     543       54208 :         info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
     544       54208 :         info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
     545             :                 0);
     546             : 
     547       54208 :         info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
     548             : 
     549       54208 :         user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
     550             :                                                          user_sess_key.data,
     551             :                                                          user_sess_key.length);
     552       54208 :         if (user_sess_key.data) {
     553           0 :                 if (user_info_dc->user_session_key.data == NULL) {
     554           0 :                         TALLOC_FREE(user_info_dc);
     555           0 :                         return NT_STATUS_NO_MEMORY;
     556             :                 }
     557             :         }
     558       54208 :         user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
     559             :                                                        lm_sess_key.data,
     560             :                                                        lm_sess_key.length);
     561       54208 :         if (lm_sess_key.data) {
     562           0 :                 if (user_info_dc->lm_session_key.data == NULL) {
     563           0 :                         TALLOC_FREE(user_info_dc);
     564           0 :                         return NT_STATUS_NO_MEMORY;
     565             :                 }
     566             :         }
     567             : 
     568       54208 :         if (info->acct_flags & ACB_SVRTRUST) {
     569             :                 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
     570             :                    PAC */
     571        2544 :                 user_info_dc->sids = talloc_realloc(user_info_dc,
     572             :                                                    user_info_dc->sids,
     573             :                                                    struct dom_sid,
     574             :                                                    user_info_dc->num_sids+1);
     575        2544 :                 if (user_info_dc->sids == NULL) {
     576           0 :                         TALLOC_FREE(user_info_dc);
     577           0 :                         return NT_STATUS_NO_MEMORY;
     578             :                 }
     579        2544 :                 user_info_dc->sids[user_info_dc->num_sids] = global_sid_Enterprise_DCs;
     580        2544 :                 user_info_dc->num_sids++;
     581             :         }
     582             : 
     583       54208 :         if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
     584             :             (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
     585             :                 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
     586         664 :                 user_info_dc->sids = talloc_realloc(user_info_dc,
     587             :                                                    user_info_dc->sids,
     588             :                                                    struct dom_sid,
     589             :                                                    user_info_dc->num_sids+1);
     590         664 :                 if (user_info_dc->sids == NULL) {
     591           0 :                         TALLOC_FREE(user_info_dc);
     592           0 :                         return NT_STATUS_NO_MEMORY;
     593             :                 }
     594         664 :                 user_info_dc->sids[user_info_dc->num_sids] = *domain_sid;
     595         664 :                 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids],
     596             :                             DOMAIN_RID_ENTERPRISE_READONLY_DCS);
     597         664 :                 user_info_dc->num_sids++;
     598             :         }
     599             : 
     600       54208 :         info->authenticated = true;
     601             : 
     602       54208 :         talloc_free(tmp_ctx);
     603       54208 :         *_user_info_dc = user_info_dc;
     604             : 
     605       54208 :         return NT_STATUS_OK;
     606             : }
     607             : 
     608       36427 : _PUBLIC_ NTSTATUS authsam_update_user_info_dc(TALLOC_CTX *mem_ctx,
     609             :                         struct ldb_context *sam_ctx,
     610             :                         struct auth_user_info_dc *user_info_dc)
     611             : {
     612       36427 :         char *filter = NULL;
     613             :         NTSTATUS status;
     614             :         uint32_t i;
     615       36427 :         uint32_t n = 0;
     616             : 
     617             :         /*
     618             :          * This function exists to expand group memberships
     619             :          * in the local domain (forest), as the token
     620             :          * may come from a different domain.
     621             :          */
     622             : 
     623             :         /*
     624             :          * Filter out builtin groups from this token. We will search
     625             :          * for builtin groups later.
     626             :          */
     627       36427 :         status = authsam_domain_group_filter(mem_ctx, &filter);
     628       36427 :         if (!NT_STATUS_IS_OK(status)) {
     629           0 :                 TALLOC_FREE(user_info_dc);
     630           0 :                 return status;
     631             :         }
     632             : 
     633             :         /*
     634             :          * We loop only over the existing number of
     635             :          * sids.
     636             :          */
     637       36427 :         n = user_info_dc->num_sids;
     638      284087 :         for (i = 0; i < n; i++) {
     639      247660 :                 struct dom_sid *sid = &user_info_dc->sids[i];
     640             :                 struct dom_sid_buf sid_buf;
     641             :                 char dn_str[sizeof(sid_buf.buf)*2];
     642      247660 :                 DATA_BLOB dn_blob = data_blob_null;
     643             : 
     644      247660 :                 snprintf(dn_str,
     645             :                         sizeof(dn_str),
     646             :                         "<SID=%s>",
     647             :                         dom_sid_str_buf(sid, &sid_buf));
     648      247660 :                 dn_blob = data_blob_string_const(dn_str);
     649             : 
     650             :                 /*
     651             :                  * We already have the SID in the token, so set
     652             :                  * 'only childs' flag to true and add all
     653             :                  * groups which match the filter.
     654             :                  */
     655      247660 :                 status = dsdb_expand_nested_groups(sam_ctx, &dn_blob,
     656             :                                                    true, filter,
     657             :                                                    user_info_dc,
     658             :                                                    &user_info_dc->sids,
     659      247660 :                                                    &user_info_dc->num_sids);
     660      247660 :                 if (!NT_STATUS_IS_OK(status)) {
     661           0 :                         return status;
     662             :                 }
     663             :         }
     664             : 
     665       36427 :         return NT_STATUS_OK;
     666             : }
     667             : 
     668       71887 : NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
     669             :                                    TALLOC_CTX *mem_ctx, const char *principal,
     670             :                                    const char **attrs,
     671             :                                    struct ldb_dn **domain_dn,
     672             :                                    struct ldb_message **msg)
     673             : {
     674             :         struct ldb_dn *user_dn;
     675             :         NTSTATUS nt_status;
     676       71887 :         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
     677             :         int ret;
     678             : 
     679       71887 :         if (!tmp_ctx) {
     680           0 :                 return NT_STATUS_NO_MEMORY;
     681             :         }
     682             : 
     683       71887 :         nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
     684             :                                               &user_dn, domain_dn);
     685       71887 :         if (!NT_STATUS_IS_OK(nt_status)) {
     686        2426 :                 talloc_free(tmp_ctx);
     687        2426 :                 return nt_status;
     688             :         }
     689             : 
     690             :         /* pull the user attributes */
     691       69461 :         ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
     692             :                               LDB_SCOPE_BASE, attrs,
     693             :                               DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
     694             :                               "(objectClass=*)");
     695       69461 :         if (ret != LDB_SUCCESS) {
     696           0 :                 talloc_free(tmp_ctx);
     697           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
     698             :         }
     699       69461 :         talloc_steal(mem_ctx, *msg);
     700       69461 :         talloc_steal(mem_ctx, *domain_dn);
     701       69461 :         talloc_free(tmp_ctx);
     702             : 
     703       69461 :         return NT_STATUS_OK;
     704             : }
     705             : 
     706             : /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
     707             : 
     708             :  Supply either a principal or a DN
     709             : */
     710         159 : NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
     711             :                                            struct loadparm_context *lp_ctx,
     712             :                                            struct ldb_context *sam_ctx,
     713             :                                            const char *principal,
     714             :                                            struct ldb_dn *user_dn,
     715             :                                            struct auth_user_info_dc **user_info_dc)
     716             : {
     717             :         NTSTATUS nt_status;
     718         159 :         DATA_BLOB user_sess_key = data_blob(NULL, 0);
     719         159 :         DATA_BLOB lm_sess_key = data_blob(NULL, 0);
     720             : 
     721             :         struct ldb_message *msg;
     722             :         struct ldb_dn *domain_dn;
     723             : 
     724         159 :         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
     725         159 :         if (!tmp_ctx) {
     726           0 :                 return NT_STATUS_NO_MEMORY;
     727             :         }
     728             : 
     729         159 :         if (principal) {
     730           0 :                 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
     731             :                                                       user_attrs, &domain_dn, &msg);
     732           0 :                 if (!NT_STATUS_IS_OK(nt_status)) {
     733           0 :                         talloc_free(tmp_ctx);
     734           0 :                         return nt_status;
     735             :                 }
     736         159 :         } else if (user_dn) {
     737             :                 struct dom_sid *user_sid, *domain_sid;
     738             :                 int ret;
     739             :                 /* pull the user attributes */
     740         159 :                 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
     741             :                                       LDB_SCOPE_BASE, user_attrs,
     742             :                                       DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
     743             :                                       "(objectClass=*)");
     744         159 :                 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
     745           0 :                         talloc_free(tmp_ctx);
     746           0 :                         return NT_STATUS_NO_SUCH_USER;
     747         159 :                 } else if (ret != LDB_SUCCESS) {
     748           0 :                         talloc_free(tmp_ctx);
     749           0 :                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
     750             :                 }
     751             : 
     752         159 :                 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
     753             : 
     754         159 :                 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
     755         159 :                 if (!NT_STATUS_IS_OK(nt_status)) {
     756           0 :                         return nt_status;
     757             :                 }
     758             : 
     759         159 :                 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
     760             :                                           "(&(objectSid=%s)(objectClass=domain))",
     761             :                                             ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
     762         159 :                 if (!domain_dn) {
     763             :                         struct dom_sid_buf buf;
     764           0 :                         DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
     765             :                                   dom_sid_str_buf(domain_sid, &buf)));
     766           0 :                         return NT_STATUS_NO_SUCH_USER;
     767             :                 }
     768             : 
     769             :         } else {
     770           0 :                 return NT_STATUS_INVALID_PARAMETER;
     771             :         }
     772             : 
     773         159 :         nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
     774             :                                              lpcfg_netbios_name(lp_ctx),
     775             :                                              lpcfg_sam_name(lp_ctx),
     776             :                                              lpcfg_sam_dnsname(lp_ctx),
     777             :                                              domain_dn,
     778             :                                              msg,
     779             :                                              user_sess_key, lm_sess_key,
     780             :                                              user_info_dc);
     781         159 :         if (!NT_STATUS_IS_OK(nt_status)) {
     782           0 :                 talloc_free(tmp_ctx);
     783           0 :                 return nt_status;
     784             :         }
     785             : 
     786         159 :         talloc_steal(mem_ctx, *user_info_dc);
     787         159 :         talloc_free(tmp_ctx);
     788             : 
     789         159 :         return NT_STATUS_OK;
     790             : }
     791             : 
     792             : /*
     793             :  * Returns the details for the Password Settings Object (PSO), if one applies
     794             :  * the user.
     795             :  */
     796        3503 : static int authsam_get_user_pso(struct ldb_context *sam_ctx,
     797             :                                 TALLOC_CTX *mem_ctx,
     798             :                                 struct ldb_message *user_msg,
     799             :                                 struct ldb_message **pso_msg)
     800             : {
     801        3503 :         const char *attrs[] = { "msDS-LockoutThreshold",
     802             :                                 "msDS-LockoutObservationWindow",
     803             :                                 NULL };
     804        3503 :         struct ldb_dn *pso_dn = NULL;
     805        3503 :         struct ldb_result *res = NULL;
     806             :         int ret;
     807             : 
     808             :         /* check if the user has a PSO that applies to it */
     809        3503 :         pso_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, user_msg,
     810             :                                          "msDS-ResultantPSO");
     811             : 
     812        3503 :         if (pso_dn != NULL) {
     813          25 :                 ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, pso_dn, attrs, 0);
     814          25 :                 if (ret != LDB_SUCCESS) {
     815           0 :                         return ret;
     816             :                 }
     817             : 
     818          25 :                 *pso_msg = res->msgs[0];
     819             :         }
     820             : 
     821        3503 :         return LDB_SUCCESS;
     822             : }
     823             : 
     824             : /*
     825             :  * Re-read the bad password and successful logon data for a user.
     826             :  *
     827             :  * The DN in the passed user record should contain the "objectGUID" in case the
     828             :  * object DN has changed.
     829             :  */
     830       29966 : NTSTATUS authsam_reread_user_logon_data(
     831             :         struct ldb_context *sam_ctx,
     832             :         TALLOC_CTX *mem_ctx,
     833             :         const struct ldb_message *user_msg,
     834             :         struct ldb_message **current)
     835             : {
     836       29966 :         const struct ldb_val *v = NULL;
     837       29966 :         struct ldb_result *res = NULL;
     838       29966 :         uint16_t acct_flags = 0;
     839       29966 :         const char *attr_name = "msDS-User-Account-Control-Computed";
     840             : 
     841             :         int ret;
     842             : 
     843             :         /*
     844             :          * Re-read the account details, using the GUID in case the DN
     845             :          * is being changed (this is automatic in LDB because the
     846             :          * original search also used DSDB_SEARCH_SHOW_EXTENDED_DN)
     847             :          *
     848             :          * We re read all the attributes in user_attrs, rather than using a
     849             :          * subset to ensure that we can reuse existing validation code.
     850             :          */
     851       29966 :         ret = dsdb_search_dn(sam_ctx,
     852             :                              mem_ctx,
     853             :                              &res,
     854       29966 :                              user_msg->dn,
     855             :                              user_attrs,
     856             :                              DSDB_SEARCH_SHOW_EXTENDED_DN);
     857       29966 :         if (ret != LDB_SUCCESS) {
     858           0 :                 DBG_ERR("Unable to re-read account control data for %s\n",
     859             :                         ldb_dn_get_linearized(user_msg->dn));
     860           0 :                 return NT_STATUS_INTERNAL_ERROR;
     861             :         }
     862             : 
     863             :         /*
     864             :          * Ensure the account has not been locked out by another request
     865             :          */
     866       29966 :         v = ldb_msg_find_ldb_val(res->msgs[0], attr_name);
     867       29966 :         if (v == NULL || v->data == NULL) {
     868           0 :                 DBG_ERR("No %s attribute for %s\n",
     869             :                         attr_name,
     870             :                         ldb_dn_get_linearized(user_msg->dn));
     871           0 :                 TALLOC_FREE(res);
     872           0 :                 return NT_STATUS_INTERNAL_ERROR;
     873             :         }
     874       29966 :         acct_flags = samdb_result_acct_flags(res->msgs[0], attr_name);
     875       29966 :         if (acct_flags & ACB_AUTOLOCK) {
     876           0 :                 DBG_WARNING(
     877             :                         "Account for user %s was locked out.\n",
     878             :                         ldb_dn_get_linearized(user_msg->dn));
     879           0 :                 TALLOC_FREE(res);
     880           0 :                 return NT_STATUS_ACCOUNT_LOCKED_OUT;
     881             :         }
     882       29966 :         *current = talloc_steal(mem_ctx, res->msgs[0]);
     883       29966 :         TALLOC_FREE(res);
     884       29966 :         return NT_STATUS_OK;
     885             : }
     886             : 
     887       72961 : static struct db_context *authsam_get_bad_password_db(
     888             :         TALLOC_CTX *mem_ctx,
     889             :         struct ldb_context *sam_ctx)
     890             : {
     891       72961 :         struct loadparm_context *lp_ctx = NULL;
     892       72961 :         const char *db_name = "bad_password";
     893       72961 :         struct db_context *db_ctx =  NULL;
     894             : 
     895       72961 :         lp_ctx = ldb_get_opaque(sam_ctx, "loadparm");
     896       72961 :         if (lp_ctx == NULL) {
     897           0 :                 DBG_ERR("Unable to get loadparm_context\n");
     898           0 :                 return NULL;
     899             :         }
     900             : 
     901       72961 :         db_ctx = cluster_db_tmp_open(mem_ctx, lp_ctx, db_name, TDB_DEFAULT);
     902       72961 :         if (db_ctx == NULL) {
     903           0 :                 DBG_ERR("Unable to open bad password attempts database\n");
     904           0 :                 return NULL;
     905             :         }
     906       72961 :         return db_ctx;
     907             : }
     908             : 
     909       72961 : static NTSTATUS get_object_sid_as_tdb_data(
     910             :         TALLOC_CTX *mem_ctx,
     911             :         const struct ldb_message *msg,
     912             :         struct dom_sid_buf *buf,
     913             :         TDB_DATA *key)
     914             : {
     915       72961 :         struct dom_sid *objectsid = NULL;
     916             : 
     917             :         /*
     918             :          * Convert the objectSID to a human readable form to
     919             :          * make debugging easier
     920             :          */
     921       72961 :         objectsid = samdb_result_dom_sid(mem_ctx, msg, "objectSID");
     922       72961 :         if (objectsid == NULL) {
     923           0 :                 DBG_ERR("Unable to extract objectSID\n");
     924           0 :                 return NT_STATUS_INTERNAL_ERROR;
     925             :         }
     926       72961 :         dom_sid_str_buf(objectsid, buf);
     927       72961 :         key->dptr = (unsigned char *)buf->buf;
     928       72961 :         key->dsize = strlen(buf->buf);
     929             : 
     930       72961 :         talloc_free(objectsid);
     931             : 
     932       72961 :         return NT_STATUS_OK;
     933             : }
     934             : 
     935             : /*
     936             :  * Add the users objectSID to the bad password attempt database
     937             :  * to indicate that last authentication failed due to a bad password
     938             :  */
     939         521 : static NTSTATUS authsam_set_bad_password_indicator(
     940             :         struct ldb_context *sam_ctx,
     941             :         TALLOC_CTX *mem_ctx,
     942             :         const struct ldb_message *msg)
     943             : {
     944         521 :         NTSTATUS status = NT_STATUS_OK;
     945             :         struct dom_sid_buf buf;
     946         521 :         TDB_DATA key = {0};
     947         521 :         TDB_DATA value = {0};
     948         521 :         struct db_context *db = NULL;
     949             : 
     950         521 :         TALLOC_CTX *ctx = talloc_new(mem_ctx);
     951         521 :         if (ctx == NULL) {
     952           0 :                 return NT_STATUS_NO_MEMORY;
     953             :         }
     954             : 
     955         521 :         db = authsam_get_bad_password_db(ctx, sam_ctx);
     956         521 :         if (db == NULL) {
     957           0 :                 status = NT_STATUS_INTERNAL_ERROR;
     958           0 :                 goto exit;
     959             :         }
     960             : 
     961         521 :         status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
     962         521 :         if (!NT_STATUS_IS_OK(status)) {
     963           0 :                 goto exit;
     964             :         }
     965             : 
     966         521 :         status = dbwrap_store(db, key, value, 0);
     967         521 :         if (!NT_STATUS_IS_OK(status)) {
     968           0 :                 DBG_ERR("Unable to store bad password indicator\n");
     969             :         }
     970         521 : exit:
     971         521 :         talloc_free(ctx);
     972         521 :         return status;
     973             : }
     974             : 
     975             : /*
     976             :  * see if the users objectSID is in the bad password attempt database
     977             :  */
     978       36220 : static NTSTATUS authsam_check_bad_password_indicator(
     979             :         struct ldb_context *sam_ctx,
     980             :         TALLOC_CTX *mem_ctx,
     981             :         bool *exists,
     982             :         const struct ldb_message *msg)
     983             : {
     984       36220 :         NTSTATUS status = NT_STATUS_OK;
     985             :         struct dom_sid_buf buf;
     986       36220 :         TDB_DATA key = {0};
     987       36220 :         struct db_context *db = NULL;
     988             : 
     989       36220 :         TALLOC_CTX *ctx = talloc_new(mem_ctx);
     990       36220 :         if (ctx == NULL) {
     991           0 :                 return NT_STATUS_NO_MEMORY;
     992             :         }
     993             : 
     994       36220 :         db = authsam_get_bad_password_db(ctx, sam_ctx);
     995       36220 :         if (db == NULL) {
     996           0 :                 status = NT_STATUS_INTERNAL_ERROR;
     997           0 :                 goto exit;
     998             :         }
     999             : 
    1000       36220 :         status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
    1001       36220 :         if (!NT_STATUS_IS_OK(status)) {
    1002           0 :                 goto exit;
    1003             :         }
    1004             : 
    1005       36220 :         *exists = dbwrap_exists(db, key);
    1006       36220 : exit:
    1007       36220 :         talloc_free(ctx);
    1008       36220 :         return status;
    1009             : }
    1010             : 
    1011             : /*
    1012             :  * Remove the users objectSID to the bad password attempt database
    1013             :  * to indicate that last authentication succeeded.
    1014             :  */
    1015       36220 : static NTSTATUS authsam_clear_bad_password_indicator(
    1016             :         struct ldb_context *sam_ctx,
    1017             :         TALLOC_CTX *mem_ctx,
    1018             :         const struct ldb_message *msg)
    1019             : {
    1020       36220 :         NTSTATUS status = NT_STATUS_OK;
    1021             :         struct dom_sid_buf buf;
    1022       36220 :         TDB_DATA key = {0};
    1023       36220 :         struct db_context *db = NULL;
    1024             : 
    1025       36220 :         TALLOC_CTX *ctx = talloc_new(mem_ctx);
    1026       36220 :         if (ctx == NULL) {
    1027           0 :                 return NT_STATUS_NO_MEMORY;
    1028             :         }
    1029             : 
    1030       36220 :         db = authsam_get_bad_password_db(ctx, sam_ctx);
    1031       36220 :         if (db == NULL) {
    1032           0 :                 status = NT_STATUS_INTERNAL_ERROR;
    1033           0 :                 goto exit;
    1034             :         }
    1035             : 
    1036       36220 :         status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
    1037       36220 :         if (!NT_STATUS_IS_OK(status)) {
    1038           0 :                 goto exit;
    1039             :         }
    1040             : 
    1041       36220 :         status = dbwrap_delete(db, key);
    1042       36220 :         if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, status)) {
    1043             :                 /*
    1044             :                  * Ok there was no bad password indicator this is expected
    1045             :                  */
    1046       35886 :                 status = NT_STATUS_OK;
    1047             :         }
    1048       36220 :         if (NT_STATUS_IS_ERR(status)) {
    1049           0 :                 DBG_ERR("Unable to delete bad password indicator, %s %s\n",
    1050             :                         nt_errstr(status),
    1051             :                         get_friendly_nt_error_msg(status));
    1052             :         }
    1053       36220 : exit:
    1054       36220 :         talloc_free(ctx);
    1055       36220 :         return status;
    1056             : }
    1057             : 
    1058        3503 : NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
    1059             :                                       struct ldb_message *msg,
    1060             :                                       struct ldb_dn *domain_dn)
    1061             : {
    1062        3503 :         const char *attrs[] = { "lockoutThreshold",
    1063             :                                 "lockOutObservationWindow",
    1064             :                                 "lockoutDuration",
    1065             :                                 "pwdProperties",
    1066             :                                 NULL };
    1067             :         int ret;
    1068             :         NTSTATUS status;
    1069             :         struct ldb_result *domain_res;
    1070        3503 :         struct ldb_message *msg_mod = NULL;
    1071        3503 :         struct ldb_message *current = NULL;
    1072        3503 :         struct ldb_message *pso_msg = NULL;
    1073        3503 :         bool txn_active = false;
    1074             :         TALLOC_CTX *mem_ctx;
    1075             : 
    1076        3503 :         mem_ctx = talloc_new(msg);
    1077        3503 :         if (mem_ctx == NULL) {
    1078           0 :                 return NT_STATUS_NO_MEMORY;
    1079             :         }
    1080             : 
    1081        3503 :         ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
    1082        3503 :         if (ret != LDB_SUCCESS) {
    1083           0 :                 TALLOC_FREE(mem_ctx);
    1084           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
    1085             :         }
    1086             : 
    1087        3503 :         ret = authsam_get_user_pso(sam_ctx, mem_ctx, msg, &pso_msg);
    1088        3503 :         if (ret != LDB_SUCCESS) {
    1089             : 
    1090             :                 /*
    1091             :                  * fallback to using the domain defaults so that we still
    1092             :                  * record the bad password attempt
    1093             :                  */
    1094           0 :                 DBG_ERR("Error (%d) checking PSO for %s\n",
    1095             :                         ret, ldb_dn_get_linearized(msg->dn));
    1096             :         }
    1097             : 
    1098             :         /*
    1099             :          * To ensure that the bad password count is updated atomically,
    1100             :          * we need to:
    1101             :          *    begin a transaction
    1102             :          *       re-read the account details,
    1103             :          *         using the <GUID= part of the DN
    1104             :          *       update the bad password count
    1105             :          *    commit the transaction.
    1106             :          */
    1107             : 
    1108             :         /*
    1109             :          * Start a new transaction
    1110             :          */
    1111        3503 :         ret = ldb_transaction_start(sam_ctx);
    1112        3503 :         if (ret != LDB_SUCCESS) {
    1113           0 :                 status = NT_STATUS_INTERNAL_ERROR;
    1114           0 :                 goto error;
    1115             :         }
    1116        3503 :         txn_active = true;
    1117             : 
    1118             :         /*
    1119             :          * Re-read the account details, using the GUID in case the DN
    1120             :          * is being changed.
    1121             :          */
    1122        3503 :         status = authsam_reread_user_logon_data(
    1123             :                 sam_ctx, mem_ctx, msg, &current);
    1124        3503 :         if (!NT_STATUS_IS_OK(status)) {
    1125             :                 /* The re-read can return account locked out, as well
    1126             :                  * as an internal error
    1127             :                  */
    1128           0 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
    1129             :                         /*
    1130             :                          * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
    1131             :                          * the transaction. Again to avoid cluttering the
    1132             :                          * audit logs with spurious errors
    1133             :                          */
    1134           0 :                         goto exit;
    1135             :                 }
    1136           0 :                 goto error;
    1137             :         }
    1138             : 
    1139             :         /*
    1140             :          * Update the bad password count and if required lock the account
    1141             :          */
    1142        3503 :         status = dsdb_update_bad_pwd_count(
    1143             :                 mem_ctx,
    1144             :                 sam_ctx,
    1145             :                 current,
    1146        3503 :                 domain_res->msgs[0],
    1147             :                 pso_msg,
    1148             :                 &msg_mod);
    1149        3503 :         if (!NT_STATUS_IS_OK(status)) {
    1150           0 :                 status = NT_STATUS_INTERNAL_ERROR;
    1151           0 :                 goto error;
    1152             :         }
    1153             : 
    1154             :         /*
    1155             :          * Write the data back to disk if required.
    1156             :          */
    1157        3503 :         if (msg_mod != NULL) {
    1158             :                 struct ldb_request *req;
    1159             : 
    1160         521 :                 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
    1161             :                                         msg_mod,
    1162             :                                         NULL,
    1163             :                                         NULL,
    1164             :                                         ldb_op_default_callback,
    1165             :                                         NULL);
    1166         521 :                 if (ret != LDB_SUCCESS) {
    1167           0 :                         TALLOC_FREE(msg_mod);
    1168           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1169           0 :                         goto error;
    1170             :                 }
    1171             : 
    1172         521 :                 ret = ldb_request_add_control(req,
    1173             :                                               DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
    1174             :                                               false, NULL);
    1175         521 :                 if (ret != LDB_SUCCESS) {
    1176           0 :                         talloc_free(req);
    1177           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1178           0 :                         goto error;
    1179             :                 }
    1180             : 
    1181             :                 /*
    1182             :                  * As we're in a transaction, make the ldb request directly
    1183             :                  * to avoid the nested transaction that would result if we
    1184             :                  * called dsdb_autotransaction_request
    1185             :                  */
    1186         521 :                 ret = ldb_request(sam_ctx, req);
    1187         521 :                 if (ret == LDB_SUCCESS) {
    1188         521 :                         ret = ldb_wait(req->handle, LDB_WAIT_ALL);
    1189             :                 }
    1190         521 :                 talloc_free(req);
    1191         521 :                 if (ret != LDB_SUCCESS) {
    1192           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1193           0 :                         goto error;
    1194             :                 }
    1195         521 :                 status = authsam_set_bad_password_indicator(
    1196             :                         sam_ctx, mem_ctx, msg);
    1197         521 :                 if (!NT_STATUS_IS_OK(status)) {
    1198           0 :                         goto error;
    1199             :                 }
    1200             :         }
    1201             :         /*
    1202             :          * Note that we may not have updated the user record, but
    1203             :          * committing the transaction in that case is still the correct
    1204             :          * thing to do.
    1205             :          * If the transaction was cancelled, this would be logged by
    1206             :          * the dsdb audit log as a failure. When in fact it is expected
    1207             :          * behaviour.
    1208             :          */
    1209        2982 : exit:
    1210        3503 :         TALLOC_FREE(mem_ctx);
    1211        3503 :         ret = ldb_transaction_commit(sam_ctx);
    1212        3503 :         if (ret != LDB_SUCCESS) {
    1213           0 :                 DBG_ERR("Error (%d) %s, committing transaction,"
    1214             :                         " while updating bad password count"
    1215             :                         " for (%s)\n",
    1216             :                         ret,
    1217             :                         ldb_errstring(sam_ctx),
    1218             :                         ldb_dn_get_linearized(msg->dn));
    1219           0 :                 return NT_STATUS_INTERNAL_ERROR;
    1220             :         }
    1221        3503 :         return status;
    1222             : 
    1223           0 : error:
    1224           0 :         DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
    1225             :                 "set lockoutTime on %s: %s\n",
    1226             :                 ldb_dn_get_linearized(msg->dn),
    1227             :                 ldb_errstring(sam_ctx) != NULL ?
    1228             :                         ldb_errstring(sam_ctx) :nt_errstr(status));
    1229           0 :         if (txn_active) {
    1230           0 :                 ret = ldb_transaction_cancel(sam_ctx);
    1231           0 :                 if (ret != LDB_SUCCESS) {
    1232           0 :                         DBG_ERR("Error rolling back transaction,"
    1233             :                                 " while updating bad password count"
    1234             :                                 " on %s: %s\n",
    1235             :                                 ldb_dn_get_linearized(msg->dn),
    1236             :                                 ldb_errstring(sam_ctx));
    1237             :                 }
    1238             :         }
    1239           0 :         TALLOC_FREE(mem_ctx);
    1240           0 :         return status;
    1241             : 
    1242             : }
    1243             : 
    1244             : /*
    1245             :  * msDS-LogonTimeSyncInterval is an int32_t number of days.
    1246             :  *
    1247             :  * The docs say: "the initial update, after the domain functional
    1248             :  * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
    1249             :  * 14 days minus a random percentage of 5 days", but we aren't doing
    1250             :  * that. The blogosphere seems to think that this randomised update
    1251             :  * happens everytime, but [MS-ADA1] doesn't agree.
    1252             :  *
    1253             :  * Dochelp referred us to the following blog post:
    1254             :  * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
    1255             :  *
    1256             :  * when msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
    1257             :  * not changed.
    1258             :  */
    1259             : 
    1260       35606 : static NTSTATUS authsam_calculate_lastlogon_sync_interval(
    1261             :         struct ldb_context *sam_ctx,
    1262             :         TALLOC_CTX *ctx,
    1263             :         struct ldb_dn *domain_dn,
    1264             :         NTTIME *sync_interval_nt)
    1265             : {
    1266             :         static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
    1267             :                                         NULL };
    1268             :         int ret;
    1269       35606 :         struct ldb_result *domain_res = NULL;
    1270       35606 :         TALLOC_CTX *mem_ctx = NULL;
    1271             :         uint32_t sync_interval;
    1272             : 
    1273       35606 :         mem_ctx = talloc_new(ctx);
    1274       35606 :         if (mem_ctx == NULL) {
    1275           0 :                 return NT_STATUS_NO_MEMORY;
    1276             :         }
    1277             : 
    1278       35606 :         ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
    1279             :                              0);
    1280       35606 :         if (ret != LDB_SUCCESS || domain_res->count != 1) {
    1281           0 :                 TALLOC_FREE(mem_ctx);
    1282           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
    1283             :         }
    1284             : 
    1285       35606 :         sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
    1286             :                                                  "msDS-LogonTimeSyncInterval",
    1287             :                                                  14);
    1288       35606 :         DEBUG(5, ("sync interval is %d\n", sync_interval));
    1289       35606 :         if (sync_interval >= 5){
    1290             :                 /*
    1291             :                  * Subtract "a random percentage of 5" days. Presumably this
    1292             :                  * percentage is between 0 and 100, and modulus is accurate
    1293             :                  * enough.
    1294             :                  */
    1295       35606 :                 uint32_t r = generate_random() % 6;
    1296       35606 :                 sync_interval -= r;
    1297       35606 :                 DBG_INFO("randomised sync interval is %d (-%d)\n", sync_interval, r);
    1298             :         }
    1299             :         /* In the case where sync_interval < 5 there is no randomisation */
    1300             : 
    1301             :         /*
    1302             :          * msDS-LogonTimeSyncInterval is an int32_t number of days,
    1303             :          * while lastLogonTimestamp (to be updated) is in the 64 bit
    1304             :          * 100ns NTTIME format so we must convert.
    1305             :          */
    1306       35606 :         *sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
    1307       35606 :         TALLOC_FREE(mem_ctx);
    1308       35606 :         return NT_STATUS_OK;
    1309             : }
    1310             : 
    1311             : 
    1312             : /*
    1313             :  * We only set lastLogonTimestamp if the current value is older than
    1314             :  * now - msDS-LogonTimeSyncInterval days.
    1315             :  *
    1316             :  * lastLogonTimestamp is in the 64 bit 100ns NTTIME format
    1317             :  */
    1318       60994 : static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
    1319             :                                                    struct ldb_message *msg_mod,
    1320             :                                                    struct ldb_dn *domain_dn,
    1321             :                                                    NTTIME old_timestamp,
    1322             :                                                    NTTIME now,
    1323             :                                                    NTTIME sync_interval_nt)
    1324             : {
    1325             :         int ret;
    1326       60994 :         DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
    1327             :                   (long long int)old_timestamp,
    1328             :                   (long long int)(now - sync_interval_nt),
    1329             :                   (long long int)(old_timestamp - now + sync_interval_nt)));
    1330             : 
    1331       60994 :         if (sync_interval_nt == 0){
    1332             :                 /*
    1333             :                  * Setting msDS-LogonTimeSyncInterval to zero is how you ask
    1334             :                  * that nothing happens here.
    1335             :                  */
    1336           0 :                 return NT_STATUS_OK;
    1337             :         }
    1338       60994 :         if (old_timestamp > now){
    1339           0 :                 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
    1340             :                           (long long int)old_timestamp, (long long int)now));
    1341             :                 /* then what? */
    1342             : 
    1343       60994 :         } else if (old_timestamp < now - sync_interval_nt){
    1344       15987 :                 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
    1345             :                           (long long int)now));
    1346             : 
    1347             :                 /* The time has come to update lastLogonTimestamp */
    1348       15987 :                 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
    1349             :                                           "lastLogonTimestamp", now);
    1350             : 
    1351       15987 :                 if (ret != LDB_SUCCESS) {
    1352           0 :                         return NT_STATUS_NO_MEMORY;
    1353             :                 }
    1354             :         }
    1355       60994 :         return NT_STATUS_OK;
    1356             : }
    1357             : 
    1358             : /****************************************************************************
    1359             :  Look for the specified user in the sam, return ldb result structures
    1360             : ****************************************************************************/
    1361             : 
    1362       23698 : NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
    1363             :                                          const char *account_name,
    1364             :                                          struct ldb_dn *domain_dn,
    1365             :                                          struct ldb_message **ret_msg)
    1366             : {
    1367             :         int ret;
    1368             : 
    1369             :         /* pull the user attributes */
    1370       23698 :         ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
    1371             :                               user_attrs,
    1372             :                               DSDB_SEARCH_SHOW_EXTENDED_DN,
    1373             :                               "(&(sAMAccountName=%s)(objectclass=user))",
    1374             :                               ldb_binary_encode_string(mem_ctx, account_name));
    1375       23698 :         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
    1376         811 :                 DEBUG(3,("sam_search_user: Couldn't find user [%s] in samdb, under %s\n",
    1377             :                          account_name, ldb_dn_get_linearized(domain_dn)));
    1378         811 :                 return NT_STATUS_NO_SUCH_USER;
    1379             :         }
    1380       22887 :         if (ret != LDB_SUCCESS) {
    1381           0 :                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
    1382             :         }
    1383             : 
    1384       22887 :         return NT_STATUS_OK;
    1385             : }
    1386             : 
    1387             : 
    1388             : /* Reset the badPwdCount to zero and update the lastLogon time. */
    1389       36220 : NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
    1390             :                                           const struct ldb_message *msg,
    1391             :                                           struct ldb_dn *domain_dn,
    1392             :                                           bool interactive_or_kerberos,
    1393             :                                           TALLOC_CTX *send_to_sam_mem_ctx,
    1394             :                                           struct netr_SendToSamBase **send_to_sam)
    1395             : {
    1396             :         int ret;
    1397             :         NTSTATUS status;
    1398             :         int badPwdCount;
    1399             :         int dbBadPwdCount;
    1400             :         int64_t lockoutTime;
    1401             :         struct ldb_message *msg_mod;
    1402             :         TALLOC_CTX *mem_ctx;
    1403             :         struct timeval tv_now;
    1404             :         NTTIME now;
    1405             :         NTTIME lastLogonTimestamp;
    1406             :         int64_t lockOutObservationWindow;
    1407       36220 :         NTTIME sync_interval_nt = 0;
    1408       36220 :         bool am_rodc = false;
    1409       36220 :         bool txn_active = false;
    1410             :         bool need_db_reread;
    1411             : 
    1412       36220 :         mem_ctx = talloc_new(msg);
    1413       36220 :         if (mem_ctx == NULL) {
    1414           0 :                 return NT_STATUS_NO_MEMORY;
    1415             :         }
    1416             : 
    1417             :         /*
    1418             :          * Any update of the last logon data, needs to be done inside a
    1419             :          * transaction.
    1420             :          * And the user data needs to be re-read, and the account re-checked
    1421             :          * for lockout.
    1422             :          *
    1423             :          * Howevver we have long-running transactions like replication
    1424             :          * that could otherwise grind the system to a halt so we first
    1425             :          * determine if *this* account has seen a bad password,
    1426             :          * otherwise we only start a transaction if there was a need
    1427             :          * (because a change was to be made).
    1428             :          */
    1429             : 
    1430       36220 :         status = authsam_check_bad_password_indicator(
    1431             :                 sam_ctx, mem_ctx, &need_db_reread, msg);
    1432       36220 :         if (!NT_STATUS_IS_OK(status)) {
    1433           0 :                 return status;
    1434             :         }
    1435             : 
    1436       36220 :         if (interactive_or_kerberos == false) {
    1437             :                 /*
    1438             :                  * Avoid calculating this twice, it reads the PSO.  A
    1439             :                  * race on this is unimportant.
    1440             :                  */
    1441             :                 lockOutObservationWindow
    1442       17537 :                         = samdb_result_msds_LockoutObservationWindow(
    1443             :                                 sam_ctx, mem_ctx, domain_dn, msg);
    1444             :         }
    1445             : 
    1446       36220 :         ret = samdb_rodc(sam_ctx, &am_rodc);
    1447       36220 :         if (ret != LDB_SUCCESS) {
    1448           0 :                 status = NT_STATUS_INTERNAL_ERROR;
    1449           0 :                 goto error;
    1450             :         }
    1451             : 
    1452       36220 :         if (!am_rodc) {
    1453             :                 /*
    1454             :                  * Avoid reading the main domain DN twice.  A race on
    1455             :                  * this is unimportant.
    1456             :                  */
    1457       35606 :                 status = authsam_calculate_lastlogon_sync_interval(
    1458             :                         sam_ctx, mem_ctx, domain_dn, &sync_interval_nt);
    1459             : 
    1460       35606 :                 if (!NT_STATUS_IS_OK(status)) {
    1461           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1462           0 :                         goto error;
    1463             :                 }
    1464             :         }
    1465             : 
    1466       36220 : get_transaction:
    1467             : 
    1468       62079 :         if (need_db_reread) {
    1469       26193 :                 struct ldb_message *current = NULL;
    1470             : 
    1471             :                 /*
    1472             :                  * Start a new transaction
    1473             :                  */
    1474       26193 :                 ret = ldb_transaction_start(sam_ctx);
    1475       26193 :                 if (ret != LDB_SUCCESS) {
    1476           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1477           0 :                         goto error;
    1478             :                 }
    1479             : 
    1480       26193 :                 txn_active = true;
    1481             : 
    1482             :                 /*
    1483             :                  * Re-read the account details, using the GUID
    1484             :                  * embedded in DN so this is safe against a race where
    1485             :                  * it is being renamed.
    1486             :                  */
    1487       26193 :                 status = authsam_reread_user_logon_data(
    1488             :                         sam_ctx, mem_ctx, msg, &current);
    1489       26193 :                 if (!NT_STATUS_IS_OK(status)) {
    1490             :                         /*
    1491             :                          * The re-read can return account locked out, as well
    1492             :                          * as an internal error
    1493             :                          */
    1494           0 :                         if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
    1495             :                                 /*
    1496             :                                  * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
    1497             :                                  * the transaction. Again to avoid cluttering the
    1498             :                                  * audit logs with spurious errors
    1499             :                                  */
    1500           0 :                                 goto exit;
    1501             :                         }
    1502           0 :                         goto error;
    1503             :                 }
    1504       26193 :                 msg = current;
    1505             :         }
    1506             : 
    1507       62079 :         lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
    1508       62079 :         dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
    1509       62079 :         tv_now = timeval_current();
    1510       62079 :         now = timeval_to_nttime(&tv_now);
    1511             : 
    1512       62079 :         if (interactive_or_kerberos) {
    1513       37233 :                 badPwdCount = dbBadPwdCount;
    1514             :         } else {
    1515             :                 /*
    1516             :                  * We get lockOutObservationWindow above, before the
    1517             :                  * transaction
    1518             :                  */
    1519       24846 :                 badPwdCount = dsdb_effective_badPwdCount(
    1520             :                         msg, lockOutObservationWindow, now);
    1521             :         }
    1522       62079 :         lastLogonTimestamp =
    1523       62079 :                 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
    1524             : 
    1525       62079 :         DEBUG(5, ("lastLogonTimestamp is %lld\n",
    1526             :                   (long long int)lastLogonTimestamp));
    1527             : 
    1528       62079 :         msg_mod = ldb_msg_new(mem_ctx);
    1529       62079 :         if (msg_mod == NULL) {
    1530           0 :                 status = NT_STATUS_NO_MEMORY;
    1531           0 :                 goto error;
    1532             :         }
    1533             : 
    1534             :         /*
    1535             :          * By using the DN from msg->dn directly, we allow LDB to
    1536             :          * prefer the embedded GUID form, so this is actually quite
    1537             :          * safe even in the case where DN has been changed
    1538             :          */
    1539       62079 :         msg_mod->dn = msg->dn;
    1540             : 
    1541       62079 :         if (lockoutTime != 0) {
    1542             :                 /*
    1543             :                  * This implies "badPwdCount" = 0, see samldb_lockout_time()
    1544             :                  */
    1545          36 :                 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
    1546          36 :                 if (ret != LDB_SUCCESS) {
    1547           0 :                         status = NT_STATUS_NO_MEMORY;
    1548           0 :                         goto error;
    1549             :                 }
    1550       62043 :         } else if (badPwdCount != 0) {
    1551         304 :                 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
    1552         304 :                 if (ret != LDB_SUCCESS) {
    1553           0 :                         status = NT_STATUS_NO_MEMORY;
    1554           0 :                         goto error;
    1555             :                 }
    1556             :         }
    1557             : 
    1558       62079 :         if (interactive_or_kerberos ||
    1559         185 :             (badPwdCount != 0 && lockoutTime == 0)) {
    1560       37418 :                 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
    1561             :                                           "lastLogon", now);
    1562       37418 :                 if (ret != LDB_SUCCESS) {
    1563           0 :                         status = NT_STATUS_NO_MEMORY;
    1564           0 :                         goto error;
    1565             :                 }
    1566             :         }
    1567             : 
    1568       62079 :         if (interactive_or_kerberos) {
    1569             :                 int logonCount;
    1570             : 
    1571       37233 :                 logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
    1572             : 
    1573       37233 :                 logonCount += 1;
    1574             : 
    1575       37233 :                 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
    1576             :                                         "logonCount", logonCount);
    1577       37233 :                 if (ret != LDB_SUCCESS) {
    1578           0 :                         status = NT_STATUS_NO_MEMORY;
    1579           0 :                         goto error;
    1580             :                 }
    1581             :         } else {
    1582             :                 /* Set an unset logonCount to 0 on first successful login */
    1583       24846 :                 if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
    1584          34 :                         ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
    1585             :                                                 "logonCount", 0);
    1586          34 :                         if (ret != LDB_SUCCESS) {
    1587           0 :                                 TALLOC_FREE(mem_ctx);
    1588           0 :                                 return NT_STATUS_NO_MEMORY;
    1589             :                         }
    1590             :                 }
    1591             :         }
    1592             : 
    1593       62079 :         if (!am_rodc) {
    1594       60994 :                 status = authsam_update_lastlogon_timestamp(
    1595             :                         sam_ctx,
    1596             :                         msg_mod,
    1597             :                         domain_dn,
    1598             :                         lastLogonTimestamp,
    1599             :                         now,
    1600             :                         sync_interval_nt);
    1601       60994 :                 if (!NT_STATUS_IS_OK(status)) {
    1602           0 :                         status = NT_STATUS_NO_MEMORY;
    1603           0 :                         goto error;
    1604             :                 }
    1605             :         } else {
    1606             :                 /* Perform the (async) SendToSAM calls for MS-SAMS */
    1607        1085 :                 if (dbBadPwdCount != 0 && send_to_sam != NULL) {
    1608             :                         struct netr_SendToSamBase *base_msg;
    1609          17 :                         struct GUID guid = samdb_result_guid(msg, "objectGUID");
    1610             : 
    1611          17 :                         base_msg = talloc_zero(send_to_sam_mem_ctx,
    1612             :                                                struct netr_SendToSamBase);
    1613          17 :                         if (base_msg == NULL) {
    1614           0 :                                 status = NT_STATUS_NO_MEMORY;
    1615           0 :                                 goto error;
    1616             :                         }
    1617             : 
    1618          17 :                         base_msg->message_type = SendToSamResetBadPasswordCount;
    1619          17 :                         base_msg->message_size = 16;
    1620          17 :                         base_msg->message.reset_bad_password.guid = guid;
    1621          17 :                         *send_to_sam = base_msg;
    1622             :                 }
    1623             :         }
    1624             : 
    1625       62079 :         if (msg_mod->num_elements > 0) {
    1626             :                 unsigned int i;
    1627             :                 struct ldb_request *req;
    1628             : 
    1629             :                 /*
    1630             :                  * If it turns out we are going to update the DB, go
    1631             :                  * back to the start, get a transaction and the
    1632             :                  * current DB state and try again
    1633             :                  */
    1634       52052 :                 if (txn_active == false) {
    1635       25859 :                         need_db_reread = true;
    1636       25859 :                         goto get_transaction;
    1637             :                 }
    1638             : 
    1639             :                 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
    1640       72214 :                 for (i=0;i<msg_mod->num_elements;i++) {
    1641       46021 :                         msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
    1642             :                 }
    1643             : 
    1644       26193 :                 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
    1645             :                                         msg_mod,
    1646             :                                         NULL,
    1647             :                                         NULL,
    1648             :                                         ldb_op_default_callback,
    1649             :                                         NULL);
    1650       26193 :                 if (ret != LDB_SUCCESS) {
    1651           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1652           0 :                         goto error;
    1653             :                 }
    1654             : 
    1655       26193 :                 ret = ldb_request_add_control(req,
    1656             :                                               DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
    1657             :                                               false, NULL);
    1658       26193 :                 if (ret != LDB_SUCCESS) {
    1659           0 :                         TALLOC_FREE(req);
    1660           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1661           0 :                         goto error;
    1662             :                 }
    1663             :                 /*
    1664             :                  * As we're in a transaction, make the ldb request directly
    1665             :                  * to avoid the nested transaction that would result if we
    1666             :                  * called dsdb_autotransaction_request
    1667             :                  */
    1668       26193 :                 ret = ldb_request(sam_ctx, req);
    1669       26193 :                 if (ret == LDB_SUCCESS) {
    1670       26193 :                         ret = ldb_wait(req->handle, LDB_WAIT_ALL);
    1671             :                 }
    1672       26193 :                 TALLOC_FREE(req);
    1673       26193 :                 if (ret != LDB_SUCCESS) {
    1674           0 :                         status = NT_STATUS_INTERNAL_ERROR;
    1675           0 :                         goto error;
    1676             :                 }
    1677             :         }
    1678       36220 :         status = authsam_clear_bad_password_indicator(sam_ctx, mem_ctx, msg);
    1679       36220 :         if (!NT_STATUS_IS_OK(status)) {
    1680           0 :                 goto error;
    1681             :         }
    1682             : 
    1683             :         /*
    1684             :          * Note that we may not have updated the user record, but
    1685             :          * committing the transaction in that case is still the correct
    1686             :          * thing to do.
    1687             :          * If the transaction was cancelled, this would be logged by
    1688             :          * the dsdb audit log as a failure. When in fact it is expected
    1689             :          * behaviour.
    1690             :          *
    1691             :          * Thankfully both TDB and LMDB seem to optimise for the empty
    1692             :          * transaction case
    1693             :          */
    1694       36220 : exit:
    1695       36220 :         TALLOC_FREE(mem_ctx);
    1696             : 
    1697       36220 :         if (txn_active == false) {
    1698       10027 :                 return status;
    1699             :         }
    1700             : 
    1701       26193 :         ret = ldb_transaction_commit(sam_ctx);
    1702       26193 :         if (ret != LDB_SUCCESS) {
    1703           0 :                 DBG_ERR("Error (%d) %s, committing transaction,"
    1704             :                         " while updating successful logon accounting"
    1705             :                         " for (%s)\n",
    1706             :                         ret,
    1707             :                         ldb_errstring(sam_ctx),
    1708             :                         ldb_dn_get_linearized(msg->dn));
    1709           0 :                 return NT_STATUS_INTERNAL_ERROR;
    1710             :         }
    1711       26193 :         return status;
    1712             : 
    1713           0 : error:
    1714           0 :         DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
    1715             :                 "set lockoutTime on %s: %s\n",
    1716             :                 ldb_dn_get_linearized(msg->dn),
    1717             :                 ldb_errstring(sam_ctx) != NULL ?
    1718             :                         ldb_errstring(sam_ctx) :nt_errstr(status));
    1719           0 :         if (txn_active) {
    1720           0 :                 ret = ldb_transaction_cancel(sam_ctx);
    1721           0 :                 if (ret != LDB_SUCCESS) {
    1722           0 :                         DBG_ERR("Error rolling back transaction,"
    1723             :                                 " while updating bad password count"
    1724             :                                 " on %s: %s\n",
    1725             :                                 ldb_dn_get_linearized(msg->dn),
    1726             :                                 ldb_errstring(sam_ctx));
    1727             :                 }
    1728             :         }
    1729           0 :         TALLOC_FREE(mem_ctx);
    1730           0 :         return status;
    1731             : }

Generated by: LCOV version 1.14