s3:utils: Handle the domain before username and password
[samba.git] / source4 / auth / sam.c
blob33956c16a4059df55e28633997f3660fc8f3a93b
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 calculations 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)");
325 if (filter == NULL) {
326 return NT_STATUS_NO_MEMORY;
330 * Skip all builtin groups, they're added later.
332 talloc_asprintf_addbuf(&filter,
333 "(!(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
334 GROUP_TYPE_BUILTIN_LOCAL_GROUP);
335 if (filter == NULL) {
336 return NT_STATUS_NO_MEMORY;
339 * Only include security groups.
341 talloc_asprintf_addbuf(&filter,
342 "(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
343 GROUP_TYPE_SECURITY_ENABLED);
344 if (filter == NULL) {
345 return NT_STATUS_NO_MEMORY;
348 *_filter = filter;
349 return NT_STATUS_OK;
352 _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
353 struct ldb_context *sam_ctx,
354 const char *netbios_name,
355 const char *domain_name,
356 const char *dns_domain_name,
357 struct ldb_dn *domain_dn,
358 const struct ldb_message *msg,
359 DATA_BLOB user_sess_key,
360 DATA_BLOB lm_sess_key,
361 struct auth_user_info_dc **_user_info_dc)
363 NTSTATUS status;
364 int ret;
365 struct auth_user_info_dc *user_info_dc;
366 struct auth_user_info *info;
367 const char *str = NULL;
368 char *filter = NULL;
369 /* SIDs for the account and his primary group */
370 struct dom_sid *account_sid;
371 struct dom_sid_buf buf;
372 const char *primary_group_dn_str = NULL;
373 DATA_BLOB primary_group_blob;
374 struct ldb_dn *primary_group_dn = NULL;
375 struct ldb_message *primary_group_msg = NULL;
376 unsigned primary_group_type;
377 /* SID structures for the expanded group memberships */
378 struct auth_SidAttr *sids = NULL;
379 uint32_t num_sids = 0;
380 unsigned int i;
381 struct dom_sid *domain_sid;
382 TALLOC_CTX *tmp_ctx;
383 struct ldb_message_element *el;
384 static const char * const group_type_attrs[] = { "groupType", NULL };
386 if (msg == NULL) {
387 return NT_STATUS_INVALID_PARAMETER;
390 user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
391 NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
393 tmp_ctx = talloc_new(user_info_dc);
394 if (tmp_ctx == NULL) {
395 TALLOC_FREE(user_info_dc);
396 return NT_STATUS_NO_MEMORY;
400 * We'll typically store three SIDs: the SID of the user, the SID of the
401 * primary group, and a copy of the latter if it's not a resource
402 * group. Allocate enough memory for these three SIDs.
404 sids = talloc_zero_array(user_info_dc, struct auth_SidAttr, 3);
405 if (sids == NULL) {
406 TALLOC_FREE(user_info_dc);
407 return NT_STATUS_NO_MEMORY;
410 num_sids = 2;
412 account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
413 if (account_sid == NULL) {
414 TALLOC_FREE(user_info_dc);
415 return NT_STATUS_NO_MEMORY;
418 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
419 if (!NT_STATUS_IS_OK(status)) {
420 talloc_free(user_info_dc);
421 return status;
424 sids[PRIMARY_USER_SID_INDEX].sid = *account_sid;
425 sids[PRIMARY_USER_SID_INDEX].attrs = SE_GROUP_DEFAULT_FLAGS;
426 sids[PRIMARY_GROUP_SID_INDEX].sid = *domain_sid;
427 sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX].sid, ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
428 sids[PRIMARY_GROUP_SID_INDEX].attrs = SE_GROUP_DEFAULT_FLAGS;
431 * Filter out builtin groups from this token. We will search
432 * for builtin groups later, and not include them in the PAC
433 * or SamLogon validation info.
435 status = authsam_domain_group_filter(tmp_ctx, &filter);
436 if (!NT_STATUS_IS_OK(status)) {
437 TALLOC_FREE(user_info_dc);
438 return status;
441 primary_group_dn_str = talloc_asprintf(
442 tmp_ctx,
443 "<SID=%s>",
444 dom_sid_str_buf(&sids[PRIMARY_GROUP_SID_INDEX].sid, &buf));
445 if (primary_group_dn_str == NULL) {
446 TALLOC_FREE(user_info_dc);
447 return NT_STATUS_NO_MEMORY;
450 /* Get the DN of the primary group. */
451 primary_group_dn = ldb_dn_new(tmp_ctx, sam_ctx, primary_group_dn_str);
452 if (primary_group_dn == NULL) {
453 TALLOC_FREE(user_info_dc);
454 return NT_STATUS_NO_MEMORY;
458 * Do a search for the primary group, for the purpose of checking
459 * whether it's a resource group.
461 ret = dsdb_search_one(sam_ctx, tmp_ctx,
462 &primary_group_msg,
463 primary_group_dn,
464 LDB_SCOPE_BASE,
465 group_type_attrs,
467 NULL);
468 if (ret != LDB_SUCCESS) {
469 talloc_free(user_info_dc);
470 return NT_STATUS_INTERNAL_DB_CORRUPTION;
473 /* Check the type of the primary group. */
474 primary_group_type = ldb_msg_find_attr_as_uint(primary_group_msg, "groupType", 0);
475 if (primary_group_type & GROUP_TYPE_RESOURCE_GROUP) {
477 * If it's a resource group, we might as well indicate that in
478 * its attributes. At any rate, the primary group's attributes
479 * are unlikely to be used in the code, as there's nowhere to
480 * store them.
482 sids[PRIMARY_GROUP_SID_INDEX].attrs |= SE_GROUP_RESOURCE;
483 } else {
485 * The primary group is not a resource group. Make a copy of its
486 * SID to ensure it is added to the Base SIDs in the PAC.
488 sids[REMAINING_SIDS_INDEX] = sids[PRIMARY_GROUP_SID_INDEX];
489 ++num_sids;
492 primary_group_blob = data_blob_string_const(primary_group_dn_str);
494 /* Expands the primary group - this function takes in
495 * memberOf-like values, so we fake one up with the
496 * <SID=S-...> format of DN and then let it expand
497 * them, as long as they meet the filter - so only
498 * domain groups, not builtin groups
500 * The primary group is still treated specially, so we set the
501 * 'only childs' flag to true
503 status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
504 user_info_dc, &sids, &num_sids);
505 if (!NT_STATUS_IS_OK(status)) {
506 talloc_free(user_info_dc);
507 return status;
510 /* Expands the additional groups */
511 el = ldb_msg_find_element(msg, "memberOf");
512 for (i = 0; el && i < el->num_values; i++) {
513 /* This function takes in memberOf values and expands
514 * them, as long as they meet the filter - so only
515 * domain groups, not builtin groups */
516 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
517 user_info_dc, &sids, &num_sids);
518 if (!NT_STATUS_IS_OK(status)) {
519 talloc_free(user_info_dc);
520 return status;
524 user_info_dc->sids = sids;
525 user_info_dc->num_sids = num_sids;
527 user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
528 if (user_info_dc->info == NULL) {
529 talloc_free(user_info_dc);
530 return NT_STATUS_NO_MEMORY;
533 str = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
534 info->account_name = talloc_strdup(info, str);
535 if (info->account_name == NULL) {
536 TALLOC_FREE(user_info_dc);
537 return NT_STATUS_NO_MEMORY;
540 str = ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL);
541 if (str == NULL && dns_domain_name != NULL) {
542 info->user_principal_name = talloc_asprintf(info, "%s@%s",
543 info->account_name,
544 dns_domain_name);
545 if (info->user_principal_name == NULL) {
546 TALLOC_FREE(user_info_dc);
547 return NT_STATUS_NO_MEMORY;
549 info->user_principal_constructed = true;
550 } else if (str != NULL) {
551 info->user_principal_name = talloc_strdup(info, str);
552 if (info->user_principal_name == NULL) {
553 TALLOC_FREE(user_info_dc);
554 return NT_STATUS_NO_MEMORY;
558 info->domain_name = talloc_strdup(info, domain_name);
559 if (info->domain_name == NULL) {
560 TALLOC_FREE(user_info_dc);
561 return NT_STATUS_NO_MEMORY;
564 if (dns_domain_name != NULL) {
565 info->dns_domain_name = talloc_strdup(info, dns_domain_name);
566 if (info->dns_domain_name == NULL) {
567 TALLOC_FREE(user_info_dc);
568 return NT_STATUS_NO_MEMORY;
572 str = ldb_msg_find_attr_as_string(msg, "displayName", "");
573 info->full_name = talloc_strdup(info, str);
574 if (info->full_name == NULL) {
575 TALLOC_FREE(user_info_dc);
576 return NT_STATUS_NO_MEMORY;
579 str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
580 info->logon_script = talloc_strdup(info, str);
581 if (info->logon_script == NULL) {
582 TALLOC_FREE(user_info_dc);
583 return NT_STATUS_NO_MEMORY;
586 str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
587 info->profile_path = talloc_strdup(info, str);
588 if (info->profile_path == NULL) {
589 TALLOC_FREE(user_info_dc);
590 return NT_STATUS_NO_MEMORY;
593 str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
594 info->home_directory = talloc_strdup(info, str);
595 if (info->home_directory == NULL) {
596 TALLOC_FREE(user_info_dc);
597 return NT_STATUS_NO_MEMORY;
600 str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
601 info->home_drive = talloc_strdup(info, str);
602 if (info->home_drive == NULL) {
603 TALLOC_FREE(user_info_dc);
604 return NT_STATUS_NO_MEMORY;
607 info->logon_server = talloc_strdup(info, netbios_name);
608 if (info->logon_server == NULL) {
609 TALLOC_FREE(user_info_dc);
610 return NT_STATUS_NO_MEMORY;
613 info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
614 info->last_logoff = samdb_result_last_logoff(msg);
615 info->acct_expiry = samdb_result_account_expires(msg);
616 info->last_password_change = samdb_result_nttime(msg,
617 "pwdLastSet", 0);
618 info->allow_password_change
619 = samdb_result_allow_password_change(sam_ctx, mem_ctx,
620 domain_dn, msg, "pwdLastSet");
621 info->force_password_change = samdb_result_nttime(msg,
622 "msDS-UserPasswordExpiryTimeComputed", 0);
623 info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
624 info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
627 info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
629 user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
630 user_sess_key.data,
631 user_sess_key.length);
632 if (user_sess_key.data) {
633 if (user_info_dc->user_session_key.data == NULL) {
634 TALLOC_FREE(user_info_dc);
635 return NT_STATUS_NO_MEMORY;
638 user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
639 lm_sess_key.data,
640 lm_sess_key.length);
641 if (lm_sess_key.data) {
642 if (user_info_dc->lm_session_key.data == NULL) {
643 TALLOC_FREE(user_info_dc);
644 return NT_STATUS_NO_MEMORY;
648 if (info->acct_flags & ACB_SVRTRUST) {
649 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
650 PAC */
651 user_info_dc->sids = talloc_realloc(user_info_dc,
652 user_info_dc->sids,
653 struct auth_SidAttr,
654 user_info_dc->num_sids+1);
655 if (user_info_dc->sids == NULL) {
656 TALLOC_FREE(user_info_dc);
657 return NT_STATUS_NO_MEMORY;
659 user_info_dc->sids[user_info_dc->num_sids].sid = global_sid_Enterprise_DCs;
660 user_info_dc->sids[user_info_dc->num_sids].attrs = SE_GROUP_DEFAULT_FLAGS;
661 user_info_dc->num_sids++;
664 if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
665 (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
666 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
667 user_info_dc->sids = talloc_realloc(user_info_dc,
668 user_info_dc->sids,
669 struct auth_SidAttr,
670 user_info_dc->num_sids+1);
671 if (user_info_dc->sids == NULL) {
672 TALLOC_FREE(user_info_dc);
673 return NT_STATUS_NO_MEMORY;
675 user_info_dc->sids[user_info_dc->num_sids].sid = *domain_sid;
676 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids].sid,
677 DOMAIN_RID_ENTERPRISE_READONLY_DCS);
678 user_info_dc->sids[user_info_dc->num_sids].attrs = SE_GROUP_DEFAULT_FLAGS;
679 user_info_dc->num_sids++;
682 info->user_flags = 0;
684 talloc_free(tmp_ctx);
685 *_user_info_dc = user_info_dc;
687 return NT_STATUS_OK;
690 _PUBLIC_ NTSTATUS authsam_update_user_info_dc(TALLOC_CTX *mem_ctx,
691 struct ldb_context *sam_ctx,
692 struct auth_user_info_dc *user_info_dc)
694 char *filter = NULL;
695 NTSTATUS status;
696 uint32_t i;
697 uint32_t n = 0;
700 * This function exists to expand group memberships
701 * in the local domain (forest), as the token
702 * may come from a different domain.
706 * Filter out builtin groups from this token. We will search
707 * for builtin groups later.
709 status = authsam_domain_group_filter(mem_ctx, &filter);
710 if (!NT_STATUS_IS_OK(status)) {
711 return status;
715 * We loop only over the existing number of
716 * sids.
718 n = user_info_dc->num_sids;
719 for (i = 0; i < n; i++) {
720 struct dom_sid *sid = &user_info_dc->sids[i].sid;
721 struct dom_sid_buf sid_buf;
722 char dn_str[sizeof(sid_buf.buf)*2];
723 DATA_BLOB dn_blob = data_blob_null;
725 snprintf(dn_str,
726 sizeof(dn_str),
727 "<SID=%s>",
728 dom_sid_str_buf(sid, &sid_buf));
729 dn_blob = data_blob_string_const(dn_str);
732 * We already have the SID in the token, so set
733 * 'only childs' flag to true and add all
734 * groups which match the filter.
736 status = dsdb_expand_nested_groups(sam_ctx, &dn_blob,
737 true, filter,
738 user_info_dc,
739 &user_info_dc->sids,
740 &user_info_dc->num_sids);
741 if (!NT_STATUS_IS_OK(status)) {
742 talloc_free(filter);
743 return status;
747 talloc_free(filter);
748 return NT_STATUS_OK;
752 * Make a shallow copy of a talloc-allocated user_info_dc structure, holding a
753 * reference to each of the original fields.
755 NTSTATUS authsam_shallow_copy_user_info_dc(TALLOC_CTX *mem_ctx,
756 const struct auth_user_info_dc *user_info_dc_in,
757 struct auth_user_info_dc **user_info_dc_out)
759 struct auth_user_info_dc *user_info_dc = NULL;
760 NTSTATUS status = NT_STATUS_OK;
762 if (user_info_dc_in == NULL) {
763 return NT_STATUS_INVALID_PARAMETER;
766 if (user_info_dc_out == NULL) {
767 return NT_STATUS_INVALID_PARAMETER;
770 user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
771 if (user_info_dc == NULL) {
772 status = NT_STATUS_NO_MEMORY;
773 goto out;
776 *user_info_dc = *user_info_dc_in;
778 if (user_info_dc->info != NULL) {
779 if (talloc_reference(user_info_dc, user_info_dc->info) == NULL) {
780 status = NT_STATUS_NO_MEMORY;
781 goto out;
785 if (user_info_dc->user_session_key.data != NULL) {
786 if (talloc_reference(user_info_dc, user_info_dc->user_session_key.data) == NULL) {
787 status = NT_STATUS_NO_MEMORY;
788 goto out;
792 if (user_info_dc->lm_session_key.data != NULL) {
793 if (talloc_reference(user_info_dc, user_info_dc->lm_session_key.data) == NULL) {
794 status = NT_STATUS_NO_MEMORY;
795 goto out;
799 if (user_info_dc->sids != NULL) {
801 * Because we want to modify the SIDs in the user_info_dc
802 * structure, adding various well-known SIDs such as Asserted
803 * Identity or Claims Valid, make a copy of the SID array to
804 * guard against modification of the original.
806 * It’s better not to make a reference, because anything that
807 * tries to call talloc_realloc() on the original or the copy
808 * will fail when called for any referenced talloc context.
810 user_info_dc->sids = talloc_memdup(user_info_dc,
811 user_info_dc->sids,
812 talloc_get_size(user_info_dc->sids));
813 if (user_info_dc->sids == NULL) {
814 status = NT_STATUS_NO_MEMORY;
815 goto out;
819 *user_info_dc_out = user_info_dc;
820 user_info_dc = NULL;
822 out:
823 talloc_free(user_info_dc);
824 return status;
827 NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
828 TALLOC_CTX *mem_ctx, const char *principal,
829 const char **attrs,
830 struct ldb_dn **domain_dn,
831 struct ldb_message **msg)
833 struct ldb_dn *user_dn;
834 NTSTATUS nt_status;
835 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
836 int ret;
838 if (!tmp_ctx) {
839 return NT_STATUS_NO_MEMORY;
842 nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
843 &user_dn, domain_dn);
844 if (!NT_STATUS_IS_OK(nt_status)) {
845 talloc_free(tmp_ctx);
846 return nt_status;
849 /* pull the user attributes */
850 ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
851 LDB_SCOPE_BASE, attrs,
852 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
853 "(objectClass=*)");
854 if (ret != LDB_SUCCESS) {
855 talloc_free(tmp_ctx);
856 return NT_STATUS_INTERNAL_DB_CORRUPTION;
858 talloc_steal(mem_ctx, *msg);
859 talloc_steal(mem_ctx, *domain_dn);
860 talloc_free(tmp_ctx);
862 return NT_STATUS_OK;
865 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
867 Supply either a principal or a DN
869 NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
870 struct loadparm_context *lp_ctx,
871 struct ldb_context *sam_ctx,
872 const char *principal,
873 struct ldb_dn *user_dn,
874 struct auth_user_info_dc **user_info_dc)
876 NTSTATUS nt_status;
877 DATA_BLOB user_sess_key = data_blob(NULL, 0);
878 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
880 struct ldb_message *msg;
881 struct ldb_dn *domain_dn;
883 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
884 if (!tmp_ctx) {
885 return NT_STATUS_NO_MEMORY;
888 if (principal) {
889 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
890 user_attrs, &domain_dn, &msg);
891 if (!NT_STATUS_IS_OK(nt_status)) {
892 talloc_free(tmp_ctx);
893 return nt_status;
895 } else if (user_dn) {
896 struct dom_sid *user_sid, *domain_sid;
897 int ret;
898 /* pull the user attributes */
899 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
900 LDB_SCOPE_BASE, user_attrs,
901 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
902 "(objectClass=*)");
903 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
904 talloc_free(tmp_ctx);
905 return NT_STATUS_NO_SUCH_USER;
906 } else if (ret != LDB_SUCCESS) {
907 talloc_free(tmp_ctx);
908 return NT_STATUS_INTERNAL_DB_CORRUPTION;
911 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
913 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
914 if (!NT_STATUS_IS_OK(nt_status)) {
915 talloc_free(tmp_ctx);
916 return nt_status;
919 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
920 "(&(objectSid=%s)(objectClass=domain))",
921 ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
922 if (!domain_dn) {
923 struct dom_sid_buf buf;
924 DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
925 dom_sid_str_buf(domain_sid, &buf)));
926 talloc_free(tmp_ctx);
927 return NT_STATUS_NO_SUCH_USER;
930 } else {
931 talloc_free(tmp_ctx);
932 return NT_STATUS_INVALID_PARAMETER;
935 nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
936 lpcfg_netbios_name(lp_ctx),
937 lpcfg_sam_name(lp_ctx),
938 lpcfg_sam_dnsname(lp_ctx),
939 domain_dn,
940 msg,
941 user_sess_key, lm_sess_key,
942 user_info_dc);
943 if (!NT_STATUS_IS_OK(nt_status)) {
944 talloc_free(tmp_ctx);
945 return nt_status;
948 talloc_steal(mem_ctx, *user_info_dc);
949 talloc_free(tmp_ctx);
951 return NT_STATUS_OK;
955 * Returns the details for the Password Settings Object (PSO), if one applies
956 * the user.
958 static int authsam_get_user_pso(struct ldb_context *sam_ctx,
959 TALLOC_CTX *mem_ctx,
960 struct ldb_message *user_msg,
961 struct ldb_message **pso_msg)
963 const char *attrs[] = { "msDS-LockoutThreshold",
964 "msDS-LockoutObservationWindow",
965 NULL };
966 struct ldb_dn *pso_dn = NULL;
967 struct ldb_result *res = NULL;
968 int ret;
970 /* check if the user has a PSO that applies to it */
971 pso_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, user_msg,
972 "msDS-ResultantPSO");
974 if (pso_dn != NULL) {
975 ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, pso_dn, attrs, 0);
976 if (ret != LDB_SUCCESS) {
977 return ret;
980 *pso_msg = res->msgs[0];
983 return LDB_SUCCESS;
987 * Re-read the bad password and successful logon data for a user.
989 * The DN in the passed user record should contain the "objectGUID" in case the
990 * object DN has changed.
992 NTSTATUS authsam_reread_user_logon_data(
993 struct ldb_context *sam_ctx,
994 TALLOC_CTX *mem_ctx,
995 const struct ldb_message *user_msg,
996 struct ldb_message **current)
998 const struct ldb_val *v = NULL;
999 struct ldb_result *res = NULL;
1000 uint16_t acct_flags = 0;
1001 const char *attr_name = "msDS-User-Account-Control-Computed";
1003 int ret;
1006 * Re-read the account details, using the GUID in case the DN
1007 * is being changed (this is automatic in LDB because the
1008 * original search also used DSDB_SEARCH_SHOW_EXTENDED_DN)
1010 * We re read all the attributes in user_attrs, rather than using a
1011 * subset to ensure that we can reuse existing validation code.
1013 ret = dsdb_search_dn(sam_ctx,
1014 mem_ctx,
1015 &res,
1016 user_msg->dn,
1017 user_attrs,
1018 DSDB_SEARCH_SHOW_EXTENDED_DN);
1019 if (ret != LDB_SUCCESS) {
1020 DBG_ERR("Unable to re-read account control data for %s\n",
1021 ldb_dn_get_linearized(user_msg->dn));
1022 return NT_STATUS_INTERNAL_ERROR;
1026 * Ensure the account has not been locked out by another request
1028 v = ldb_msg_find_ldb_val(res->msgs[0], attr_name);
1029 if (v == NULL || v->data == NULL) {
1030 DBG_ERR("No %s attribute for %s\n",
1031 attr_name,
1032 ldb_dn_get_linearized(user_msg->dn));
1033 TALLOC_FREE(res);
1034 return NT_STATUS_INTERNAL_ERROR;
1036 acct_flags = samdb_result_acct_flags(res->msgs[0], attr_name);
1037 if (acct_flags & ACB_AUTOLOCK) {
1038 DBG_WARNING(
1039 "Account for user %s was locked out.\n",
1040 ldb_dn_get_linearized(user_msg->dn));
1041 TALLOC_FREE(res);
1042 return NT_STATUS_ACCOUNT_LOCKED_OUT;
1044 *current = talloc_steal(mem_ctx, res->msgs[0]);
1045 TALLOC_FREE(res);
1046 return NT_STATUS_OK;
1049 static struct db_context *authsam_get_bad_password_db(
1050 TALLOC_CTX *mem_ctx,
1051 struct ldb_context *sam_ctx)
1053 struct loadparm_context *lp_ctx = NULL;
1054 const char *db_name = "bad_password";
1055 struct db_context *db_ctx = NULL;
1057 lp_ctx = ldb_get_opaque(sam_ctx, "loadparm");
1058 if (lp_ctx == NULL) {
1059 DBG_ERR("Unable to get loadparm_context\n");
1060 return NULL;
1063 db_ctx = cluster_db_tmp_open(mem_ctx, lp_ctx, db_name, TDB_DEFAULT);
1064 if (db_ctx == NULL) {
1065 DBG_ERR("Unable to open bad password attempts database\n");
1066 return NULL;
1068 return db_ctx;
1071 static NTSTATUS get_object_sid_as_tdb_data(
1072 TALLOC_CTX *mem_ctx,
1073 const struct ldb_message *msg,
1074 struct dom_sid_buf *buf,
1075 TDB_DATA *key)
1077 struct dom_sid *objectsid = NULL;
1080 * Convert the objectSID to a human readable form to
1081 * make debugging easier
1083 objectsid = samdb_result_dom_sid(mem_ctx, msg, "objectSID");
1084 if (objectsid == NULL) {
1085 DBG_ERR("Unable to extract objectSID\n");
1086 return NT_STATUS_INTERNAL_ERROR;
1088 dom_sid_str_buf(objectsid, buf);
1089 key->dptr = (unsigned char *)buf->buf;
1090 key->dsize = strlen(buf->buf);
1092 talloc_free(objectsid);
1094 return NT_STATUS_OK;
1098 * Add the users objectSID to the bad password attempt database
1099 * to indicate that last authentication failed due to a bad password
1101 static NTSTATUS authsam_set_bad_password_indicator(
1102 struct ldb_context *sam_ctx,
1103 TALLOC_CTX *mem_ctx,
1104 const struct ldb_message *msg)
1106 NTSTATUS status = NT_STATUS_OK;
1107 struct dom_sid_buf buf;
1108 TDB_DATA key = {0};
1109 TDB_DATA value = {0};
1110 struct db_context *db = NULL;
1112 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1113 if (ctx == NULL) {
1114 return NT_STATUS_NO_MEMORY;
1117 db = authsam_get_bad_password_db(ctx, sam_ctx);
1118 if (db == NULL) {
1119 status = NT_STATUS_INTERNAL_ERROR;
1120 goto exit;
1123 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1124 if (!NT_STATUS_IS_OK(status)) {
1125 goto exit;
1128 status = dbwrap_store(db, key, value, 0);
1129 if (!NT_STATUS_IS_OK(status)) {
1130 DBG_ERR("Unable to store bad password indicator\n");
1132 exit:
1133 talloc_free(ctx);
1134 return status;
1138 * see if the users objectSID is in the bad password attempt database
1140 static NTSTATUS authsam_check_bad_password_indicator(
1141 struct ldb_context *sam_ctx,
1142 TALLOC_CTX *mem_ctx,
1143 bool *exists,
1144 const struct ldb_message *msg)
1146 NTSTATUS status = NT_STATUS_OK;
1147 struct dom_sid_buf buf;
1148 TDB_DATA key = {0};
1149 struct db_context *db = NULL;
1151 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1152 if (ctx == NULL) {
1153 return NT_STATUS_NO_MEMORY;
1156 db = authsam_get_bad_password_db(ctx, sam_ctx);
1157 if (db == NULL) {
1158 status = NT_STATUS_INTERNAL_ERROR;
1159 goto exit;
1162 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1163 if (!NT_STATUS_IS_OK(status)) {
1164 goto exit;
1167 *exists = dbwrap_exists(db, key);
1168 exit:
1169 talloc_free(ctx);
1170 return status;
1174 * Remove the users objectSID to the bad password attempt database
1175 * to indicate that last authentication succeeded.
1177 static NTSTATUS authsam_clear_bad_password_indicator(
1178 struct ldb_context *sam_ctx,
1179 TALLOC_CTX *mem_ctx,
1180 const struct ldb_message *msg)
1182 NTSTATUS status = NT_STATUS_OK;
1183 struct dom_sid_buf buf;
1184 TDB_DATA key = {0};
1185 struct db_context *db = NULL;
1187 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1188 if (ctx == NULL) {
1189 return NT_STATUS_NO_MEMORY;
1192 db = authsam_get_bad_password_db(ctx, sam_ctx);
1193 if (db == NULL) {
1194 status = NT_STATUS_INTERNAL_ERROR;
1195 goto exit;
1198 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1199 if (!NT_STATUS_IS_OK(status)) {
1200 goto exit;
1203 status = dbwrap_delete(db, key);
1204 if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, status)) {
1206 * Ok there was no bad password indicator this is expected
1208 status = NT_STATUS_OK;
1210 if (NT_STATUS_IS_ERR(status)) {
1211 DBG_ERR("Unable to delete bad password indicator, %s %s\n",
1212 nt_errstr(status),
1213 get_friendly_nt_error_msg(status));
1215 exit:
1216 talloc_free(ctx);
1217 return status;
1220 NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
1221 struct ldb_message *msg,
1222 struct ldb_dn *domain_dn)
1224 const char *attrs[] = { "lockoutThreshold",
1225 "lockOutObservationWindow",
1226 "lockoutDuration",
1227 "pwdProperties",
1228 NULL };
1229 int ret;
1230 NTSTATUS status;
1231 struct ldb_result *domain_res;
1232 struct ldb_message *msg_mod = NULL;
1233 struct ldb_message *current = NULL;
1234 struct ldb_message *pso_msg = NULL;
1235 bool txn_active = false;
1236 TALLOC_CTX *mem_ctx;
1238 mem_ctx = talloc_new(msg);
1239 if (mem_ctx == NULL) {
1240 return NT_STATUS_NO_MEMORY;
1243 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
1244 if (ret != LDB_SUCCESS) {
1245 TALLOC_FREE(mem_ctx);
1246 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1249 ret = authsam_get_user_pso(sam_ctx, mem_ctx, msg, &pso_msg);
1250 if (ret != LDB_SUCCESS) {
1253 * fallback to using the domain defaults so that we still
1254 * record the bad password attempt
1256 DBG_ERR("Error (%d) checking PSO for %s\n",
1257 ret, ldb_dn_get_linearized(msg->dn));
1261 * To ensure that the bad password count is updated atomically,
1262 * we need to:
1263 * begin a transaction
1264 * re-read the account details,
1265 * using the <GUID= part of the DN
1266 * update the bad password count
1267 * commit the transaction.
1271 * Start a new transaction
1273 ret = ldb_transaction_start(sam_ctx);
1274 if (ret != LDB_SUCCESS) {
1275 status = NT_STATUS_INTERNAL_ERROR;
1276 goto error;
1278 txn_active = true;
1281 * Re-read the account details, using the GUID in case the DN
1282 * is being changed.
1284 status = authsam_reread_user_logon_data(
1285 sam_ctx, mem_ctx, msg, &current);
1286 if (!NT_STATUS_IS_OK(status)) {
1287 /* The re-read can return account locked out, as well
1288 * as an internal error
1290 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
1292 * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
1293 * the transaction. Again to avoid cluttering the
1294 * audit logs with spurious errors
1296 goto exit;
1298 goto error;
1302 * Update the bad password count and if required lock the account
1304 status = dsdb_update_bad_pwd_count(
1305 mem_ctx,
1306 sam_ctx,
1307 current,
1308 domain_res->msgs[0],
1309 pso_msg,
1310 &msg_mod);
1311 if (!NT_STATUS_IS_OK(status)) {
1312 status = NT_STATUS_INTERNAL_ERROR;
1313 goto error;
1317 * Write the data back to disk if required.
1319 if (msg_mod != NULL) {
1320 struct ldb_request *req;
1322 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1323 msg_mod,
1324 NULL,
1325 NULL,
1326 ldb_op_default_callback,
1327 NULL);
1328 if (ret != LDB_SUCCESS) {
1329 TALLOC_FREE(msg_mod);
1330 status = NT_STATUS_INTERNAL_ERROR;
1331 goto error;
1334 ret = ldb_request_add_control(req,
1335 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1336 false, NULL);
1337 if (ret != LDB_SUCCESS) {
1338 talloc_free(req);
1339 status = NT_STATUS_INTERNAL_ERROR;
1340 goto error;
1344 * As we're in a transaction, make the ldb request directly
1345 * to avoid the nested transaction that would result if we
1346 * called dsdb_autotransaction_request
1348 ret = ldb_request(sam_ctx, req);
1349 if (ret == LDB_SUCCESS) {
1350 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1352 talloc_free(req);
1353 if (ret != LDB_SUCCESS) {
1354 status = NT_STATUS_INTERNAL_ERROR;
1355 goto error;
1357 status = authsam_set_bad_password_indicator(
1358 sam_ctx, mem_ctx, msg);
1359 if (!NT_STATUS_IS_OK(status)) {
1360 goto error;
1364 * Note that we may not have updated the user record, but
1365 * committing the transaction in that case is still the correct
1366 * thing to do.
1367 * If the transaction was cancelled, this would be logged by
1368 * the dsdb audit log as a failure. When in fact it is expected
1369 * behaviour.
1371 exit:
1372 TALLOC_FREE(mem_ctx);
1373 ret = ldb_transaction_commit(sam_ctx);
1374 if (ret != LDB_SUCCESS) {
1375 DBG_ERR("Error (%d) %s, committing transaction,"
1376 " while updating bad password count"
1377 " for (%s)\n",
1378 ret,
1379 ldb_errstring(sam_ctx),
1380 ldb_dn_get_linearized(msg->dn));
1381 return NT_STATUS_INTERNAL_ERROR;
1383 return status;
1385 error:
1386 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
1387 "set lockoutTime on %s: %s\n",
1388 ldb_dn_get_linearized(msg->dn),
1389 ldb_errstring(sam_ctx) != NULL ?
1390 ldb_errstring(sam_ctx) :nt_errstr(status));
1391 if (txn_active) {
1392 ret = ldb_transaction_cancel(sam_ctx);
1393 if (ret != LDB_SUCCESS) {
1394 DBG_ERR("Error rolling back transaction,"
1395 " while updating bad password count"
1396 " on %s: %s\n",
1397 ldb_dn_get_linearized(msg->dn),
1398 ldb_errstring(sam_ctx));
1401 TALLOC_FREE(mem_ctx);
1402 return status;
1407 * msDS-LogonTimeSyncInterval is an int32_t number of days.
1409 * The docs say: "the initial update, after the domain functional
1410 * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
1411 * 14 days minus a random percentage of 5 days", but we aren't doing
1412 * that. The blogosphere seems to think that this randomised update
1413 * happens every time, but [MS-ADA1] doesn't agree.
1415 * Dochelp referred us to the following blog post:
1416 * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
1418 * when msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
1419 * not changed.
1422 static NTSTATUS authsam_calculate_lastlogon_sync_interval(
1423 struct ldb_context *sam_ctx,
1424 TALLOC_CTX *ctx,
1425 struct ldb_dn *domain_dn,
1426 NTTIME *sync_interval_nt)
1428 static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
1429 NULL };
1430 int ret;
1431 struct ldb_result *domain_res = NULL;
1432 TALLOC_CTX *mem_ctx = NULL;
1433 uint32_t sync_interval;
1435 mem_ctx = talloc_new(ctx);
1436 if (mem_ctx == NULL) {
1437 return NT_STATUS_NO_MEMORY;
1440 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
1442 if (ret != LDB_SUCCESS || domain_res->count != 1) {
1443 TALLOC_FREE(mem_ctx);
1444 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1447 sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
1448 "msDS-LogonTimeSyncInterval",
1449 14);
1450 DEBUG(5, ("sync interval is %d\n", sync_interval));
1451 if (sync_interval >= 5){
1453 * Subtract "a random percentage of 5" days. Presumably this
1454 * percentage is between 0 and 100, and modulus is accurate
1455 * enough.
1457 uint32_t r = generate_random() % 6;
1458 sync_interval -= r;
1459 DBG_INFO("randomised sync interval is %d (-%d)\n", sync_interval, r);
1461 /* In the case where sync_interval < 5 there is no randomisation */
1464 * msDS-LogonTimeSyncInterval is an int32_t number of days,
1465 * while lastLogonTimestamp (to be updated) is in the 64 bit
1466 * 100ns NTTIME format so we must convert.
1468 *sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
1469 TALLOC_FREE(mem_ctx);
1470 return NT_STATUS_OK;
1475 * We only set lastLogonTimestamp if the current value is older than
1476 * now - msDS-LogonTimeSyncInterval days.
1478 * lastLogonTimestamp is in the 64 bit 100ns NTTIME format
1480 static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
1481 struct ldb_message *msg_mod,
1482 struct ldb_dn *domain_dn,
1483 NTTIME old_timestamp,
1484 NTTIME now,
1485 NTTIME sync_interval_nt)
1487 int ret;
1488 DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
1489 (long long int)old_timestamp,
1490 (long long int)(now - sync_interval_nt),
1491 (long long int)(old_timestamp - now + sync_interval_nt)));
1493 if (sync_interval_nt == 0){
1495 * Setting msDS-LogonTimeSyncInterval to zero is how you ask
1496 * that nothing happens here.
1498 return NT_STATUS_OK;
1500 if (old_timestamp > now){
1501 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
1502 (long long int)old_timestamp, (long long int)now));
1503 /* then what? */
1505 } else if (old_timestamp < now - sync_interval_nt){
1506 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
1507 (long long int)now));
1509 /* The time has come to update lastLogonTimestamp */
1510 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1511 "lastLogonTimestamp", now);
1513 if (ret != LDB_SUCCESS) {
1514 return NT_STATUS_NO_MEMORY;
1517 return NT_STATUS_OK;
1520 /****************************************************************************
1521 Look for the specified user in the sam, return ldb result structures
1522 ****************************************************************************/
1524 NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
1525 const char *account_name,
1526 struct ldb_dn *domain_dn,
1527 struct ldb_message **ret_msg)
1529 int ret;
1530 char *account_name_encoded = NULL;
1532 account_name_encoded = ldb_binary_encode_string(mem_ctx, account_name);
1533 if (account_name_encoded == NULL) {
1534 return NT_STATUS_NO_MEMORY;
1537 /* pull the user attributes */
1538 ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
1539 user_attrs,
1540 DSDB_SEARCH_SHOW_EXTENDED_DN,
1541 "(&(sAMAccountName=%s)(objectclass=user))",
1542 account_name_encoded);
1543 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1544 DEBUG(3,("authsam_search_account: Couldn't find user [%s] in samdb, under %s\n",
1545 account_name, ldb_dn_get_linearized(domain_dn)));
1546 return NT_STATUS_NO_SUCH_USER;
1548 if (ret != LDB_SUCCESS) {
1549 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1552 return NT_STATUS_OK;
1556 /* Reset the badPwdCount to zero and update the lastLogon time. */
1557 NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
1558 const struct ldb_message *msg,
1559 struct ldb_dn *domain_dn,
1560 bool interactive_or_kerberos,
1561 TALLOC_CTX *send_to_sam_mem_ctx,
1562 struct netr_SendToSamBase **send_to_sam)
1564 int ret;
1565 NTSTATUS status;
1566 int badPwdCount;
1567 int dbBadPwdCount;
1568 int64_t lockoutTime;
1569 struct ldb_message *msg_mod;
1570 TALLOC_CTX *mem_ctx;
1571 struct timeval tv_now;
1572 NTTIME now;
1573 NTTIME lastLogonTimestamp;
1574 int64_t lockOutObservationWindow;
1575 NTTIME sync_interval_nt = 0;
1576 bool am_rodc = false;
1577 bool txn_active = false;
1578 bool need_db_reread;
1580 mem_ctx = talloc_new(msg);
1581 if (mem_ctx == NULL) {
1582 return NT_STATUS_NO_MEMORY;
1586 * Any update of the last logon data, needs to be done inside a
1587 * transaction.
1588 * And the user data needs to be re-read, and the account re-checked
1589 * for lockout.
1591 * Howevver we have long-running transactions like replication
1592 * that could otherwise grind the system to a halt so we first
1593 * determine if *this* account has seen a bad password,
1594 * otherwise we only start a transaction if there was a need
1595 * (because a change was to be made).
1598 status = authsam_check_bad_password_indicator(
1599 sam_ctx, mem_ctx, &need_db_reread, msg);
1600 if (!NT_STATUS_IS_OK(status)) {
1601 TALLOC_FREE(mem_ctx);
1602 return status;
1605 if (interactive_or_kerberos == false) {
1607 * Avoid calculating this twice, it reads the PSO. A
1608 * race on this is unimportant.
1610 lockOutObservationWindow
1611 = samdb_result_msds_LockoutObservationWindow(
1612 sam_ctx, mem_ctx, domain_dn, msg);
1615 ret = samdb_rodc(sam_ctx, &am_rodc);
1616 if (ret != LDB_SUCCESS) {
1617 status = NT_STATUS_INTERNAL_ERROR;
1618 goto error;
1621 if (!am_rodc) {
1623 * Avoid reading the main domain DN twice. A race on
1624 * this is unimportant.
1626 status = authsam_calculate_lastlogon_sync_interval(
1627 sam_ctx, mem_ctx, domain_dn, &sync_interval_nt);
1629 if (!NT_STATUS_IS_OK(status)) {
1630 status = NT_STATUS_INTERNAL_ERROR;
1631 goto error;
1635 get_transaction:
1637 if (need_db_reread) {
1638 struct ldb_message *current = NULL;
1641 * Start a new transaction
1643 ret = ldb_transaction_start(sam_ctx);
1644 if (ret != LDB_SUCCESS) {
1645 status = NT_STATUS_INTERNAL_ERROR;
1646 goto error;
1649 txn_active = true;
1652 * Re-read the account details, using the GUID
1653 * embedded in DN so this is safe against a race where
1654 * it is being renamed.
1656 status = authsam_reread_user_logon_data(
1657 sam_ctx, mem_ctx, msg, &current);
1658 if (!NT_STATUS_IS_OK(status)) {
1660 * The re-read can return account locked out, as well
1661 * as an internal error
1663 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
1665 * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
1666 * the transaction. Again to avoid cluttering the
1667 * audit logs with spurious errors
1669 goto exit;
1671 goto error;
1673 msg = current;
1676 lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
1677 dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
1678 tv_now = timeval_current();
1679 now = timeval_to_nttime(&tv_now);
1681 if (interactive_or_kerberos) {
1682 badPwdCount = dbBadPwdCount;
1683 } else {
1685 * We get lockOutObservationWindow above, before the
1686 * transaction
1688 badPwdCount = dsdb_effective_badPwdCount(
1689 msg, lockOutObservationWindow, now);
1691 lastLogonTimestamp =
1692 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
1694 DEBUG(5, ("lastLogonTimestamp is %lld\n",
1695 (long long int)lastLogonTimestamp));
1697 msg_mod = ldb_msg_new(mem_ctx);
1698 if (msg_mod == NULL) {
1699 status = NT_STATUS_NO_MEMORY;
1700 goto error;
1704 * By using the DN from msg->dn directly, we allow LDB to
1705 * prefer the embedded GUID form, so this is actually quite
1706 * safe even in the case where DN has been changed
1708 msg_mod->dn = msg->dn;
1710 if (lockoutTime != 0) {
1712 * This implies "badPwdCount" = 0, see samldb_lockout_time()
1714 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
1715 if (ret != LDB_SUCCESS) {
1716 status = NT_STATUS_NO_MEMORY;
1717 goto error;
1719 } else if (badPwdCount != 0) {
1720 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
1721 if (ret != LDB_SUCCESS) {
1722 status = NT_STATUS_NO_MEMORY;
1723 goto error;
1727 if (interactive_or_kerberos ||
1728 (badPwdCount != 0 && lockoutTime == 0)) {
1729 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1730 "lastLogon", now);
1731 if (ret != LDB_SUCCESS) {
1732 status = NT_STATUS_NO_MEMORY;
1733 goto error;
1737 if (interactive_or_kerberos) {
1738 int logonCount;
1740 logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
1742 logonCount += 1;
1744 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1745 "logonCount", logonCount);
1746 if (ret != LDB_SUCCESS) {
1747 status = NT_STATUS_NO_MEMORY;
1748 goto error;
1750 } else {
1751 /* Set an unset logonCount to 0 on first successful login */
1752 if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
1753 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1754 "logonCount", 0);
1755 if (ret != LDB_SUCCESS) {
1756 TALLOC_FREE(mem_ctx);
1757 return NT_STATUS_NO_MEMORY;
1762 if (!am_rodc) {
1763 status = authsam_update_lastlogon_timestamp(
1764 sam_ctx,
1765 msg_mod,
1766 domain_dn,
1767 lastLogonTimestamp,
1768 now,
1769 sync_interval_nt);
1770 if (!NT_STATUS_IS_OK(status)) {
1771 status = NT_STATUS_NO_MEMORY;
1772 goto error;
1774 } else {
1775 /* Perform the (async) SendToSAM calls for MS-SAMS */
1776 if (dbBadPwdCount != 0 && send_to_sam != NULL) {
1777 struct netr_SendToSamBase *base_msg;
1778 struct GUID guid = samdb_result_guid(msg, "objectGUID");
1780 base_msg = talloc_zero(send_to_sam_mem_ctx,
1781 struct netr_SendToSamBase);
1782 if (base_msg == NULL) {
1783 status = NT_STATUS_NO_MEMORY;
1784 goto error;
1787 base_msg->message_type = SendToSamResetBadPasswordCount;
1788 base_msg->message_size = 16;
1789 base_msg->message.reset_bad_password.guid = guid;
1790 *send_to_sam = base_msg;
1794 if (msg_mod->num_elements > 0) {
1795 unsigned int i;
1796 struct ldb_request *req;
1799 * If it turns out we are going to update the DB, go
1800 * back to the start, get a transaction and the
1801 * current DB state and try again
1803 if (txn_active == false) {
1804 need_db_reread = true;
1805 goto get_transaction;
1808 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
1809 for (i=0;i<msg_mod->num_elements;i++) {
1810 msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1813 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1814 msg_mod,
1815 NULL,
1816 NULL,
1817 ldb_op_default_callback,
1818 NULL);
1819 if (ret != LDB_SUCCESS) {
1820 status = NT_STATUS_INTERNAL_ERROR;
1821 goto error;
1824 ret = ldb_request_add_control(req,
1825 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1826 false, NULL);
1827 if (ret != LDB_SUCCESS) {
1828 TALLOC_FREE(req);
1829 status = NT_STATUS_INTERNAL_ERROR;
1830 goto error;
1833 * As we're in a transaction, make the ldb request directly
1834 * to avoid the nested transaction that would result if we
1835 * called dsdb_autotransaction_request
1837 ret = ldb_request(sam_ctx, req);
1838 if (ret == LDB_SUCCESS) {
1839 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1841 TALLOC_FREE(req);
1842 if (ret != LDB_SUCCESS) {
1843 status = NT_STATUS_INTERNAL_ERROR;
1844 goto error;
1847 status = authsam_clear_bad_password_indicator(sam_ctx, mem_ctx, msg);
1848 if (!NT_STATUS_IS_OK(status)) {
1849 goto error;
1853 * Note that we may not have updated the user record, but
1854 * committing the transaction in that case is still the correct
1855 * thing to do.
1856 * If the transaction was cancelled, this would be logged by
1857 * the dsdb audit log as a failure. When in fact it is expected
1858 * behaviour.
1860 * Thankfully both TDB and LMDB seem to optimise for the empty
1861 * transaction case
1863 exit:
1864 TALLOC_FREE(mem_ctx);
1866 if (txn_active == false) {
1867 return status;
1870 ret = ldb_transaction_commit(sam_ctx);
1871 if (ret != LDB_SUCCESS) {
1872 DBG_ERR("Error (%d) %s, committing transaction,"
1873 " while updating successful logon accounting"
1874 " for (%s)\n",
1875 ret,
1876 ldb_errstring(sam_ctx),
1877 ldb_dn_get_linearized(msg->dn));
1878 return NT_STATUS_INTERNAL_ERROR;
1880 return status;
1882 error:
1883 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
1884 "set lockoutTime on %s: %s\n",
1885 ldb_dn_get_linearized(msg->dn),
1886 ldb_errstring(sam_ctx) != NULL ?
1887 ldb_errstring(sam_ctx) :nt_errstr(status));
1888 if (txn_active) {
1889 ret = ldb_transaction_cancel(sam_ctx);
1890 if (ret != LDB_SUCCESS) {
1891 DBG_ERR("Error rolling back transaction,"
1892 " while updating bad password count"
1893 " on %s: %s\n",
1894 ldb_dn_get_linearized(msg->dn),
1895 ldb_errstring(sam_ctx));
1898 TALLOC_FREE(mem_ctx);
1899 return status;