WHATSNEW: Add release notes for Samba 4.19.1.
[Samba.git] / source4 / auth / sam.c
blob3b34525b7e161d311fac2c625d3707d9ead46fcb
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
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.
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.
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/>.
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"
37 #undef DBGC_CLASS
38 #define DBGC_CLASS DBGC_AUTH
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", \
53 /* passwords */ \
54 "unicodePwd", \
56 "userAccountControl", \
57 "msDS-User-Account-Control-Computed", \
58 "objectSid", \
60 "pwdLastSet", \
61 "msDS-UserPasswordExpiryTimeComputed", \
62 "accountExpires", \
64 /* Needed for RODC rule processing */ \
65 "msDS-KrbTgtLinkBL"
67 #define AUTHN_POLICY_ATTRS \
68 /* Required for authentication policies / silos */ \
69 "msDS-AssignedAuthNPolicy", \
70 "msDS-AssignedAuthNPolicySilo"
72 const char *krbtgt_attrs[] = {
74 * Authentication policies will not be enforced on the TGS
75 * account. Don’t include the relevant attributes in the account search.
77 KRBTGT_ATTRS, NULL
80 const char *server_attrs[] = {
81 KRBTGT_ATTRS,
82 AUTHN_POLICY_ATTRS,
83 NULL
86 const char *user_attrs[] = {
88 * This ordering (having msDS-ResultantPSO first) is
89 * important. By processing this attribute first it is
90 * available in the operational module for the other PSO
91 * attribute calcuations to use.
93 "msDS-ResultantPSO",
95 KRBTGT_ATTRS,
96 AUTHN_POLICY_ATTRS,
98 "logonHours",
101 * To allow us to zero the badPwdCount and lockoutTime on
102 * successful logon, without database churn
104 "lockoutTime",
107 * Needed for SendToSAM requests
109 "objectGUID",
111 /* check 'allowed workstations' */
112 "userWorkstations",
114 /* required for user_info_dc, not access control: */
115 "displayName",
116 "scriptPath",
117 "profilePath",
118 "homeDirectory",
119 "homeDrive",
120 "lastLogon",
121 "lastLogonTimestamp",
122 "lastLogoff",
123 "accountExpires",
124 "badPwdCount",
125 "logonCount",
126 "primaryGroupID",
127 "memberOf",
128 "badPasswordTime",
129 "lmPwdHistory",
130 "ntPwdHistory",
131 NULL,
134 /****************************************************************************
135 Check if a user is allowed to logon at this time. Note this is the
136 servers local time, as logon hours are just specified as a weekly
137 bitmask.
138 ****************************************************************************/
140 static bool logon_hours_ok(struct ldb_message *msg, const char *name_for_logs)
142 /* In logon hours first bit is Sunday from 12AM to 1AM */
143 const struct ldb_val *hours;
144 struct tm *utctime;
145 time_t lasttime;
146 const char *asct;
147 uint8_t bitmask, bitpos;
149 hours = ldb_msg_find_ldb_val(msg, "logonHours");
150 if (!hours) {
151 DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n", name_for_logs));
152 return true;
155 if (hours->length != 168/8) {
156 DEBUG(5,("logon_hours_ok: malformed logon hours restrictions for user %s\n", name_for_logs));
157 return true;
160 lasttime = time(NULL);
161 utctime = gmtime(&lasttime);
162 if (!utctime) {
163 DEBUG(1, ("logon_hours_ok: failed to get gmtime. Failing logon for user %s\n",
164 name_for_logs));
165 return false;
168 /* find the corresponding byte and bit */
169 bitpos = (utctime->tm_wday * 24 + utctime->tm_hour) % 168;
170 bitmask = 1 << (bitpos % 8);
172 if (! (hours->data[bitpos/8] & bitmask)) {
173 struct tm *t = localtime(&lasttime);
174 if (!t) {
175 asct = "INVALID TIME";
176 } else {
177 asct = asctime(t);
178 if (!asct) {
179 asct = "INVALID TIME";
183 DEBUG(1, ("logon_hours_ok: Account for user %s not allowed to "
184 "logon at this time (%s).\n",
185 name_for_logs, asct ));
186 return false;
189 asct = asctime(utctime);
190 DEBUG(5,("logon_hours_ok: user %s allowed to logon at this time (%s)\n",
191 name_for_logs, asct ? asct : "UNKNOWN TIME" ));
193 return true;
196 /****************************************************************************
197 Do a specific test for a SAM_ACCOUNT being valid for this connection
198 (ie not disabled, expired and the like).
199 ****************************************************************************/
200 _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
201 struct ldb_context *sam_ctx,
202 uint32_t logon_parameters,
203 struct ldb_dn *domain_dn,
204 struct ldb_message *msg,
205 const char *logon_workstation,
206 const char *name_for_logs,
207 bool allow_domain_trust,
208 bool password_change)
210 uint16_t acct_flags;
211 const char *workstation_list;
212 NTTIME acct_expiry;
213 NTTIME must_change_time;
214 struct timeval tv_now = timeval_current();
215 NTTIME now = timeval_to_nttime(&tv_now);
217 DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
219 acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
221 acct_expiry = samdb_result_account_expires(msg);
223 /* Check for when we must change this password, taking the
224 * userAccountControl flags into account */
225 must_change_time = samdb_result_nttime(msg,
226 "msDS-UserPasswordExpiryTimeComputed", 0);
228 workstation_list = ldb_msg_find_attr_as_string(msg, "userWorkstations", NULL);
230 /* Quit if the account was disabled. */
231 if (acct_flags & ACB_DISABLED) {
232 DEBUG(2,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
233 return NT_STATUS_ACCOUNT_DISABLED;
236 /* Quit if the account was locked out. */
237 if (acct_flags & ACB_AUTOLOCK) {
238 DEBUG(2,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
239 return NT_STATUS_ACCOUNT_LOCKED_OUT;
242 /* Test account expire time */
243 if (now > acct_expiry) {
244 DEBUG(2,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
245 DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
246 nt_time_string(mem_ctx, acct_expiry)));
247 return NT_STATUS_ACCOUNT_EXPIRED;
250 /* check for immediate expiry "must change at next logon" (but not if this is a password change request) */
251 if ((must_change_time == 0) && !password_change) {
252 DEBUG(2,("sam_account_ok: Account for user '%s' password must change!.\n",
253 name_for_logs));
254 return NT_STATUS_PASSWORD_MUST_CHANGE;
257 /* check for expired password (but not if this is a password change request) */
258 if ((must_change_time < now) && !password_change) {
259 DEBUG(2,("sam_account_ok: Account for user '%s' password expired!.\n",
260 name_for_logs));
261 DEBUG(2,("sam_account_ok: Password expired at '%s' unix time.\n",
262 nt_time_string(mem_ctx, must_change_time)));
263 return NT_STATUS_PASSWORD_EXPIRED;
266 /* Test workstation. Workstation list is comma separated. */
267 if (logon_workstation && workstation_list && *workstation_list) {
268 bool invalid_ws = true;
269 int i;
270 char **workstations = str_list_make(mem_ctx, workstation_list, ",");
272 for (i = 0; workstations && workstations[i]; i++) {
273 DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
274 workstations[i], logon_workstation));
276 if (strequal(workstations[i], logon_workstation)) {
277 invalid_ws = false;
278 break;
282 talloc_free(workstations);
284 if (invalid_ws) {
285 return NT_STATUS_INVALID_WORKSTATION;
289 if (!logon_hours_ok(msg, name_for_logs)) {
290 return NT_STATUS_INVALID_LOGON_HOURS;
293 if (!allow_domain_trust) {
294 if (acct_flags & ACB_DOMTRUST) {
295 DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
296 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
299 if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
300 if (acct_flags & ACB_SVRTRUST) {
301 DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
302 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
305 if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
306 /* TODO: this fails with current solaris client. We
307 need to work with Gordon to work out why */
308 if (acct_flags & ACB_WSTRUST) {
309 DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
310 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
314 return NT_STATUS_OK;
317 static NTSTATUS authsam_domain_group_filter(TALLOC_CTX *mem_ctx,
318 char **_filter)
320 char *filter = NULL;
322 *_filter = NULL;
324 filter = talloc_strdup(mem_ctx, "(&(objectClass=group)");
327 * Skip all builtin groups, they're added later.
329 talloc_asprintf_addbuf(&filter,
330 "(!(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
331 GROUP_TYPE_BUILTIN_LOCAL_GROUP);
333 * Only include security groups.
335 talloc_asprintf_addbuf(&filter,
336 "(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
337 GROUP_TYPE_SECURITY_ENABLED);
338 if (filter == NULL) {
339 return NT_STATUS_NO_MEMORY;
342 *_filter = filter;
343 return NT_STATUS_OK;
346 _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
347 struct ldb_context *sam_ctx,
348 const char *netbios_name,
349 const char *domain_name,
350 const char *dns_domain_name,
351 struct ldb_dn *domain_dn,
352 const struct ldb_message *msg,
353 DATA_BLOB user_sess_key,
354 DATA_BLOB lm_sess_key,
355 struct auth_user_info_dc **_user_info_dc)
357 NTSTATUS status;
358 int ret;
359 struct auth_user_info_dc *user_info_dc;
360 struct auth_user_info *info;
361 const char *str = NULL;
362 char *filter = NULL;
363 /* SIDs for the account and his primary group */
364 struct dom_sid *account_sid;
365 struct dom_sid_buf buf;
366 const char *primary_group_dn_str = NULL;
367 DATA_BLOB primary_group_blob;
368 struct ldb_dn *primary_group_dn = NULL;
369 struct ldb_message *primary_group_msg = NULL;
370 unsigned primary_group_type;
371 /* SID structures for the expanded group memberships */
372 struct auth_SidAttr *sids = NULL;
373 uint32_t num_sids = 0;
374 unsigned int i;
375 struct dom_sid *domain_sid;
376 TALLOC_CTX *tmp_ctx;
377 struct ldb_message_element *el;
378 static const char * const group_type_attrs[] = { "groupType", NULL };
380 user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
381 NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
383 tmp_ctx = talloc_new(user_info_dc);
384 if (tmp_ctx == NULL) {
385 TALLOC_FREE(user_info_dc);
386 return NT_STATUS_NO_MEMORY;
390 * We'll typically store three SIDs: the SID of the user, the SID of the
391 * primary group, and a copy of the latter if it's not a resource
392 * group. Allocate enough memory for these three SIDs.
394 sids = talloc_zero_array(user_info_dc, struct auth_SidAttr, 3);
395 if (sids == NULL) {
396 TALLOC_FREE(user_info_dc);
397 return NT_STATUS_NO_MEMORY;
400 num_sids = 2;
402 account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
403 if (account_sid == NULL) {
404 TALLOC_FREE(user_info_dc);
405 return NT_STATUS_NO_MEMORY;
408 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
409 if (!NT_STATUS_IS_OK(status)) {
410 talloc_free(user_info_dc);
411 return status;
414 sids[PRIMARY_USER_SID_INDEX].sid = *account_sid;
415 sids[PRIMARY_USER_SID_INDEX].attrs = SE_GROUP_DEFAULT_FLAGS;
416 sids[PRIMARY_GROUP_SID_INDEX].sid = *domain_sid;
417 sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX].sid, ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
418 sids[PRIMARY_GROUP_SID_INDEX].attrs = SE_GROUP_DEFAULT_FLAGS;
421 * Filter out builtin groups from this token. We will search
422 * for builtin groups later, and not include them in the PAC
423 * or SamLogon validation info.
425 status = authsam_domain_group_filter(tmp_ctx, &filter);
426 if (!NT_STATUS_IS_OK(status)) {
427 TALLOC_FREE(user_info_dc);
428 return status;
431 primary_group_dn_str = talloc_asprintf(
432 tmp_ctx,
433 "<SID=%s>",
434 dom_sid_str_buf(&sids[PRIMARY_GROUP_SID_INDEX].sid, &buf));
435 if (primary_group_dn_str == NULL) {
436 TALLOC_FREE(user_info_dc);
437 return NT_STATUS_NO_MEMORY;
440 /* Get the DN of the primary group. */
441 primary_group_dn = ldb_dn_new(tmp_ctx, sam_ctx, primary_group_dn_str);
442 if (primary_group_dn == NULL) {
443 TALLOC_FREE(user_info_dc);
444 return NT_STATUS_NO_MEMORY;
448 * Do a search for the primary group, for the purpose of checking
449 * whether it's a resource group.
451 ret = dsdb_search_one(sam_ctx, tmp_ctx,
452 &primary_group_msg,
453 primary_group_dn,
454 LDB_SCOPE_BASE,
455 group_type_attrs,
457 NULL);
458 if (ret != LDB_SUCCESS) {
459 talloc_free(user_info_dc);
460 return NT_STATUS_INTERNAL_DB_CORRUPTION;
463 /* Check the type of the primary group. */
464 primary_group_type = ldb_msg_find_attr_as_uint(primary_group_msg, "groupType", 0);
465 if (primary_group_type & GROUP_TYPE_RESOURCE_GROUP) {
467 * If it's a resource group, we might as well indicate that in
468 * its attributes. At any rate, the primary group's attributes
469 * are unlikely to be used in the code, as there's nowhere to
470 * store them.
472 sids[PRIMARY_GROUP_SID_INDEX].attrs |= SE_GROUP_RESOURCE;
473 } else {
475 * The primary group is not a resource group. Make a copy of its
476 * SID to ensure it is added to the Base SIDs in the PAC.
478 sids[REMAINING_SIDS_INDEX] = sids[PRIMARY_GROUP_SID_INDEX];
479 ++num_sids;
482 primary_group_blob = data_blob_string_const(primary_group_dn_str);
484 /* Expands the primary group - this function takes in
485 * memberOf-like values, so we fake one up with the
486 * <SID=S-...> format of DN and then let it expand
487 * them, as long as they meet the filter - so only
488 * domain groups, not builtin groups
490 * The primary group is still treated specially, so we set the
491 * 'only childs' flag to true
493 status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
494 user_info_dc, &sids, &num_sids);
495 if (!NT_STATUS_IS_OK(status)) {
496 talloc_free(user_info_dc);
497 return status;
500 /* Expands the additional groups */
501 el = ldb_msg_find_element(msg, "memberOf");
502 for (i = 0; el && i < el->num_values; i++) {
503 /* This function takes in memberOf values and expands
504 * them, as long as they meet the filter - so only
505 * domain groups, not builtin groups */
506 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
507 user_info_dc, &sids, &num_sids);
508 if (!NT_STATUS_IS_OK(status)) {
509 talloc_free(user_info_dc);
510 return status;
514 user_info_dc->sids = sids;
515 user_info_dc->num_sids = num_sids;
517 user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
518 if (user_info_dc->info == NULL) {
519 talloc_free(user_info_dc);
520 return NT_STATUS_NO_MEMORY;
523 str = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
524 info->account_name = talloc_strdup(info, str);
525 if (info->account_name == NULL) {
526 TALLOC_FREE(user_info_dc);
527 return NT_STATUS_NO_MEMORY;
530 str = ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL);
531 if (str == NULL && dns_domain_name != NULL) {
532 info->user_principal_name = talloc_asprintf(info, "%s@%s",
533 info->account_name,
534 dns_domain_name);
535 if (info->user_principal_name == NULL) {
536 TALLOC_FREE(user_info_dc);
537 return NT_STATUS_NO_MEMORY;
539 info->user_principal_constructed = true;
540 } else if (str != NULL) {
541 info->user_principal_name = talloc_strdup(info, str);
542 if (info->user_principal_name == NULL) {
543 TALLOC_FREE(user_info_dc);
544 return NT_STATUS_NO_MEMORY;
548 info->domain_name = talloc_strdup(info, domain_name);
549 if (info->domain_name == NULL) {
550 TALLOC_FREE(user_info_dc);
551 return NT_STATUS_NO_MEMORY;
554 if (dns_domain_name != NULL) {
555 info->dns_domain_name = talloc_strdup(info, dns_domain_name);
556 if (info->dns_domain_name == NULL) {
557 TALLOC_FREE(user_info_dc);
558 return NT_STATUS_NO_MEMORY;
562 str = ldb_msg_find_attr_as_string(msg, "displayName", "");
563 info->full_name = talloc_strdup(info, str);
564 if (info->full_name == NULL) {
565 TALLOC_FREE(user_info_dc);
566 return NT_STATUS_NO_MEMORY;
569 str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
570 info->logon_script = talloc_strdup(info, str);
571 if (info->logon_script == NULL) {
572 TALLOC_FREE(user_info_dc);
573 return NT_STATUS_NO_MEMORY;
576 str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
577 info->profile_path = talloc_strdup(info, str);
578 if (info->profile_path == NULL) {
579 TALLOC_FREE(user_info_dc);
580 return NT_STATUS_NO_MEMORY;
583 str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
584 info->home_directory = talloc_strdup(info, str);
585 if (info->home_directory == NULL) {
586 TALLOC_FREE(user_info_dc);
587 return NT_STATUS_NO_MEMORY;
590 str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
591 info->home_drive = talloc_strdup(info, str);
592 if (info->home_drive == NULL) {
593 TALLOC_FREE(user_info_dc);
594 return NT_STATUS_NO_MEMORY;
597 info->logon_server = talloc_strdup(info, netbios_name);
598 if (info->logon_server == NULL) {
599 TALLOC_FREE(user_info_dc);
600 return NT_STATUS_NO_MEMORY;
603 info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
604 info->last_logoff = samdb_result_last_logoff(msg);
605 info->acct_expiry = samdb_result_account_expires(msg);
606 info->last_password_change = samdb_result_nttime(msg,
607 "pwdLastSet", 0);
608 info->allow_password_change
609 = samdb_result_allow_password_change(sam_ctx, mem_ctx,
610 domain_dn, msg, "pwdLastSet");
611 info->force_password_change = samdb_result_nttime(msg,
612 "msDS-UserPasswordExpiryTimeComputed", 0);
613 info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
614 info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
617 info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
619 user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
620 user_sess_key.data,
621 user_sess_key.length);
622 if (user_sess_key.data) {
623 if (user_info_dc->user_session_key.data == NULL) {
624 TALLOC_FREE(user_info_dc);
625 return NT_STATUS_NO_MEMORY;
628 user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
629 lm_sess_key.data,
630 lm_sess_key.length);
631 if (lm_sess_key.data) {
632 if (user_info_dc->lm_session_key.data == NULL) {
633 TALLOC_FREE(user_info_dc);
634 return NT_STATUS_NO_MEMORY;
638 if (info->acct_flags & ACB_SVRTRUST) {
639 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
640 PAC */
641 user_info_dc->sids = talloc_realloc(user_info_dc,
642 user_info_dc->sids,
643 struct auth_SidAttr,
644 user_info_dc->num_sids+1);
645 if (user_info_dc->sids == NULL) {
646 TALLOC_FREE(user_info_dc);
647 return NT_STATUS_NO_MEMORY;
649 user_info_dc->sids[user_info_dc->num_sids].sid = global_sid_Enterprise_DCs;
650 user_info_dc->sids[user_info_dc->num_sids].attrs = SE_GROUP_DEFAULT_FLAGS;
651 user_info_dc->num_sids++;
654 if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
655 (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
656 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
657 user_info_dc->sids = talloc_realloc(user_info_dc,
658 user_info_dc->sids,
659 struct auth_SidAttr,
660 user_info_dc->num_sids+1);
661 if (user_info_dc->sids == NULL) {
662 TALLOC_FREE(user_info_dc);
663 return NT_STATUS_NO_MEMORY;
665 user_info_dc->sids[user_info_dc->num_sids].sid = *domain_sid;
666 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids].sid,
667 DOMAIN_RID_ENTERPRISE_READONLY_DCS);
668 user_info_dc->sids[user_info_dc->num_sids].attrs = SE_GROUP_DEFAULT_FLAGS;
669 user_info_dc->num_sids++;
672 info->user_flags = 0;
674 talloc_free(tmp_ctx);
675 *_user_info_dc = user_info_dc;
677 return NT_STATUS_OK;
680 _PUBLIC_ NTSTATUS authsam_update_user_info_dc(TALLOC_CTX *mem_ctx,
681 struct ldb_context *sam_ctx,
682 struct auth_user_info_dc *user_info_dc)
684 char *filter = NULL;
685 NTSTATUS status;
686 uint32_t i;
687 uint32_t n = 0;
690 * This function exists to expand group memberships
691 * in the local domain (forest), as the token
692 * may come from a different domain.
696 * Filter out builtin groups from this token. We will search
697 * for builtin groups later.
699 status = authsam_domain_group_filter(mem_ctx, &filter);
700 if (!NT_STATUS_IS_OK(status)) {
701 return status;
705 * We loop only over the existing number of
706 * sids.
708 n = user_info_dc->num_sids;
709 for (i = 0; i < n; i++) {
710 struct dom_sid *sid = &user_info_dc->sids[i].sid;
711 struct dom_sid_buf sid_buf;
712 char dn_str[sizeof(sid_buf.buf)*2];
713 DATA_BLOB dn_blob = data_blob_null;
715 snprintf(dn_str,
716 sizeof(dn_str),
717 "<SID=%s>",
718 dom_sid_str_buf(sid, &sid_buf));
719 dn_blob = data_blob_string_const(dn_str);
722 * We already have the SID in the token, so set
723 * 'only childs' flag to true and add all
724 * groups which match the filter.
726 status = dsdb_expand_nested_groups(sam_ctx, &dn_blob,
727 true, filter,
728 user_info_dc,
729 &user_info_dc->sids,
730 &user_info_dc->num_sids);
731 if (!NT_STATUS_IS_OK(status)) {
732 return status;
736 return NT_STATUS_OK;
740 * Make a shallow copy of a talloc-allocated user_info_dc structure, holding a
741 * reference to each of the original fields.
743 NTSTATUS authsam_shallow_copy_user_info_dc(TALLOC_CTX *mem_ctx,
744 const struct auth_user_info_dc *user_info_dc_in,
745 struct auth_user_info_dc **user_info_dc_out)
747 struct auth_user_info_dc *user_info_dc = NULL;
748 NTSTATUS status = NT_STATUS_OK;
750 user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
751 if (user_info_dc == NULL) {
752 status = NT_STATUS_NO_MEMORY;
753 goto out;
756 *user_info_dc = *user_info_dc_in;
758 if (user_info_dc->info != NULL) {
759 if (talloc_reference(user_info_dc, user_info_dc->info) == NULL) {
760 status = NT_STATUS_NO_MEMORY;
761 goto out;
765 if (user_info_dc->user_session_key.data != NULL) {
766 if (talloc_reference(user_info_dc, user_info_dc->user_session_key.data) == NULL) {
767 status = NT_STATUS_NO_MEMORY;
768 goto out;
772 if (user_info_dc->lm_session_key.data != NULL) {
773 if (talloc_reference(user_info_dc, user_info_dc->lm_session_key.data) == NULL) {
774 status = NT_STATUS_NO_MEMORY;
775 goto out;
779 if (user_info_dc->sids != NULL) {
781 * Because we want to modify the SIDs in the user_info_dc
782 * structure, adding various well-known SIDs such as Asserted
783 * Identity or Claims Valid, make a copy of the SID array to
784 * guard against modification of the original.
786 * It’s better not to make a reference, because anything that
787 * tries to call talloc_realloc() on the original or the copy
788 * will fail when called for any referenced talloc context.
790 user_info_dc->sids = talloc_memdup(user_info_dc,
791 user_info_dc->sids,
792 talloc_get_size(user_info_dc->sids));
793 if (user_info_dc->sids == NULL) {
794 status = NT_STATUS_NO_MEMORY;
795 goto out;
799 *user_info_dc_out = user_info_dc;
800 user_info_dc = NULL;
802 out:
803 talloc_free(user_info_dc);
804 return status;
807 NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
808 TALLOC_CTX *mem_ctx, const char *principal,
809 const char **attrs,
810 struct ldb_dn **domain_dn,
811 struct ldb_message **msg)
813 struct ldb_dn *user_dn;
814 NTSTATUS nt_status;
815 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
816 int ret;
818 if (!tmp_ctx) {
819 return NT_STATUS_NO_MEMORY;
822 nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
823 &user_dn, domain_dn);
824 if (!NT_STATUS_IS_OK(nt_status)) {
825 talloc_free(tmp_ctx);
826 return nt_status;
829 /* pull the user attributes */
830 ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
831 LDB_SCOPE_BASE, attrs,
832 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
833 "(objectClass=*)");
834 if (ret != LDB_SUCCESS) {
835 talloc_free(tmp_ctx);
836 return NT_STATUS_INTERNAL_DB_CORRUPTION;
838 talloc_steal(mem_ctx, *msg);
839 talloc_steal(mem_ctx, *domain_dn);
840 talloc_free(tmp_ctx);
842 return NT_STATUS_OK;
845 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
847 Supply either a principal or a DN
849 NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
850 struct loadparm_context *lp_ctx,
851 struct ldb_context *sam_ctx,
852 const char *principal,
853 struct ldb_dn *user_dn,
854 struct auth_user_info_dc **user_info_dc)
856 NTSTATUS nt_status;
857 DATA_BLOB user_sess_key = data_blob(NULL, 0);
858 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
860 struct ldb_message *msg;
861 struct ldb_dn *domain_dn;
863 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
864 if (!tmp_ctx) {
865 return NT_STATUS_NO_MEMORY;
868 if (principal) {
869 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
870 user_attrs, &domain_dn, &msg);
871 if (!NT_STATUS_IS_OK(nt_status)) {
872 talloc_free(tmp_ctx);
873 return nt_status;
875 } else if (user_dn) {
876 struct dom_sid *user_sid, *domain_sid;
877 int ret;
878 /* pull the user attributes */
879 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
880 LDB_SCOPE_BASE, user_attrs,
881 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
882 "(objectClass=*)");
883 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
884 talloc_free(tmp_ctx);
885 return NT_STATUS_NO_SUCH_USER;
886 } else if (ret != LDB_SUCCESS) {
887 talloc_free(tmp_ctx);
888 return NT_STATUS_INTERNAL_DB_CORRUPTION;
891 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
893 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
894 if (!NT_STATUS_IS_OK(nt_status)) {
895 return nt_status;
898 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
899 "(&(objectSid=%s)(objectClass=domain))",
900 ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
901 if (!domain_dn) {
902 struct dom_sid_buf buf;
903 DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
904 dom_sid_str_buf(domain_sid, &buf)));
905 return NT_STATUS_NO_SUCH_USER;
908 } else {
909 return NT_STATUS_INVALID_PARAMETER;
912 nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
913 lpcfg_netbios_name(lp_ctx),
914 lpcfg_sam_name(lp_ctx),
915 lpcfg_sam_dnsname(lp_ctx),
916 domain_dn,
917 msg,
918 user_sess_key, lm_sess_key,
919 user_info_dc);
920 if (!NT_STATUS_IS_OK(nt_status)) {
921 talloc_free(tmp_ctx);
922 return nt_status;
925 talloc_steal(mem_ctx, *user_info_dc);
926 talloc_free(tmp_ctx);
928 return NT_STATUS_OK;
932 * Returns the details for the Password Settings Object (PSO), if one applies
933 * the user.
935 static int authsam_get_user_pso(struct ldb_context *sam_ctx,
936 TALLOC_CTX *mem_ctx,
937 struct ldb_message *user_msg,
938 struct ldb_message **pso_msg)
940 const char *attrs[] = { "msDS-LockoutThreshold",
941 "msDS-LockoutObservationWindow",
942 NULL };
943 struct ldb_dn *pso_dn = NULL;
944 struct ldb_result *res = NULL;
945 int ret;
947 /* check if the user has a PSO that applies to it */
948 pso_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, user_msg,
949 "msDS-ResultantPSO");
951 if (pso_dn != NULL) {
952 ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, pso_dn, attrs, 0);
953 if (ret != LDB_SUCCESS) {
954 return ret;
957 *pso_msg = res->msgs[0];
960 return LDB_SUCCESS;
964 * Re-read the bad password and successful logon data for a user.
966 * The DN in the passed user record should contain the "objectGUID" in case the
967 * object DN has changed.
969 NTSTATUS authsam_reread_user_logon_data(
970 struct ldb_context *sam_ctx,
971 TALLOC_CTX *mem_ctx,
972 const struct ldb_message *user_msg,
973 struct ldb_message **current)
975 const struct ldb_val *v = NULL;
976 struct ldb_result *res = NULL;
977 uint16_t acct_flags = 0;
978 const char *attr_name = "msDS-User-Account-Control-Computed";
980 int ret;
983 * Re-read the account details, using the GUID in case the DN
984 * is being changed (this is automatic in LDB because the
985 * original search also used DSDB_SEARCH_SHOW_EXTENDED_DN)
987 * We re read all the attributes in user_attrs, rather than using a
988 * subset to ensure that we can reuse existing validation code.
990 ret = dsdb_search_dn(sam_ctx,
991 mem_ctx,
992 &res,
993 user_msg->dn,
994 user_attrs,
995 DSDB_SEARCH_SHOW_EXTENDED_DN);
996 if (ret != LDB_SUCCESS) {
997 DBG_ERR("Unable to re-read account control data for %s\n",
998 ldb_dn_get_linearized(user_msg->dn));
999 return NT_STATUS_INTERNAL_ERROR;
1003 * Ensure the account has not been locked out by another request
1005 v = ldb_msg_find_ldb_val(res->msgs[0], attr_name);
1006 if (v == NULL || v->data == NULL) {
1007 DBG_ERR("No %s attribute for %s\n",
1008 attr_name,
1009 ldb_dn_get_linearized(user_msg->dn));
1010 TALLOC_FREE(res);
1011 return NT_STATUS_INTERNAL_ERROR;
1013 acct_flags = samdb_result_acct_flags(res->msgs[0], attr_name);
1014 if (acct_flags & ACB_AUTOLOCK) {
1015 DBG_WARNING(
1016 "Account for user %s was locked out.\n",
1017 ldb_dn_get_linearized(user_msg->dn));
1018 TALLOC_FREE(res);
1019 return NT_STATUS_ACCOUNT_LOCKED_OUT;
1021 *current = talloc_steal(mem_ctx, res->msgs[0]);
1022 TALLOC_FREE(res);
1023 return NT_STATUS_OK;
1026 static struct db_context *authsam_get_bad_password_db(
1027 TALLOC_CTX *mem_ctx,
1028 struct ldb_context *sam_ctx)
1030 struct loadparm_context *lp_ctx = NULL;
1031 const char *db_name = "bad_password";
1032 struct db_context *db_ctx = NULL;
1034 lp_ctx = ldb_get_opaque(sam_ctx, "loadparm");
1035 if (lp_ctx == NULL) {
1036 DBG_ERR("Unable to get loadparm_context\n");
1037 return NULL;
1040 db_ctx = cluster_db_tmp_open(mem_ctx, lp_ctx, db_name, TDB_DEFAULT);
1041 if (db_ctx == NULL) {
1042 DBG_ERR("Unable to open bad password attempts database\n");
1043 return NULL;
1045 return db_ctx;
1048 static NTSTATUS get_object_sid_as_tdb_data(
1049 TALLOC_CTX *mem_ctx,
1050 const struct ldb_message *msg,
1051 struct dom_sid_buf *buf,
1052 TDB_DATA *key)
1054 struct dom_sid *objectsid = NULL;
1057 * Convert the objectSID to a human readable form to
1058 * make debugging easier
1060 objectsid = samdb_result_dom_sid(mem_ctx, msg, "objectSID");
1061 if (objectsid == NULL) {
1062 DBG_ERR("Unable to extract objectSID\n");
1063 return NT_STATUS_INTERNAL_ERROR;
1065 dom_sid_str_buf(objectsid, buf);
1066 key->dptr = (unsigned char *)buf->buf;
1067 key->dsize = strlen(buf->buf);
1069 talloc_free(objectsid);
1071 return NT_STATUS_OK;
1075 * Add the users objectSID to the bad password attempt database
1076 * to indicate that last authentication failed due to a bad password
1078 static NTSTATUS authsam_set_bad_password_indicator(
1079 struct ldb_context *sam_ctx,
1080 TALLOC_CTX *mem_ctx,
1081 const struct ldb_message *msg)
1083 NTSTATUS status = NT_STATUS_OK;
1084 struct dom_sid_buf buf;
1085 TDB_DATA key = {0};
1086 TDB_DATA value = {0};
1087 struct db_context *db = NULL;
1089 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1090 if (ctx == NULL) {
1091 return NT_STATUS_NO_MEMORY;
1094 db = authsam_get_bad_password_db(ctx, sam_ctx);
1095 if (db == NULL) {
1096 status = NT_STATUS_INTERNAL_ERROR;
1097 goto exit;
1100 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1101 if (!NT_STATUS_IS_OK(status)) {
1102 goto exit;
1105 status = dbwrap_store(db, key, value, 0);
1106 if (!NT_STATUS_IS_OK(status)) {
1107 DBG_ERR("Unable to store bad password indicator\n");
1109 exit:
1110 talloc_free(ctx);
1111 return status;
1115 * see if the users objectSID is in the bad password attempt database
1117 static NTSTATUS authsam_check_bad_password_indicator(
1118 struct ldb_context *sam_ctx,
1119 TALLOC_CTX *mem_ctx,
1120 bool *exists,
1121 const struct ldb_message *msg)
1123 NTSTATUS status = NT_STATUS_OK;
1124 struct dom_sid_buf buf;
1125 TDB_DATA key = {0};
1126 struct db_context *db = NULL;
1128 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1129 if (ctx == NULL) {
1130 return NT_STATUS_NO_MEMORY;
1133 db = authsam_get_bad_password_db(ctx, sam_ctx);
1134 if (db == NULL) {
1135 status = NT_STATUS_INTERNAL_ERROR;
1136 goto exit;
1139 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1140 if (!NT_STATUS_IS_OK(status)) {
1141 goto exit;
1144 *exists = dbwrap_exists(db, key);
1145 exit:
1146 talloc_free(ctx);
1147 return status;
1151 * Remove the users objectSID to the bad password attempt database
1152 * to indicate that last authentication succeeded.
1154 static NTSTATUS authsam_clear_bad_password_indicator(
1155 struct ldb_context *sam_ctx,
1156 TALLOC_CTX *mem_ctx,
1157 const struct ldb_message *msg)
1159 NTSTATUS status = NT_STATUS_OK;
1160 struct dom_sid_buf buf;
1161 TDB_DATA key = {0};
1162 struct db_context *db = NULL;
1164 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1165 if (ctx == NULL) {
1166 return NT_STATUS_NO_MEMORY;
1169 db = authsam_get_bad_password_db(ctx, sam_ctx);
1170 if (db == NULL) {
1171 status = NT_STATUS_INTERNAL_ERROR;
1172 goto exit;
1175 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1176 if (!NT_STATUS_IS_OK(status)) {
1177 goto exit;
1180 status = dbwrap_delete(db, key);
1181 if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, status)) {
1183 * Ok there was no bad password indicator this is expected
1185 status = NT_STATUS_OK;
1187 if (NT_STATUS_IS_ERR(status)) {
1188 DBG_ERR("Unable to delete bad password indicator, %s %s\n",
1189 nt_errstr(status),
1190 get_friendly_nt_error_msg(status));
1192 exit:
1193 talloc_free(ctx);
1194 return status;
1197 NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
1198 struct ldb_message *msg,
1199 struct ldb_dn *domain_dn)
1201 const char *attrs[] = { "lockoutThreshold",
1202 "lockOutObservationWindow",
1203 "lockoutDuration",
1204 "pwdProperties",
1205 NULL };
1206 int ret;
1207 NTSTATUS status;
1208 struct ldb_result *domain_res;
1209 struct ldb_message *msg_mod = NULL;
1210 struct ldb_message *current = NULL;
1211 struct ldb_message *pso_msg = NULL;
1212 bool txn_active = false;
1213 TALLOC_CTX *mem_ctx;
1215 mem_ctx = talloc_new(msg);
1216 if (mem_ctx == NULL) {
1217 return NT_STATUS_NO_MEMORY;
1220 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
1221 if (ret != LDB_SUCCESS) {
1222 TALLOC_FREE(mem_ctx);
1223 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1226 ret = authsam_get_user_pso(sam_ctx, mem_ctx, msg, &pso_msg);
1227 if (ret != LDB_SUCCESS) {
1230 * fallback to using the domain defaults so that we still
1231 * record the bad password attempt
1233 DBG_ERR("Error (%d) checking PSO for %s\n",
1234 ret, ldb_dn_get_linearized(msg->dn));
1238 * To ensure that the bad password count is updated atomically,
1239 * we need to:
1240 * begin a transaction
1241 * re-read the account details,
1242 * using the <GUID= part of the DN
1243 * update the bad password count
1244 * commit the transaction.
1248 * Start a new transaction
1250 ret = ldb_transaction_start(sam_ctx);
1251 if (ret != LDB_SUCCESS) {
1252 status = NT_STATUS_INTERNAL_ERROR;
1253 goto error;
1255 txn_active = true;
1258 * Re-read the account details, using the GUID in case the DN
1259 * is being changed.
1261 status = authsam_reread_user_logon_data(
1262 sam_ctx, mem_ctx, msg, &current);
1263 if (!NT_STATUS_IS_OK(status)) {
1264 /* The re-read can return account locked out, as well
1265 * as an internal error
1267 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
1269 * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
1270 * the transaction. Again to avoid cluttering the
1271 * audit logs with spurious errors
1273 goto exit;
1275 goto error;
1279 * Update the bad password count and if required lock the account
1281 status = dsdb_update_bad_pwd_count(
1282 mem_ctx,
1283 sam_ctx,
1284 current,
1285 domain_res->msgs[0],
1286 pso_msg,
1287 &msg_mod);
1288 if (!NT_STATUS_IS_OK(status)) {
1289 status = NT_STATUS_INTERNAL_ERROR;
1290 goto error;
1294 * Write the data back to disk if required.
1296 if (msg_mod != NULL) {
1297 struct ldb_request *req;
1299 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1300 msg_mod,
1301 NULL,
1302 NULL,
1303 ldb_op_default_callback,
1304 NULL);
1305 if (ret != LDB_SUCCESS) {
1306 TALLOC_FREE(msg_mod);
1307 status = NT_STATUS_INTERNAL_ERROR;
1308 goto error;
1311 ret = ldb_request_add_control(req,
1312 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1313 false, NULL);
1314 if (ret != LDB_SUCCESS) {
1315 talloc_free(req);
1316 status = NT_STATUS_INTERNAL_ERROR;
1317 goto error;
1321 * As we're in a transaction, make the ldb request directly
1322 * to avoid the nested transaction that would result if we
1323 * called dsdb_autotransaction_request
1325 ret = ldb_request(sam_ctx, req);
1326 if (ret == LDB_SUCCESS) {
1327 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1329 talloc_free(req);
1330 if (ret != LDB_SUCCESS) {
1331 status = NT_STATUS_INTERNAL_ERROR;
1332 goto error;
1334 status = authsam_set_bad_password_indicator(
1335 sam_ctx, mem_ctx, msg);
1336 if (!NT_STATUS_IS_OK(status)) {
1337 goto error;
1341 * Note that we may not have updated the user record, but
1342 * committing the transaction in that case is still the correct
1343 * thing to do.
1344 * If the transaction was cancelled, this would be logged by
1345 * the dsdb audit log as a failure. When in fact it is expected
1346 * behaviour.
1348 exit:
1349 TALLOC_FREE(mem_ctx);
1350 ret = ldb_transaction_commit(sam_ctx);
1351 if (ret != LDB_SUCCESS) {
1352 DBG_ERR("Error (%d) %s, committing transaction,"
1353 " while updating bad password count"
1354 " for (%s)\n",
1355 ret,
1356 ldb_errstring(sam_ctx),
1357 ldb_dn_get_linearized(msg->dn));
1358 return NT_STATUS_INTERNAL_ERROR;
1360 return status;
1362 error:
1363 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
1364 "set lockoutTime on %s: %s\n",
1365 ldb_dn_get_linearized(msg->dn),
1366 ldb_errstring(sam_ctx) != NULL ?
1367 ldb_errstring(sam_ctx) :nt_errstr(status));
1368 if (txn_active) {
1369 ret = ldb_transaction_cancel(sam_ctx);
1370 if (ret != LDB_SUCCESS) {
1371 DBG_ERR("Error rolling back transaction,"
1372 " while updating bad password count"
1373 " on %s: %s\n",
1374 ldb_dn_get_linearized(msg->dn),
1375 ldb_errstring(sam_ctx));
1378 TALLOC_FREE(mem_ctx);
1379 return status;
1384 * msDS-LogonTimeSyncInterval is an int32_t number of days.
1386 * The docs say: "the initial update, after the domain functional
1387 * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
1388 * 14 days minus a random percentage of 5 days", but we aren't doing
1389 * that. The blogosphere seems to think that this randomised update
1390 * happens everytime, but [MS-ADA1] doesn't agree.
1392 * Dochelp referred us to the following blog post:
1393 * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
1395 * when msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
1396 * not changed.
1399 static NTSTATUS authsam_calculate_lastlogon_sync_interval(
1400 struct ldb_context *sam_ctx,
1401 TALLOC_CTX *ctx,
1402 struct ldb_dn *domain_dn,
1403 NTTIME *sync_interval_nt)
1405 static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
1406 NULL };
1407 int ret;
1408 struct ldb_result *domain_res = NULL;
1409 TALLOC_CTX *mem_ctx = NULL;
1410 uint32_t sync_interval;
1412 mem_ctx = talloc_new(ctx);
1413 if (mem_ctx == NULL) {
1414 return NT_STATUS_NO_MEMORY;
1417 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
1419 if (ret != LDB_SUCCESS || domain_res->count != 1) {
1420 TALLOC_FREE(mem_ctx);
1421 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1424 sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
1425 "msDS-LogonTimeSyncInterval",
1426 14);
1427 DEBUG(5, ("sync interval is %d\n", sync_interval));
1428 if (sync_interval >= 5){
1430 * Subtract "a random percentage of 5" days. Presumably this
1431 * percentage is between 0 and 100, and modulus is accurate
1432 * enough.
1434 uint32_t r = generate_random() % 6;
1435 sync_interval -= r;
1436 DBG_INFO("randomised sync interval is %d (-%d)\n", sync_interval, r);
1438 /* In the case where sync_interval < 5 there is no randomisation */
1441 * msDS-LogonTimeSyncInterval is an int32_t number of days,
1442 * while lastLogonTimestamp (to be updated) is in the 64 bit
1443 * 100ns NTTIME format so we must convert.
1445 *sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
1446 TALLOC_FREE(mem_ctx);
1447 return NT_STATUS_OK;
1452 * We only set lastLogonTimestamp if the current value is older than
1453 * now - msDS-LogonTimeSyncInterval days.
1455 * lastLogonTimestamp is in the 64 bit 100ns NTTIME format
1457 static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
1458 struct ldb_message *msg_mod,
1459 struct ldb_dn *domain_dn,
1460 NTTIME old_timestamp,
1461 NTTIME now,
1462 NTTIME sync_interval_nt)
1464 int ret;
1465 DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
1466 (long long int)old_timestamp,
1467 (long long int)(now - sync_interval_nt),
1468 (long long int)(old_timestamp - now + sync_interval_nt)));
1470 if (sync_interval_nt == 0){
1472 * Setting msDS-LogonTimeSyncInterval to zero is how you ask
1473 * that nothing happens here.
1475 return NT_STATUS_OK;
1477 if (old_timestamp > now){
1478 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
1479 (long long int)old_timestamp, (long long int)now));
1480 /* then what? */
1482 } else if (old_timestamp < now - sync_interval_nt){
1483 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
1484 (long long int)now));
1486 /* The time has come to update lastLogonTimestamp */
1487 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1488 "lastLogonTimestamp", now);
1490 if (ret != LDB_SUCCESS) {
1491 return NT_STATUS_NO_MEMORY;
1494 return NT_STATUS_OK;
1497 /****************************************************************************
1498 Look for the specified user in the sam, return ldb result structures
1499 ****************************************************************************/
1501 NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
1502 const char *account_name,
1503 struct ldb_dn *domain_dn,
1504 struct ldb_message **ret_msg)
1506 int ret;
1507 char *account_name_encoded = NULL;
1509 account_name_encoded = ldb_binary_encode_string(mem_ctx, account_name);
1510 if (account_name_encoded == NULL) {
1511 return NT_STATUS_NO_MEMORY;
1514 /* pull the user attributes */
1515 ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
1516 user_attrs,
1517 DSDB_SEARCH_SHOW_EXTENDED_DN,
1518 "(&(sAMAccountName=%s)(objectclass=user))",
1519 account_name_encoded);
1520 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1521 DEBUG(3,("authsam_search_account: Couldn't find user [%s] in samdb, under %s\n",
1522 account_name, ldb_dn_get_linearized(domain_dn)));
1523 return NT_STATUS_NO_SUCH_USER;
1525 if (ret != LDB_SUCCESS) {
1526 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1529 return NT_STATUS_OK;
1533 /* Reset the badPwdCount to zero and update the lastLogon time. */
1534 NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
1535 const struct ldb_message *msg,
1536 struct ldb_dn *domain_dn,
1537 bool interactive_or_kerberos,
1538 TALLOC_CTX *send_to_sam_mem_ctx,
1539 struct netr_SendToSamBase **send_to_sam)
1541 int ret;
1542 NTSTATUS status;
1543 int badPwdCount;
1544 int dbBadPwdCount;
1545 int64_t lockoutTime;
1546 struct ldb_message *msg_mod;
1547 TALLOC_CTX *mem_ctx;
1548 struct timeval tv_now;
1549 NTTIME now;
1550 NTTIME lastLogonTimestamp;
1551 int64_t lockOutObservationWindow;
1552 NTTIME sync_interval_nt = 0;
1553 bool am_rodc = false;
1554 bool txn_active = false;
1555 bool need_db_reread;
1557 mem_ctx = talloc_new(msg);
1558 if (mem_ctx == NULL) {
1559 return NT_STATUS_NO_MEMORY;
1563 * Any update of the last logon data, needs to be done inside a
1564 * transaction.
1565 * And the user data needs to be re-read, and the account re-checked
1566 * for lockout.
1568 * Howevver we have long-running transactions like replication
1569 * that could otherwise grind the system to a halt so we first
1570 * determine if *this* account has seen a bad password,
1571 * otherwise we only start a transaction if there was a need
1572 * (because a change was to be made).
1575 status = authsam_check_bad_password_indicator(
1576 sam_ctx, mem_ctx, &need_db_reread, msg);
1577 if (!NT_STATUS_IS_OK(status)) {
1578 return status;
1581 if (interactive_or_kerberos == false) {
1583 * Avoid calculating this twice, it reads the PSO. A
1584 * race on this is unimportant.
1586 lockOutObservationWindow
1587 = samdb_result_msds_LockoutObservationWindow(
1588 sam_ctx, mem_ctx, domain_dn, msg);
1591 ret = samdb_rodc(sam_ctx, &am_rodc);
1592 if (ret != LDB_SUCCESS) {
1593 status = NT_STATUS_INTERNAL_ERROR;
1594 goto error;
1597 if (!am_rodc) {
1599 * Avoid reading the main domain DN twice. A race on
1600 * this is unimportant.
1602 status = authsam_calculate_lastlogon_sync_interval(
1603 sam_ctx, mem_ctx, domain_dn, &sync_interval_nt);
1605 if (!NT_STATUS_IS_OK(status)) {
1606 status = NT_STATUS_INTERNAL_ERROR;
1607 goto error;
1611 get_transaction:
1613 if (need_db_reread) {
1614 struct ldb_message *current = NULL;
1617 * Start a new transaction
1619 ret = ldb_transaction_start(sam_ctx);
1620 if (ret != LDB_SUCCESS) {
1621 status = NT_STATUS_INTERNAL_ERROR;
1622 goto error;
1625 txn_active = true;
1628 * Re-read the account details, using the GUID
1629 * embedded in DN so this is safe against a race where
1630 * it is being renamed.
1632 status = authsam_reread_user_logon_data(
1633 sam_ctx, mem_ctx, msg, &current);
1634 if (!NT_STATUS_IS_OK(status)) {
1636 * The re-read can return account locked out, as well
1637 * as an internal error
1639 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
1641 * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
1642 * the transaction. Again to avoid cluttering the
1643 * audit logs with spurious errors
1645 goto exit;
1647 goto error;
1649 msg = current;
1652 lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
1653 dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
1654 tv_now = timeval_current();
1655 now = timeval_to_nttime(&tv_now);
1657 if (interactive_or_kerberos) {
1658 badPwdCount = dbBadPwdCount;
1659 } else {
1661 * We get lockOutObservationWindow above, before the
1662 * transaction
1664 badPwdCount = dsdb_effective_badPwdCount(
1665 msg, lockOutObservationWindow, now);
1667 lastLogonTimestamp =
1668 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
1670 DEBUG(5, ("lastLogonTimestamp is %lld\n",
1671 (long long int)lastLogonTimestamp));
1673 msg_mod = ldb_msg_new(mem_ctx);
1674 if (msg_mod == NULL) {
1675 status = NT_STATUS_NO_MEMORY;
1676 goto error;
1680 * By using the DN from msg->dn directly, we allow LDB to
1681 * prefer the embedded GUID form, so this is actually quite
1682 * safe even in the case where DN has been changed
1684 msg_mod->dn = msg->dn;
1686 if (lockoutTime != 0) {
1688 * This implies "badPwdCount" = 0, see samldb_lockout_time()
1690 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
1691 if (ret != LDB_SUCCESS) {
1692 status = NT_STATUS_NO_MEMORY;
1693 goto error;
1695 } else if (badPwdCount != 0) {
1696 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
1697 if (ret != LDB_SUCCESS) {
1698 status = NT_STATUS_NO_MEMORY;
1699 goto error;
1703 if (interactive_or_kerberos ||
1704 (badPwdCount != 0 && lockoutTime == 0)) {
1705 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1706 "lastLogon", now);
1707 if (ret != LDB_SUCCESS) {
1708 status = NT_STATUS_NO_MEMORY;
1709 goto error;
1713 if (interactive_or_kerberos) {
1714 int logonCount;
1716 logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
1718 logonCount += 1;
1720 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1721 "logonCount", logonCount);
1722 if (ret != LDB_SUCCESS) {
1723 status = NT_STATUS_NO_MEMORY;
1724 goto error;
1726 } else {
1727 /* Set an unset logonCount to 0 on first successful login */
1728 if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
1729 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1730 "logonCount", 0);
1731 if (ret != LDB_SUCCESS) {
1732 TALLOC_FREE(mem_ctx);
1733 return NT_STATUS_NO_MEMORY;
1738 if (!am_rodc) {
1739 status = authsam_update_lastlogon_timestamp(
1740 sam_ctx,
1741 msg_mod,
1742 domain_dn,
1743 lastLogonTimestamp,
1744 now,
1745 sync_interval_nt);
1746 if (!NT_STATUS_IS_OK(status)) {
1747 status = NT_STATUS_NO_MEMORY;
1748 goto error;
1750 } else {
1751 /* Perform the (async) SendToSAM calls for MS-SAMS */
1752 if (dbBadPwdCount != 0 && send_to_sam != NULL) {
1753 struct netr_SendToSamBase *base_msg;
1754 struct GUID guid = samdb_result_guid(msg, "objectGUID");
1756 base_msg = talloc_zero(send_to_sam_mem_ctx,
1757 struct netr_SendToSamBase);
1758 if (base_msg == NULL) {
1759 status = NT_STATUS_NO_MEMORY;
1760 goto error;
1763 base_msg->message_type = SendToSamResetBadPasswordCount;
1764 base_msg->message_size = 16;
1765 base_msg->message.reset_bad_password.guid = guid;
1766 *send_to_sam = base_msg;
1770 if (msg_mod->num_elements > 0) {
1771 unsigned int i;
1772 struct ldb_request *req;
1775 * If it turns out we are going to update the DB, go
1776 * back to the start, get a transaction and the
1777 * current DB state and try again
1779 if (txn_active == false) {
1780 need_db_reread = true;
1781 goto get_transaction;
1784 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
1785 for (i=0;i<msg_mod->num_elements;i++) {
1786 msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1789 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1790 msg_mod,
1791 NULL,
1792 NULL,
1793 ldb_op_default_callback,
1794 NULL);
1795 if (ret != LDB_SUCCESS) {
1796 status = NT_STATUS_INTERNAL_ERROR;
1797 goto error;
1800 ret = ldb_request_add_control(req,
1801 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1802 false, NULL);
1803 if (ret != LDB_SUCCESS) {
1804 TALLOC_FREE(req);
1805 status = NT_STATUS_INTERNAL_ERROR;
1806 goto error;
1809 * As we're in a transaction, make the ldb request directly
1810 * to avoid the nested transaction that would result if we
1811 * called dsdb_autotransaction_request
1813 ret = ldb_request(sam_ctx, req);
1814 if (ret == LDB_SUCCESS) {
1815 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1817 TALLOC_FREE(req);
1818 if (ret != LDB_SUCCESS) {
1819 status = NT_STATUS_INTERNAL_ERROR;
1820 goto error;
1823 status = authsam_clear_bad_password_indicator(sam_ctx, mem_ctx, msg);
1824 if (!NT_STATUS_IS_OK(status)) {
1825 goto error;
1829 * Note that we may not have updated the user record, but
1830 * committing the transaction in that case is still the correct
1831 * thing to do.
1832 * If the transaction was cancelled, this would be logged by
1833 * the dsdb audit log as a failure. When in fact it is expected
1834 * behaviour.
1836 * Thankfully both TDB and LMDB seem to optimise for the empty
1837 * transaction case
1839 exit:
1840 TALLOC_FREE(mem_ctx);
1842 if (txn_active == false) {
1843 return status;
1846 ret = ldb_transaction_commit(sam_ctx);
1847 if (ret != LDB_SUCCESS) {
1848 DBG_ERR("Error (%d) %s, committing transaction,"
1849 " while updating successful logon accounting"
1850 " for (%s)\n",
1851 ret,
1852 ldb_errstring(sam_ctx),
1853 ldb_dn_get_linearized(msg->dn));
1854 return NT_STATUS_INTERNAL_ERROR;
1856 return status;
1858 error:
1859 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
1860 "set lockoutTime on %s: %s\n",
1861 ldb_dn_get_linearized(msg->dn),
1862 ldb_errstring(sam_ctx) != NULL ?
1863 ldb_errstring(sam_ctx) :nt_errstr(status));
1864 if (txn_active) {
1865 ret = ldb_transaction_cancel(sam_ctx);
1866 if (ret != LDB_SUCCESS) {
1867 DBG_ERR("Error rolling back transaction,"
1868 " while updating bad password count"
1869 " on %s: %s\n",
1870 ldb_dn_get_linearized(msg->dn),
1871 ldb_errstring(sam_ctx));
1874 TALLOC_FREE(mem_ctx);
1875 return status;