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

Generated by: LCOV version 1.14