s3:winbindd: make use of ads_connect_cldap_only() in dcip_check_name_ads()
[Samba.git] / source4 / auth / sam.c
blobbd8219d733525d42b49e1740f4b0e528023486d1
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 /* Required for Group Managed Service Accounts. */ \
68 "msDS-ManagedPasswordId", \
69 "msDS-ManagedPasswordInterval", \
70 "whenCreated"
72 #define AUTHN_POLICY_ATTRS \
73 /* Required for authentication policies / silos */ \
74 "msDS-AssignedAuthNPolicy", \
75 "msDS-AssignedAuthNPolicySilo"
77 const char *krbtgt_attrs[] = {
79 * Authentication policies will not be enforced on the TGS
80 * account. Don’t include the relevant attributes in the account search.
82 KRBTGT_ATTRS, NULL
85 const char *server_attrs[] = {
86 KRBTGT_ATTRS,
87 AUTHN_POLICY_ATTRS,
88 NULL
91 const char *user_attrs[] = {
93 * This ordering (having msDS-ResultantPSO first) is
94 * important. By processing this attribute first it is
95 * available in the operational module for the other PSO
96 * attribute calculations to use.
98 "msDS-ResultantPSO",
100 KRBTGT_ATTRS,
101 AUTHN_POLICY_ATTRS,
103 "logonHours",
106 * To allow us to zero the badPwdCount and lockoutTime on
107 * successful logon, without database churn
109 "lockoutTime",
112 * Needed for SendToSAM requests
114 "objectGUID",
116 /* check 'allowed workstations' */
117 "userWorkstations",
119 /* required for user_info_dc, not access control: */
120 "displayName",
121 "scriptPath",
122 "profilePath",
123 "homeDirectory",
124 "homeDrive",
125 "lastLogon",
126 "lastLogonTimestamp",
127 "lastLogoff",
128 "accountExpires",
129 "badPwdCount",
130 "logonCount",
131 "primaryGroupID",
132 "memberOf",
133 "badPasswordTime",
134 "lmPwdHistory",
135 "ntPwdHistory",
136 NULL,
139 /****************************************************************************
140 Check if a user is allowed to logon at this time. Note this is the
141 servers local time, as logon hours are just specified as a weekly
142 bitmask.
143 ****************************************************************************/
145 static bool logon_hours_ok(struct ldb_message *msg, const char *name_for_logs)
147 /* In logon hours first bit is Sunday from 12AM to 1AM */
148 const struct ldb_val *hours;
149 struct tm *utctime;
150 time_t lasttime;
151 const char *asct;
152 uint8_t bitmask, bitpos;
154 hours = ldb_msg_find_ldb_val(msg, "logonHours");
155 if (!hours) {
156 DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n", name_for_logs));
157 return true;
160 if (hours->length != 168/8) {
161 DEBUG(5,("logon_hours_ok: malformed logon hours restrictions for user %s\n", name_for_logs));
162 return true;
165 lasttime = time(NULL);
166 utctime = gmtime(&lasttime);
167 if (!utctime) {
168 DEBUG(1, ("logon_hours_ok: failed to get gmtime. Failing logon for user %s\n",
169 name_for_logs));
170 return false;
173 /* find the corresponding byte and bit */
174 bitpos = (utctime->tm_wday * 24 + utctime->tm_hour) % 168;
175 bitmask = 1 << (bitpos % 8);
177 if (! (hours->data[bitpos/8] & bitmask)) {
178 struct tm *t = localtime(&lasttime);
179 if (!t) {
180 asct = "INVALID TIME";
181 } else {
182 asct = asctime(t);
183 if (!asct) {
184 asct = "INVALID TIME";
188 DEBUG(1, ("logon_hours_ok: Account for user %s not allowed to "
189 "logon at this time (%s).\n",
190 name_for_logs, asct ));
191 return false;
194 asct = asctime(utctime);
195 DEBUG(5,("logon_hours_ok: user %s allowed to logon at this time (%s)\n",
196 name_for_logs, asct ? asct : "UNKNOWN TIME" ));
198 return true;
201 /****************************************************************************
202 Do a specific test for a SAM_ACCOUNT being valid for this connection
203 (ie not disabled, expired and the like).
204 ****************************************************************************/
205 _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
206 struct ldb_context *sam_ctx,
207 uint32_t logon_parameters,
208 struct ldb_dn *domain_dn,
209 struct ldb_message *msg,
210 const char *logon_workstation,
211 const char *name_for_logs,
212 bool allow_domain_trust,
213 bool password_change)
215 uint16_t acct_flags;
216 const char *workstation_list;
217 NTTIME acct_expiry;
218 NTTIME must_change_time;
219 struct timeval tv_now = timeval_current();
220 NTTIME now = timeval_to_nttime(&tv_now);
222 DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
224 acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
226 acct_expiry = samdb_result_account_expires(msg);
228 /* Check for when we must change this password, taking the
229 * userAccountControl flags into account */
230 must_change_time = samdb_result_nttime(msg,
231 "msDS-UserPasswordExpiryTimeComputed", 0);
233 workstation_list = ldb_msg_find_attr_as_string(msg, "userWorkstations", NULL);
235 /* Quit if the account was disabled. */
236 if (acct_flags & ACB_DISABLED) {
237 DEBUG(2,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
238 return NT_STATUS_ACCOUNT_DISABLED;
241 /* Quit if the account was locked out. */
242 if (acct_flags & ACB_AUTOLOCK) {
243 DEBUG(2,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
244 return NT_STATUS_ACCOUNT_LOCKED_OUT;
247 /* Test account expire time */
248 if (now > acct_expiry) {
249 DEBUG(2,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
250 DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
251 nt_time_string(mem_ctx, acct_expiry)));
252 return NT_STATUS_ACCOUNT_EXPIRED;
255 /* check for immediate expiry "must change at next logon" (but not if this is a password change request) */
256 if ((must_change_time == 0) && !password_change) {
257 DEBUG(2,("sam_account_ok: Account for user '%s' password must change!.\n",
258 name_for_logs));
259 return NT_STATUS_PASSWORD_MUST_CHANGE;
262 /* check for expired password (but not if this is a password change request) */
263 if ((must_change_time < now) && !password_change) {
264 DEBUG(2,("sam_account_ok: Account for user '%s' password expired!.\n",
265 name_for_logs));
266 DEBUG(2,("sam_account_ok: Password expired at '%s' unix time.\n",
267 nt_time_string(mem_ctx, must_change_time)));
268 return NT_STATUS_PASSWORD_EXPIRED;
271 /* Test workstation. Workstation list is comma separated. */
272 if (logon_workstation && workstation_list && *workstation_list) {
273 bool invalid_ws = true;
274 int i;
275 char **workstations = str_list_make(mem_ctx, workstation_list, ",");
277 for (i = 0; workstations && workstations[i]; i++) {
278 DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
279 workstations[i], logon_workstation));
281 if (strequal(workstations[i], logon_workstation)) {
282 invalid_ws = false;
283 break;
287 talloc_free(workstations);
289 if (invalid_ws) {
290 return NT_STATUS_INVALID_WORKSTATION;
294 if (!logon_hours_ok(msg, name_for_logs)) {
295 return NT_STATUS_INVALID_LOGON_HOURS;
298 if (!allow_domain_trust) {
299 if (acct_flags & ACB_DOMTRUST) {
300 DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
301 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
304 if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
305 if (acct_flags & ACB_SVRTRUST) {
306 DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
307 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
310 if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
311 /* TODO: this fails with current solaris client. We
312 need to work with Gordon to work out why */
313 if (acct_flags & ACB_WSTRUST) {
314 DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
315 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
319 return NT_STATUS_OK;
322 static NTSTATUS authsam_domain_group_filter(TALLOC_CTX *mem_ctx,
323 char **_filter)
325 char *filter = NULL;
327 *_filter = NULL;
329 filter = talloc_strdup(mem_ctx, "(&(objectClass=group)");
330 if (filter == NULL) {
331 return NT_STATUS_NO_MEMORY;
335 * Skip all builtin groups, they're added later.
337 talloc_asprintf_addbuf(&filter,
338 "(!(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
339 GROUP_TYPE_BUILTIN_LOCAL_GROUP);
340 if (filter == NULL) {
341 return NT_STATUS_NO_MEMORY;
344 * Only include security groups.
346 talloc_asprintf_addbuf(&filter,
347 "(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
348 GROUP_TYPE_SECURITY_ENABLED);
349 if (filter == NULL) {
350 return NT_STATUS_NO_MEMORY;
353 *_filter = filter;
354 return NT_STATUS_OK;
357 _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
358 struct ldb_context *sam_ctx,
359 const char *netbios_name,
360 const char *domain_name,
361 const char *dns_domain_name,
362 struct ldb_dn *domain_dn,
363 const struct ldb_message *msg,
364 DATA_BLOB user_sess_key,
365 DATA_BLOB lm_sess_key,
366 struct auth_user_info_dc **_user_info_dc)
368 NTSTATUS status;
369 int ret;
370 struct auth_user_info_dc *user_info_dc;
371 struct auth_user_info *info;
372 const char *str = NULL;
373 char *filter = NULL;
374 /* SIDs for the account and his primary group */
375 struct dom_sid *account_sid;
376 struct dom_sid_buf buf;
377 const char *primary_group_dn_str = NULL;
378 DATA_BLOB primary_group_blob;
379 struct ldb_dn *primary_group_dn = NULL;
380 struct ldb_message *primary_group_msg = NULL;
381 unsigned primary_group_type;
382 /* SID structures for the expanded group memberships */
383 struct auth_SidAttr *sids = NULL;
384 uint32_t num_sids = 0;
385 unsigned int i;
386 struct dom_sid *domain_sid;
387 TALLOC_CTX *tmp_ctx;
388 struct ldb_message_element *el;
389 static const char * const group_type_attrs[] = { "groupType", NULL };
391 if (msg == NULL) {
392 return NT_STATUS_INVALID_PARAMETER;
395 user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
396 NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
398 tmp_ctx = talloc_new(user_info_dc);
399 if (tmp_ctx == NULL) {
400 TALLOC_FREE(user_info_dc);
401 return NT_STATUS_NO_MEMORY;
405 * We'll typically store three SIDs: the SID of the user, the SID of the
406 * primary group, and a copy of the latter if it's not a resource
407 * group. Allocate enough memory for these three SIDs.
409 sids = talloc_zero_array(user_info_dc, struct auth_SidAttr, 3);
410 if (sids == NULL) {
411 TALLOC_FREE(user_info_dc);
412 return NT_STATUS_NO_MEMORY;
415 num_sids = 2;
417 account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
418 if (account_sid == NULL) {
419 TALLOC_FREE(user_info_dc);
420 return NT_STATUS_NO_MEMORY;
423 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
424 if (!NT_STATUS_IS_OK(status)) {
425 talloc_free(user_info_dc);
426 return status;
429 sids[PRIMARY_USER_SID_INDEX].sid = *account_sid;
430 sids[PRIMARY_USER_SID_INDEX].attrs = SE_GROUP_DEFAULT_FLAGS;
431 sids[PRIMARY_GROUP_SID_INDEX].sid = *domain_sid;
432 sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX].sid, ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
433 sids[PRIMARY_GROUP_SID_INDEX].attrs = SE_GROUP_DEFAULT_FLAGS;
436 * Filter out builtin groups from this token. We will search
437 * for builtin groups later, and not include them in the PAC
438 * or SamLogon validation info.
440 status = authsam_domain_group_filter(tmp_ctx, &filter);
441 if (!NT_STATUS_IS_OK(status)) {
442 TALLOC_FREE(user_info_dc);
443 return status;
446 primary_group_dn_str = talloc_asprintf(
447 tmp_ctx,
448 "<SID=%s>",
449 dom_sid_str_buf(&sids[PRIMARY_GROUP_SID_INDEX].sid, &buf));
450 if (primary_group_dn_str == NULL) {
451 TALLOC_FREE(user_info_dc);
452 return NT_STATUS_NO_MEMORY;
455 /* Get the DN of the primary group. */
456 primary_group_dn = ldb_dn_new(tmp_ctx, sam_ctx, primary_group_dn_str);
457 if (primary_group_dn == NULL) {
458 TALLOC_FREE(user_info_dc);
459 return NT_STATUS_NO_MEMORY;
463 * Do a search for the primary group, for the purpose of checking
464 * whether it's a resource group.
466 ret = dsdb_search_one(sam_ctx, tmp_ctx,
467 &primary_group_msg,
468 primary_group_dn,
469 LDB_SCOPE_BASE,
470 group_type_attrs,
472 NULL);
473 if (ret != LDB_SUCCESS) {
474 talloc_free(user_info_dc);
475 return NT_STATUS_INTERNAL_DB_CORRUPTION;
478 /* Check the type of the primary group. */
479 primary_group_type = ldb_msg_find_attr_as_uint(primary_group_msg, "groupType", 0);
480 if (primary_group_type & GROUP_TYPE_RESOURCE_GROUP) {
482 * If it's a resource group, we might as well indicate that in
483 * its attributes. At any rate, the primary group's attributes
484 * are unlikely to be used in the code, as there's nowhere to
485 * store them.
487 sids[PRIMARY_GROUP_SID_INDEX].attrs |= SE_GROUP_RESOURCE;
488 } else {
490 * The primary group is not a resource group. Make a copy of its
491 * SID to ensure it is added to the Base SIDs in the PAC.
493 sids[REMAINING_SIDS_INDEX] = sids[PRIMARY_GROUP_SID_INDEX];
494 ++num_sids;
497 primary_group_blob = data_blob_string_const(primary_group_dn_str);
499 /* Expands the primary group - this function takes in
500 * memberOf-like values, so we fake one up with the
501 * <SID=S-...> format of DN and then let it expand
502 * them, as long as they meet the filter - so only
503 * domain groups, not builtin groups
505 * The primary group is still treated specially, so we set the
506 * 'only childs' flag to true
508 status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
509 user_info_dc, &sids, &num_sids);
510 if (!NT_STATUS_IS_OK(status)) {
511 talloc_free(user_info_dc);
512 return status;
515 /* Expands the additional groups */
516 el = ldb_msg_find_element(msg, "memberOf");
517 for (i = 0; el && i < el->num_values; i++) {
518 /* This function takes in memberOf values and expands
519 * them, as long as they meet the filter - so only
520 * domain groups, not builtin groups */
521 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
522 user_info_dc, &sids, &num_sids);
523 if (!NT_STATUS_IS_OK(status)) {
524 talloc_free(user_info_dc);
525 return status;
529 user_info_dc->sids = sids;
530 user_info_dc->num_sids = num_sids;
532 user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
533 if (user_info_dc->info == NULL) {
534 talloc_free(user_info_dc);
535 return NT_STATUS_NO_MEMORY;
538 str = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
539 info->account_name = talloc_strdup(info, str);
540 if (info->account_name == NULL) {
541 TALLOC_FREE(user_info_dc);
542 return NT_STATUS_NO_MEMORY;
545 str = ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL);
546 if (str == NULL && dns_domain_name != NULL) {
547 info->user_principal_name = talloc_asprintf(info, "%s@%s",
548 info->account_name,
549 dns_domain_name);
550 if (info->user_principal_name == NULL) {
551 TALLOC_FREE(user_info_dc);
552 return NT_STATUS_NO_MEMORY;
554 info->user_principal_constructed = true;
555 } else if (str != NULL) {
556 info->user_principal_name = talloc_strdup(info, str);
557 if (info->user_principal_name == NULL) {
558 TALLOC_FREE(user_info_dc);
559 return NT_STATUS_NO_MEMORY;
563 info->domain_name = talloc_strdup(info, domain_name);
564 if (info->domain_name == NULL) {
565 TALLOC_FREE(user_info_dc);
566 return NT_STATUS_NO_MEMORY;
569 if (dns_domain_name != NULL) {
570 info->dns_domain_name = talloc_strdup(info, dns_domain_name);
571 if (info->dns_domain_name == NULL) {
572 TALLOC_FREE(user_info_dc);
573 return NT_STATUS_NO_MEMORY;
577 str = ldb_msg_find_attr_as_string(msg, "displayName", "");
578 info->full_name = talloc_strdup(info, str);
579 if (info->full_name == NULL) {
580 TALLOC_FREE(user_info_dc);
581 return NT_STATUS_NO_MEMORY;
584 str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
585 info->logon_script = talloc_strdup(info, str);
586 if (info->logon_script == NULL) {
587 TALLOC_FREE(user_info_dc);
588 return NT_STATUS_NO_MEMORY;
591 str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
592 info->profile_path = talloc_strdup(info, str);
593 if (info->profile_path == NULL) {
594 TALLOC_FREE(user_info_dc);
595 return NT_STATUS_NO_MEMORY;
598 str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
599 info->home_directory = talloc_strdup(info, str);
600 if (info->home_directory == NULL) {
601 TALLOC_FREE(user_info_dc);
602 return NT_STATUS_NO_MEMORY;
605 str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
606 info->home_drive = talloc_strdup(info, str);
607 if (info->home_drive == NULL) {
608 TALLOC_FREE(user_info_dc);
609 return NT_STATUS_NO_MEMORY;
612 info->logon_server = talloc_strdup(info, netbios_name);
613 if (info->logon_server == NULL) {
614 TALLOC_FREE(user_info_dc);
615 return NT_STATUS_NO_MEMORY;
618 info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
619 info->last_logoff = samdb_result_last_logoff(msg);
620 info->acct_expiry = samdb_result_account_expires(msg);
621 info->last_password_change = samdb_result_nttime(msg,
622 "pwdLastSet", 0);
623 info->allow_password_change
624 = samdb_result_allow_password_change(sam_ctx, mem_ctx,
625 domain_dn, msg, "pwdLastSet");
626 info->force_password_change = samdb_result_nttime(msg,
627 "msDS-UserPasswordExpiryTimeComputed", 0);
628 info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
629 info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
632 info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
634 user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
635 user_sess_key.data,
636 user_sess_key.length);
637 if (user_sess_key.data) {
638 if (user_info_dc->user_session_key.data == NULL) {
639 TALLOC_FREE(user_info_dc);
640 return NT_STATUS_NO_MEMORY;
643 user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
644 lm_sess_key.data,
645 lm_sess_key.length);
646 if (lm_sess_key.data) {
647 if (user_info_dc->lm_session_key.data == NULL) {
648 TALLOC_FREE(user_info_dc);
649 return NT_STATUS_NO_MEMORY;
653 if (info->acct_flags & ACB_SVRTRUST) {
654 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
655 PAC */
656 user_info_dc->sids = talloc_realloc(user_info_dc,
657 user_info_dc->sids,
658 struct auth_SidAttr,
659 user_info_dc->num_sids+1);
660 if (user_info_dc->sids == NULL) {
661 TALLOC_FREE(user_info_dc);
662 return NT_STATUS_NO_MEMORY;
664 user_info_dc->sids[user_info_dc->num_sids].sid = global_sid_Enterprise_DCs;
665 user_info_dc->sids[user_info_dc->num_sids].attrs = SE_GROUP_DEFAULT_FLAGS;
666 user_info_dc->num_sids++;
669 if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
670 (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
671 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
672 user_info_dc->sids = talloc_realloc(user_info_dc,
673 user_info_dc->sids,
674 struct auth_SidAttr,
675 user_info_dc->num_sids+1);
676 if (user_info_dc->sids == NULL) {
677 TALLOC_FREE(user_info_dc);
678 return NT_STATUS_NO_MEMORY;
680 user_info_dc->sids[user_info_dc->num_sids].sid = *domain_sid;
681 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids].sid,
682 DOMAIN_RID_ENTERPRISE_READONLY_DCS);
683 user_info_dc->sids[user_info_dc->num_sids].attrs = SE_GROUP_DEFAULT_FLAGS;
684 user_info_dc->num_sids++;
687 info->user_flags = 0;
689 talloc_free(tmp_ctx);
690 *_user_info_dc = user_info_dc;
692 return NT_STATUS_OK;
695 _PUBLIC_ NTSTATUS authsam_update_user_info_dc(TALLOC_CTX *mem_ctx,
696 struct ldb_context *sam_ctx,
697 struct auth_user_info_dc *user_info_dc)
699 char *filter = NULL;
700 NTSTATUS status;
701 uint32_t i;
702 uint32_t n = 0;
705 * This function exists to expand group memberships
706 * in the local domain (forest), as the token
707 * may come from a different domain.
711 * Filter out builtin groups from this token. We will search
712 * for builtin groups later.
714 status = authsam_domain_group_filter(mem_ctx, &filter);
715 if (!NT_STATUS_IS_OK(status)) {
716 return status;
720 * We loop only over the existing number of
721 * sids.
723 n = user_info_dc->num_sids;
724 for (i = 0; i < n; i++) {
725 struct dom_sid *sid = &user_info_dc->sids[i].sid;
726 struct dom_sid_buf sid_buf;
727 char dn_str[sizeof(sid_buf.buf)*2];
728 DATA_BLOB dn_blob = data_blob_null;
730 snprintf(dn_str,
731 sizeof(dn_str),
732 "<SID=%s>",
733 dom_sid_str_buf(sid, &sid_buf));
734 dn_blob = data_blob_string_const(dn_str);
737 * We already have the SID in the token, so set
738 * 'only childs' flag to true and add all
739 * groups which match the filter.
741 status = dsdb_expand_nested_groups(sam_ctx, &dn_blob,
742 true, filter,
743 user_info_dc,
744 &user_info_dc->sids,
745 &user_info_dc->num_sids);
746 if (!NT_STATUS_IS_OK(status)) {
747 talloc_free(filter);
748 return status;
752 talloc_free(filter);
753 return NT_STATUS_OK;
757 * Make a shallow copy of a talloc-allocated user_info_dc structure, holding a
758 * reference to each of the original fields.
760 NTSTATUS authsam_shallow_copy_user_info_dc(TALLOC_CTX *mem_ctx,
761 const struct auth_user_info_dc *user_info_dc_in,
762 struct auth_user_info_dc **user_info_dc_out)
764 struct auth_user_info_dc *user_info_dc = NULL;
765 NTSTATUS status = NT_STATUS_OK;
767 if (user_info_dc_in == NULL) {
768 return NT_STATUS_INVALID_PARAMETER;
771 if (user_info_dc_out == NULL) {
772 return NT_STATUS_INVALID_PARAMETER;
775 user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
776 if (user_info_dc == NULL) {
777 status = NT_STATUS_NO_MEMORY;
778 goto out;
781 *user_info_dc = *user_info_dc_in;
783 if (user_info_dc->info != NULL) {
784 if (talloc_reference(user_info_dc, user_info_dc->info) == NULL) {
785 status = NT_STATUS_NO_MEMORY;
786 goto out;
790 if (user_info_dc->user_session_key.data != NULL) {
791 if (talloc_reference(user_info_dc, user_info_dc->user_session_key.data) == NULL) {
792 status = NT_STATUS_NO_MEMORY;
793 goto out;
797 if (user_info_dc->lm_session_key.data != NULL) {
798 if (talloc_reference(user_info_dc, user_info_dc->lm_session_key.data) == NULL) {
799 status = NT_STATUS_NO_MEMORY;
800 goto out;
804 if (user_info_dc->sids != NULL) {
806 * Because we want to modify the SIDs in the user_info_dc
807 * structure, adding various well-known SIDs such as Asserted
808 * Identity or Claims Valid, make a copy of the SID array to
809 * guard against modification of the original.
811 * It’s better not to make a reference, because anything that
812 * tries to call talloc_realloc() on the original or the copy
813 * will fail when called for any referenced talloc context.
815 user_info_dc->sids = talloc_memdup(user_info_dc,
816 user_info_dc->sids,
817 talloc_get_size(user_info_dc->sids));
818 if (user_info_dc->sids == NULL) {
819 status = NT_STATUS_NO_MEMORY;
820 goto out;
824 *user_info_dc_out = user_info_dc;
825 user_info_dc = NULL;
827 out:
828 talloc_free(user_info_dc);
829 return status;
832 NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
833 TALLOC_CTX *mem_ctx, const char *principal,
834 const char **attrs,
835 const uint32_t dsdb_flags,
836 struct ldb_dn **domain_dn,
837 struct ldb_message **msg)
839 struct ldb_dn *user_dn;
840 NTSTATUS nt_status;
841 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
842 int ret;
844 if (!tmp_ctx) {
845 return NT_STATUS_NO_MEMORY;
848 nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
849 &user_dn, domain_dn);
850 if (!NT_STATUS_IS_OK(nt_status)) {
851 talloc_free(tmp_ctx);
852 return nt_status;
855 /* pull the user attributes */
856 ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
857 LDB_SCOPE_BASE, attrs,
858 dsdb_flags | DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
859 "(objectClass=*)");
860 if (ret != LDB_SUCCESS) {
861 talloc_free(tmp_ctx);
862 return NT_STATUS_INTERNAL_DB_CORRUPTION;
864 talloc_steal(mem_ctx, *msg);
865 talloc_steal(mem_ctx, *domain_dn);
866 talloc_free(tmp_ctx);
868 return NT_STATUS_OK;
871 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
873 Supply either a principal or a DN
875 NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
876 struct loadparm_context *lp_ctx,
877 struct ldb_context *sam_ctx,
878 const char *principal,
879 struct ldb_dn *user_dn,
880 struct auth_user_info_dc **user_info_dc)
882 NTSTATUS nt_status;
883 DATA_BLOB user_sess_key = data_blob(NULL, 0);
884 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
886 struct ldb_message *msg;
887 struct ldb_dn *domain_dn;
889 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
890 if (!tmp_ctx) {
891 return NT_STATUS_NO_MEMORY;
894 if (principal) {
895 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
896 user_attrs, DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS, &domain_dn, &msg);
897 if (!NT_STATUS_IS_OK(nt_status)) {
898 talloc_free(tmp_ctx);
899 return nt_status;
901 } else if (user_dn) {
902 struct dom_sid *user_sid, *domain_sid;
903 int ret;
904 /* pull the user attributes */
905 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
906 LDB_SCOPE_BASE, user_attrs,
907 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG | DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS,
908 "(objectClass=*)");
909 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
910 talloc_free(tmp_ctx);
911 return NT_STATUS_NO_SUCH_USER;
912 } else if (ret != LDB_SUCCESS) {
913 talloc_free(tmp_ctx);
914 return NT_STATUS_INTERNAL_DB_CORRUPTION;
917 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
919 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
920 if (!NT_STATUS_IS_OK(nt_status)) {
921 talloc_free(tmp_ctx);
922 return nt_status;
925 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
926 "(&(objectSid=%s)(objectClass=domain))",
927 ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
928 if (!domain_dn) {
929 struct dom_sid_buf buf;
930 DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
931 dom_sid_str_buf(domain_sid, &buf)));
932 talloc_free(tmp_ctx);
933 return NT_STATUS_NO_SUCH_USER;
936 } else {
937 talloc_free(tmp_ctx);
938 return NT_STATUS_INVALID_PARAMETER;
941 nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
942 lpcfg_netbios_name(lp_ctx),
943 lpcfg_sam_name(lp_ctx),
944 lpcfg_sam_dnsname(lp_ctx),
945 domain_dn,
946 msg,
947 user_sess_key, lm_sess_key,
948 user_info_dc);
949 if (!NT_STATUS_IS_OK(nt_status)) {
950 talloc_free(tmp_ctx);
951 return nt_status;
954 talloc_steal(mem_ctx, *user_info_dc);
955 talloc_free(tmp_ctx);
957 return NT_STATUS_OK;
961 * Returns the details for the Password Settings Object (PSO), if one applies
962 * the user.
964 static int authsam_get_user_pso(struct ldb_context *sam_ctx,
965 TALLOC_CTX *mem_ctx,
966 struct ldb_message *user_msg,
967 struct ldb_message **pso_msg)
969 const char *attrs[] = { "msDS-LockoutThreshold",
970 "msDS-LockoutObservationWindow",
971 NULL };
972 struct ldb_dn *pso_dn = NULL;
973 struct ldb_result *res = NULL;
974 int ret;
976 /* check if the user has a PSO that applies to it */
977 pso_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, user_msg,
978 "msDS-ResultantPSO");
980 if (pso_dn != NULL) {
981 ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, pso_dn, attrs, 0);
982 if (ret != LDB_SUCCESS) {
983 return ret;
986 *pso_msg = res->msgs[0];
989 return LDB_SUCCESS;
993 * Re-read the bad password and successful logon data for a user.
995 * The DN in the passed user record should contain the "objectGUID" in case the
996 * object DN has changed.
998 NTSTATUS authsam_reread_user_logon_data(
999 struct ldb_context *sam_ctx,
1000 TALLOC_CTX *mem_ctx,
1001 const struct ldb_message *user_msg,
1002 struct ldb_message **current)
1004 const struct ldb_val *v = NULL;
1005 struct ldb_result *res = NULL;
1006 uint16_t acct_flags = 0;
1007 const char *attr_name = "msDS-User-Account-Control-Computed";
1009 int ret;
1012 * Re-read the account details, using the GUID in case the DN
1013 * is being changed (this is automatic in LDB because the
1014 * original search also used DSDB_SEARCH_SHOW_EXTENDED_DN)
1016 * We re read all the attributes in user_attrs, rather than using a
1017 * subset to ensure that we can reuse existing validation code.
1019 ret = dsdb_search_dn(sam_ctx,
1020 mem_ctx,
1021 &res,
1022 user_msg->dn,
1023 user_attrs,
1024 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS);
1025 if (ret != LDB_SUCCESS) {
1026 DBG_ERR("Unable to re-read account control data for %s\n",
1027 ldb_dn_get_linearized(user_msg->dn));
1028 return NT_STATUS_INTERNAL_ERROR;
1032 * Ensure the account has not been locked out by another request
1034 v = ldb_msg_find_ldb_val(res->msgs[0], attr_name);
1035 if (v == NULL || v->data == NULL) {
1036 DBG_ERR("No %s attribute for %s\n",
1037 attr_name,
1038 ldb_dn_get_linearized(user_msg->dn));
1039 TALLOC_FREE(res);
1040 return NT_STATUS_INTERNAL_ERROR;
1042 acct_flags = samdb_result_acct_flags(res->msgs[0], attr_name);
1043 if (acct_flags & ACB_AUTOLOCK) {
1044 DBG_WARNING(
1045 "Account for user %s was locked out.\n",
1046 ldb_dn_get_linearized(user_msg->dn));
1047 TALLOC_FREE(res);
1048 return NT_STATUS_ACCOUNT_LOCKED_OUT;
1050 *current = talloc_steal(mem_ctx, res->msgs[0]);
1051 TALLOC_FREE(res);
1052 return NT_STATUS_OK;
1055 static struct db_context *authsam_get_bad_password_db(
1056 TALLOC_CTX *mem_ctx,
1057 struct ldb_context *sam_ctx)
1059 struct loadparm_context *lp_ctx = NULL;
1060 const char *db_name = "bad_password";
1061 struct db_context *db_ctx = NULL;
1063 lp_ctx = ldb_get_opaque(sam_ctx, "loadparm");
1064 if (lp_ctx == NULL) {
1065 DBG_ERR("Unable to get loadparm_context\n");
1066 return NULL;
1069 db_ctx = cluster_db_tmp_open(mem_ctx, lp_ctx, db_name, TDB_DEFAULT);
1070 if (db_ctx == NULL) {
1071 DBG_ERR("Unable to open bad password attempts database\n");
1072 return NULL;
1074 return db_ctx;
1077 static NTSTATUS get_object_sid_as_tdb_data(
1078 TALLOC_CTX *mem_ctx,
1079 const struct ldb_message *msg,
1080 struct dom_sid_buf *buf,
1081 TDB_DATA *key)
1083 struct dom_sid *objectsid = NULL;
1086 * Convert the objectSID to a human readable form to
1087 * make debugging easier
1089 objectsid = samdb_result_dom_sid(mem_ctx, msg, "objectSID");
1090 if (objectsid == NULL) {
1091 DBG_ERR("Unable to extract objectSID\n");
1092 return NT_STATUS_INTERNAL_ERROR;
1094 dom_sid_str_buf(objectsid, buf);
1095 key->dptr = (unsigned char *)buf->buf;
1096 key->dsize = strlen(buf->buf);
1098 talloc_free(objectsid);
1100 return NT_STATUS_OK;
1104 * Add the users objectSID to the bad password attempt database
1105 * to indicate that last authentication failed due to a bad password
1107 static NTSTATUS authsam_set_bad_password_indicator(
1108 struct ldb_context *sam_ctx,
1109 TALLOC_CTX *mem_ctx,
1110 const struct ldb_message *msg)
1112 NTSTATUS status = NT_STATUS_OK;
1113 struct dom_sid_buf buf;
1114 TDB_DATA key = {0};
1115 TDB_DATA value = {0};
1116 struct db_context *db = NULL;
1118 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1119 if (ctx == NULL) {
1120 return NT_STATUS_NO_MEMORY;
1123 db = authsam_get_bad_password_db(ctx, sam_ctx);
1124 if (db == NULL) {
1125 status = NT_STATUS_INTERNAL_ERROR;
1126 goto exit;
1129 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1130 if (!NT_STATUS_IS_OK(status)) {
1131 goto exit;
1134 status = dbwrap_store(db, key, value, 0);
1135 if (!NT_STATUS_IS_OK(status)) {
1136 DBG_ERR("Unable to store bad password indicator\n");
1138 exit:
1139 talloc_free(ctx);
1140 return status;
1144 * see if the users objectSID is in the bad password attempt database
1146 static NTSTATUS authsam_check_bad_password_indicator(
1147 struct ldb_context *sam_ctx,
1148 TALLOC_CTX *mem_ctx,
1149 bool *exists,
1150 const struct ldb_message *msg)
1152 NTSTATUS status = NT_STATUS_OK;
1153 struct dom_sid_buf buf;
1154 TDB_DATA key = {0};
1155 struct db_context *db = NULL;
1157 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1158 if (ctx == NULL) {
1159 return NT_STATUS_NO_MEMORY;
1162 db = authsam_get_bad_password_db(ctx, sam_ctx);
1163 if (db == NULL) {
1164 status = NT_STATUS_INTERNAL_ERROR;
1165 goto exit;
1168 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1169 if (!NT_STATUS_IS_OK(status)) {
1170 goto exit;
1173 *exists = dbwrap_exists(db, key);
1174 exit:
1175 talloc_free(ctx);
1176 return status;
1180 * Remove the users objectSID to the bad password attempt database
1181 * to indicate that last authentication succeeded.
1183 static NTSTATUS authsam_clear_bad_password_indicator(
1184 struct ldb_context *sam_ctx,
1185 TALLOC_CTX *mem_ctx,
1186 const struct ldb_message *msg)
1188 NTSTATUS status = NT_STATUS_OK;
1189 struct dom_sid_buf buf;
1190 TDB_DATA key = {0};
1191 struct db_context *db = NULL;
1193 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1194 if (ctx == NULL) {
1195 return NT_STATUS_NO_MEMORY;
1198 db = authsam_get_bad_password_db(ctx, sam_ctx);
1199 if (db == NULL) {
1200 status = NT_STATUS_INTERNAL_ERROR;
1201 goto exit;
1204 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1205 if (!NT_STATUS_IS_OK(status)) {
1206 goto exit;
1209 status = dbwrap_delete(db, key);
1210 if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, status)) {
1212 * Ok there was no bad password indicator this is expected
1214 status = NT_STATUS_OK;
1216 if (NT_STATUS_IS_ERR(status)) {
1217 DBG_ERR("Unable to delete bad password indicator, %s %s\n",
1218 nt_errstr(status),
1219 get_friendly_nt_error_msg(status));
1221 exit:
1222 talloc_free(ctx);
1223 return status;
1226 NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
1227 struct ldb_message *msg,
1228 struct ldb_dn *domain_dn)
1230 const char *attrs[] = { "lockoutThreshold",
1231 "lockOutObservationWindow",
1232 "lockoutDuration",
1233 "pwdProperties",
1234 NULL };
1235 int ret;
1236 NTSTATUS status;
1237 struct ldb_result *domain_res;
1238 struct ldb_message *msg_mod = NULL;
1239 struct ldb_message *current = NULL;
1240 struct ldb_message *pso_msg = NULL;
1241 bool txn_active = false;
1242 TALLOC_CTX *mem_ctx;
1244 mem_ctx = talloc_new(msg);
1245 if (mem_ctx == NULL) {
1246 return NT_STATUS_NO_MEMORY;
1249 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
1250 if (ret != LDB_SUCCESS) {
1251 TALLOC_FREE(mem_ctx);
1252 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1255 ret = authsam_get_user_pso(sam_ctx, mem_ctx, msg, &pso_msg);
1256 if (ret != LDB_SUCCESS) {
1259 * fallback to using the domain defaults so that we still
1260 * record the bad password attempt
1262 DBG_ERR("Error (%d) checking PSO for %s\n",
1263 ret, ldb_dn_get_linearized(msg->dn));
1267 * To ensure that the bad password count is updated atomically,
1268 * we need to:
1269 * begin a transaction
1270 * re-read the account details,
1271 * using the <GUID= part of the DN
1272 * update the bad password count
1273 * commit the transaction.
1277 * Start a new transaction
1279 ret = ldb_transaction_start(sam_ctx);
1280 if (ret != LDB_SUCCESS) {
1281 status = NT_STATUS_INTERNAL_ERROR;
1282 goto error;
1284 txn_active = true;
1287 * Re-read the account details, using the GUID in case the DN
1288 * is being changed.
1290 status = authsam_reread_user_logon_data(
1291 sam_ctx, mem_ctx, msg, &current);
1292 if (!NT_STATUS_IS_OK(status)) {
1293 /* The re-read can return account locked out, as well
1294 * as an internal error
1296 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
1298 * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
1299 * the transaction. Again to avoid cluttering the
1300 * audit logs with spurious errors
1302 goto exit;
1304 goto error;
1308 * Update the bad password count and if required lock the account
1310 status = dsdb_update_bad_pwd_count(
1311 mem_ctx,
1312 sam_ctx,
1313 current,
1314 domain_res->msgs[0],
1315 pso_msg,
1316 &msg_mod);
1317 if (!NT_STATUS_IS_OK(status)) {
1318 status = NT_STATUS_INTERNAL_ERROR;
1319 goto error;
1323 * Write the data back to disk if required.
1325 if (msg_mod != NULL) {
1326 struct ldb_request *req;
1328 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1329 msg_mod,
1330 NULL,
1331 NULL,
1332 ldb_op_default_callback,
1333 NULL);
1334 if (ret != LDB_SUCCESS) {
1335 TALLOC_FREE(msg_mod);
1336 status = NT_STATUS_INTERNAL_ERROR;
1337 goto error;
1340 ret = ldb_request_add_control(req,
1341 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1342 false, NULL);
1343 if (ret != LDB_SUCCESS) {
1344 talloc_free(req);
1345 status = NT_STATUS_INTERNAL_ERROR;
1346 goto error;
1350 * As we're in a transaction, make the ldb request directly
1351 * to avoid the nested transaction that would result if we
1352 * called dsdb_autotransaction_request
1354 ret = ldb_request(sam_ctx, req);
1355 if (ret == LDB_SUCCESS) {
1356 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1358 talloc_free(req);
1359 if (ret != LDB_SUCCESS) {
1360 status = NT_STATUS_INTERNAL_ERROR;
1361 goto error;
1363 status = authsam_set_bad_password_indicator(
1364 sam_ctx, mem_ctx, msg);
1365 if (!NT_STATUS_IS_OK(status)) {
1366 goto error;
1370 * Note that we may not have updated the user record, but
1371 * committing the transaction in that case is still the correct
1372 * thing to do.
1373 * If the transaction was cancelled, this would be logged by
1374 * the dsdb audit log as a failure. When in fact it is expected
1375 * behaviour.
1377 exit:
1378 TALLOC_FREE(mem_ctx);
1379 ret = ldb_transaction_commit(sam_ctx);
1380 if (ret != LDB_SUCCESS) {
1381 DBG_ERR("Error (%d) %s, committing transaction,"
1382 " while updating bad password count"
1383 " for (%s)\n",
1384 ret,
1385 ldb_errstring(sam_ctx),
1386 ldb_dn_get_linearized(msg->dn));
1387 return NT_STATUS_INTERNAL_ERROR;
1389 return status;
1391 error:
1392 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
1393 "set lockoutTime on %s: %s\n",
1394 ldb_dn_get_linearized(msg->dn),
1395 ldb_errstring(sam_ctx) != NULL ?
1396 ldb_errstring(sam_ctx) :nt_errstr(status));
1397 if (txn_active) {
1398 ret = ldb_transaction_cancel(sam_ctx);
1399 if (ret != LDB_SUCCESS) {
1400 DBG_ERR("Error rolling back transaction,"
1401 " while updating bad password count"
1402 " on %s: %s\n",
1403 ldb_dn_get_linearized(msg->dn),
1404 ldb_errstring(sam_ctx));
1407 TALLOC_FREE(mem_ctx);
1408 return status;
1413 * msDS-LogonTimeSyncInterval is an int32_t number of days.
1415 * The docs say: "the initial update, after the domain functional
1416 * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
1417 * 14 days minus a random percentage of 5 days", but we aren't doing
1418 * that. The blogosphere seems to think that this randomised update
1419 * happens every time, but [MS-ADA1] doesn't agree.
1421 * Dochelp referred us to the following blog post:
1422 * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
1424 * when msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
1425 * not changed.
1428 static NTSTATUS authsam_calculate_lastlogon_sync_interval(
1429 struct ldb_context *sam_ctx,
1430 TALLOC_CTX *ctx,
1431 struct ldb_dn *domain_dn,
1432 NTTIME *sync_interval_nt)
1434 static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
1435 NULL };
1436 int ret;
1437 struct ldb_result *domain_res = NULL;
1438 TALLOC_CTX *mem_ctx = NULL;
1439 uint32_t sync_interval;
1441 mem_ctx = talloc_new(ctx);
1442 if (mem_ctx == NULL) {
1443 return NT_STATUS_NO_MEMORY;
1446 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
1448 if (ret != LDB_SUCCESS || domain_res->count != 1) {
1449 TALLOC_FREE(mem_ctx);
1450 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1453 sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
1454 "msDS-LogonTimeSyncInterval",
1455 14);
1456 DEBUG(5, ("sync interval is %d\n", sync_interval));
1457 if (sync_interval >= 5){
1459 * Subtract "a random percentage of 5" days. Presumably this
1460 * percentage is between 0 and 100, and modulus is accurate
1461 * enough.
1463 uint32_t r = generate_random() % 6;
1464 sync_interval -= r;
1465 DBG_INFO("randomised sync interval is %d (-%d)\n", sync_interval, r);
1467 /* In the case where sync_interval < 5 there is no randomisation */
1470 * msDS-LogonTimeSyncInterval is an int32_t number of days,
1471 * while lastLogonTimestamp (to be updated) is in the 64 bit
1472 * 100ns NTTIME format so we must convert.
1474 *sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
1475 TALLOC_FREE(mem_ctx);
1476 return NT_STATUS_OK;
1481 * We only set lastLogonTimestamp if the current value is older than
1482 * now - msDS-LogonTimeSyncInterval days.
1484 * lastLogonTimestamp is in the 64 bit 100ns NTTIME format
1486 static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
1487 struct ldb_message *msg_mod,
1488 struct ldb_dn *domain_dn,
1489 NTTIME old_timestamp,
1490 NTTIME now,
1491 NTTIME sync_interval_nt)
1493 int ret;
1494 DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
1495 (long long int)old_timestamp,
1496 (long long int)(now - sync_interval_nt),
1497 (long long int)(old_timestamp - now + sync_interval_nt)));
1499 if (sync_interval_nt == 0){
1501 * Setting msDS-LogonTimeSyncInterval to zero is how you ask
1502 * that nothing happens here.
1504 return NT_STATUS_OK;
1506 if (old_timestamp > now){
1507 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
1508 (long long int)old_timestamp, (long long int)now));
1509 /* then what? */
1511 } else if (old_timestamp < now - sync_interval_nt){
1512 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
1513 (long long int)now));
1515 /* The time has come to update lastLogonTimestamp */
1516 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1517 "lastLogonTimestamp", now);
1519 if (ret != LDB_SUCCESS) {
1520 return NT_STATUS_NO_MEMORY;
1523 return NT_STATUS_OK;
1526 /****************************************************************************
1527 Look for the specified user in the sam, return ldb result structures
1528 ****************************************************************************/
1530 NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
1531 const char *account_name,
1532 struct ldb_dn *domain_dn,
1533 struct ldb_message **ret_msg)
1535 int ret;
1536 char *account_name_encoded = NULL;
1538 account_name_encoded = ldb_binary_encode_string(mem_ctx, account_name);
1539 if (account_name_encoded == NULL) {
1540 return NT_STATUS_NO_MEMORY;
1543 /* pull the user attributes */
1544 ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
1545 user_attrs,
1546 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS,
1547 "(&(sAMAccountName=%s)(objectclass=user))",
1548 account_name_encoded);
1549 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1550 DEBUG(3,("authsam_search_account: Couldn't find user [%s] in samdb, under %s\n",
1551 account_name, ldb_dn_get_linearized(domain_dn)));
1552 return NT_STATUS_NO_SUCH_USER;
1554 if (ret != LDB_SUCCESS) {
1555 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1558 return NT_STATUS_OK;
1562 /* Reset the badPwdCount to zero and update the lastLogon time. */
1563 NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
1564 const struct ldb_message *msg,
1565 struct ldb_dn *domain_dn,
1566 bool interactive_or_kerberos,
1567 TALLOC_CTX *send_to_sam_mem_ctx,
1568 struct netr_SendToSamBase **send_to_sam)
1570 int ret;
1571 NTSTATUS status;
1572 int badPwdCount;
1573 int dbBadPwdCount;
1574 int64_t lockoutTime;
1575 struct ldb_message *msg_mod;
1576 TALLOC_CTX *mem_ctx;
1577 struct timeval tv_now;
1578 NTTIME now;
1579 NTTIME lastLogonTimestamp;
1580 int64_t lockOutObservationWindow;
1581 NTTIME sync_interval_nt = 0;
1582 bool am_rodc = false;
1583 bool txn_active = false;
1584 bool need_db_reread = false;
1586 mem_ctx = talloc_new(msg);
1587 if (mem_ctx == NULL) {
1588 return NT_STATUS_NO_MEMORY;
1592 * Any update of the last logon data, needs to be done inside a
1593 * transaction.
1594 * And the user data needs to be re-read, and the account re-checked
1595 * for lockout.
1597 * However we have long-running transactions like replication
1598 * that could otherwise grind the system to a halt so we first
1599 * determine if *this* account has seen a bad password,
1600 * otherwise we only start a transaction if there was a need
1601 * (because a change was to be made).
1604 status = authsam_check_bad_password_indicator(
1605 sam_ctx, mem_ctx, &need_db_reread, msg);
1606 if (!NT_STATUS_IS_OK(status)) {
1607 TALLOC_FREE(mem_ctx);
1608 return status;
1611 if (interactive_or_kerberos == false) {
1613 * Avoid calculating this twice, it reads the PSO. A
1614 * race on this is unimportant.
1616 lockOutObservationWindow
1617 = samdb_result_msds_LockoutObservationWindow(
1618 sam_ctx, mem_ctx, domain_dn, msg);
1621 ret = samdb_rodc(sam_ctx, &am_rodc);
1622 if (ret != LDB_SUCCESS) {
1623 status = NT_STATUS_INTERNAL_ERROR;
1624 goto error;
1627 if (!am_rodc) {
1629 * Avoid reading the main domain DN twice. A race on
1630 * this is unimportant.
1632 status = authsam_calculate_lastlogon_sync_interval(
1633 sam_ctx, mem_ctx, domain_dn, &sync_interval_nt);
1635 if (!NT_STATUS_IS_OK(status)) {
1636 status = NT_STATUS_INTERNAL_ERROR;
1637 goto error;
1641 get_transaction:
1643 if (need_db_reread) {
1644 struct ldb_message *current = NULL;
1647 * Start a new transaction
1649 ret = ldb_transaction_start(sam_ctx);
1650 if (ret != LDB_SUCCESS) {
1651 status = NT_STATUS_INTERNAL_ERROR;
1652 goto error;
1655 txn_active = true;
1658 * Re-read the account details, using the GUID
1659 * embedded in DN so this is safe against a race where
1660 * it is being renamed.
1662 status = authsam_reread_user_logon_data(
1663 sam_ctx, mem_ctx, msg, &current);
1664 if (!NT_STATUS_IS_OK(status)) {
1666 * The re-read can return account locked out, as well
1667 * as an internal error
1669 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
1671 * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
1672 * the transaction. Again to avoid cluttering the
1673 * audit logs with spurious errors
1675 goto exit;
1677 goto error;
1679 msg = current;
1682 lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
1683 dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
1684 tv_now = timeval_current();
1685 now = timeval_to_nttime(&tv_now);
1687 if (interactive_or_kerberos) {
1688 badPwdCount = dbBadPwdCount;
1689 } else {
1691 * We get lockOutObservationWindow above, before the
1692 * transaction
1694 badPwdCount = dsdb_effective_badPwdCount(
1695 msg, lockOutObservationWindow, now);
1697 lastLogonTimestamp =
1698 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
1700 DEBUG(5, ("lastLogonTimestamp is %lld\n",
1701 (long long int)lastLogonTimestamp));
1703 msg_mod = ldb_msg_new(mem_ctx);
1704 if (msg_mod == NULL) {
1705 status = NT_STATUS_NO_MEMORY;
1706 goto error;
1710 * By using the DN from msg->dn directly, we allow LDB to
1711 * prefer the embedded GUID form, so this is actually quite
1712 * safe even in the case where DN has been changed
1714 msg_mod->dn = msg->dn;
1716 if (lockoutTime != 0) {
1718 * This implies "badPwdCount" = 0, see samldb_lockout_time()
1720 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
1721 if (ret != LDB_SUCCESS) {
1722 status = NT_STATUS_NO_MEMORY;
1723 goto error;
1725 } else if (badPwdCount != 0) {
1726 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
1727 if (ret != LDB_SUCCESS) {
1728 status = NT_STATUS_NO_MEMORY;
1729 goto error;
1733 if (interactive_or_kerberos ||
1734 (badPwdCount != 0 && lockoutTime == 0)) {
1735 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1736 "lastLogon", now);
1737 if (ret != LDB_SUCCESS) {
1738 status = NT_STATUS_NO_MEMORY;
1739 goto error;
1743 if (interactive_or_kerberos) {
1744 int logonCount;
1746 logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
1748 logonCount += 1;
1750 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1751 "logonCount", logonCount);
1752 if (ret != LDB_SUCCESS) {
1753 status = NT_STATUS_NO_MEMORY;
1754 goto error;
1756 } else {
1757 /* Set an unset logonCount to 0 on first successful login */
1758 if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
1759 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1760 "logonCount", 0);
1761 if (ret != LDB_SUCCESS) {
1762 TALLOC_FREE(mem_ctx);
1763 return NT_STATUS_NO_MEMORY;
1768 if (!am_rodc) {
1769 status = authsam_update_lastlogon_timestamp(
1770 sam_ctx,
1771 msg_mod,
1772 domain_dn,
1773 lastLogonTimestamp,
1774 now,
1775 sync_interval_nt);
1776 if (!NT_STATUS_IS_OK(status)) {
1777 status = NT_STATUS_NO_MEMORY;
1778 goto error;
1780 } else {
1781 /* Perform the (async) SendToSAM calls for MS-SAMS */
1782 if (dbBadPwdCount != 0 && send_to_sam != NULL) {
1783 struct netr_SendToSamBase *base_msg;
1784 struct GUID guid = samdb_result_guid(msg, "objectGUID");
1786 base_msg = talloc_zero(send_to_sam_mem_ctx,
1787 struct netr_SendToSamBase);
1788 if (base_msg == NULL) {
1789 status = NT_STATUS_NO_MEMORY;
1790 goto error;
1793 base_msg->message_type = SendToSamResetBadPasswordCount;
1794 base_msg->message_size = 16;
1795 base_msg->message.reset_bad_password.guid = guid;
1796 *send_to_sam = base_msg;
1800 if (msg_mod->num_elements > 0) {
1801 unsigned int i;
1802 struct ldb_request *req;
1805 * If it turns out we are going to update the DB, go
1806 * back to the start, get a transaction and the
1807 * current DB state and try again
1809 if (txn_active == false) {
1810 need_db_reread = true;
1811 goto get_transaction;
1814 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
1815 for (i=0;i<msg_mod->num_elements;i++) {
1816 msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1819 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1820 msg_mod,
1821 NULL,
1822 NULL,
1823 ldb_op_default_callback,
1824 NULL);
1825 if (ret != LDB_SUCCESS) {
1826 status = NT_STATUS_INTERNAL_ERROR;
1827 goto error;
1830 ret = ldb_request_add_control(req,
1831 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1832 false, NULL);
1833 if (ret != LDB_SUCCESS) {
1834 TALLOC_FREE(req);
1835 status = NT_STATUS_INTERNAL_ERROR;
1836 goto error;
1839 * As we're in a transaction, make the ldb request directly
1840 * to avoid the nested transaction that would result if we
1841 * called dsdb_autotransaction_request
1843 ret = ldb_request(sam_ctx, req);
1844 if (ret == LDB_SUCCESS) {
1845 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1847 TALLOC_FREE(req);
1848 if (ret != LDB_SUCCESS) {
1849 status = NT_STATUS_INTERNAL_ERROR;
1850 goto error;
1853 status = authsam_clear_bad_password_indicator(sam_ctx, mem_ctx, msg);
1854 if (!NT_STATUS_IS_OK(status)) {
1855 goto error;
1859 * Note that we may not have updated the user record, but
1860 * committing the transaction in that case is still the correct
1861 * thing to do.
1862 * If the transaction was cancelled, this would be logged by
1863 * the dsdb audit log as a failure. When in fact it is expected
1864 * behaviour.
1866 * Thankfully both TDB and LMDB seem to optimise for the empty
1867 * transaction case
1869 exit:
1870 TALLOC_FREE(mem_ctx);
1872 if (txn_active == false) {
1873 return status;
1876 ret = ldb_transaction_commit(sam_ctx);
1877 if (ret != LDB_SUCCESS) {
1878 DBG_ERR("Error (%d) %s, committing transaction,"
1879 " while updating successful logon accounting"
1880 " for (%s)\n",
1881 ret,
1882 ldb_errstring(sam_ctx),
1883 ldb_dn_get_linearized(msg->dn));
1884 return NT_STATUS_INTERNAL_ERROR;
1886 return status;
1888 error:
1889 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
1890 "set lockoutTime on %s: %s\n",
1891 ldb_dn_get_linearized(msg->dn),
1892 ldb_errstring(sam_ctx) != NULL ?
1893 ldb_errstring(sam_ctx) :nt_errstr(status));
1894 if (txn_active) {
1895 ret = ldb_transaction_cancel(sam_ctx);
1896 if (ret != LDB_SUCCESS) {
1897 DBG_ERR("Error rolling back transaction,"
1898 " while updating bad password count"
1899 " on %s: %s\n",
1900 ldb_dn_get_linearized(msg->dn),
1901 ldb_errstring(sam_ctx));
1904 TALLOC_FREE(mem_ctx);
1905 return status;