Coverage Report

Created: 2025-11-16 06:57

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