s3: smbd: Correctly process SMB3 POSIX paths in create.
[Samba.git] / source4 / auth / sam.c
blob6941d2068fd1c61e0eca2c73d8a9dcfc1fe96166
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 const char *krbtgt_attrs[] = {
68 KRBTGT_ATTRS, NULL
71 const char *server_attrs[] = {
72 KRBTGT_ATTRS, NULL
75 const char *user_attrs[] = {
77 * This ordering (having msDS-ResultantPSO first) is
78 * important. By processing this attribute first it is
79 * available in the operational module for the other PSO
80 * attribute calcuations to use.
82 "msDS-ResultantPSO",
84 KRBTGT_ATTRS,
86 "logonHours",
89 * To allow us to zero the badPwdCount and lockoutTime on
90 * successful logon, without database churn
92 "lockoutTime",
95 * Needed for SendToSAM requests
97 "objectGUID",
99 /* check 'allowed workstations' */
100 "userWorkstations",
102 /* required for user_info_dc, not access control: */
103 "displayName",
104 "scriptPath",
105 "profilePath",
106 "homeDirectory",
107 "homeDrive",
108 "lastLogon",
109 "lastLogonTimestamp",
110 "lastLogoff",
111 "accountExpires",
112 "badPwdCount",
113 "logonCount",
114 "primaryGroupID",
115 "memberOf",
116 "badPasswordTime",
117 "lmPwdHistory",
118 "ntPwdHistory",
119 NULL,
122 /****************************************************************************
123 Check if a user is allowed to logon at this time. Note this is the
124 servers local time, as logon hours are just specified as a weekly
125 bitmask.
126 ****************************************************************************/
128 static bool logon_hours_ok(struct ldb_message *msg, const char *name_for_logs)
130 /* In logon hours first bit is Sunday from 12AM to 1AM */
131 const struct ldb_val *hours;
132 struct tm *utctime;
133 time_t lasttime;
134 const char *asct;
135 uint8_t bitmask, bitpos;
137 hours = ldb_msg_find_ldb_val(msg, "logonHours");
138 if (!hours) {
139 DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n", name_for_logs));
140 return true;
143 if (hours->length != 168/8) {
144 DEBUG(5,("logon_hours_ok: malformed logon hours restrictions for user %s\n", name_for_logs));
145 return true;
148 lasttime = time(NULL);
149 utctime = gmtime(&lasttime);
150 if (!utctime) {
151 DEBUG(1, ("logon_hours_ok: failed to get gmtime. Failing logon for user %s\n",
152 name_for_logs));
153 return false;
156 /* find the corresponding byte and bit */
157 bitpos = (utctime->tm_wday * 24 + utctime->tm_hour) % 168;
158 bitmask = 1 << (bitpos % 8);
160 if (! (hours->data[bitpos/8] & bitmask)) {
161 struct tm *t = localtime(&lasttime);
162 if (!t) {
163 asct = "INVALID TIME";
164 } else {
165 asct = asctime(t);
166 if (!asct) {
167 asct = "INVALID TIME";
171 DEBUG(1, ("logon_hours_ok: Account for user %s not allowed to "
172 "logon at this time (%s).\n",
173 name_for_logs, asct ));
174 return false;
177 asct = asctime(utctime);
178 DEBUG(5,("logon_hours_ok: user %s allowed to logon at this time (%s)\n",
179 name_for_logs, asct ? asct : "UNKNOWN TIME" ));
181 return true;
184 /****************************************************************************
185 Do a specific test for a SAM_ACCOUNT being valid for this connection
186 (ie not disabled, expired and the like).
187 ****************************************************************************/
188 _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
189 struct ldb_context *sam_ctx,
190 uint32_t logon_parameters,
191 struct ldb_dn *domain_dn,
192 struct ldb_message *msg,
193 const char *logon_workstation,
194 const char *name_for_logs,
195 bool allow_domain_trust,
196 bool password_change)
198 uint16_t acct_flags;
199 const char *workstation_list;
200 NTTIME acct_expiry;
201 NTTIME must_change_time;
202 struct timeval tv_now = timeval_current();
203 NTTIME now = timeval_to_nttime(&tv_now);
205 DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
207 acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
209 acct_expiry = samdb_result_account_expires(msg);
211 /* Check for when we must change this password, taking the
212 * userAccountControl flags into account */
213 must_change_time = samdb_result_nttime(msg,
214 "msDS-UserPasswordExpiryTimeComputed", 0);
216 workstation_list = ldb_msg_find_attr_as_string(msg, "userWorkstations", NULL);
218 /* Quit if the account was disabled. */
219 if (acct_flags & ACB_DISABLED) {
220 DEBUG(2,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
221 return NT_STATUS_ACCOUNT_DISABLED;
224 /* Quit if the account was locked out. */
225 if (acct_flags & ACB_AUTOLOCK) {
226 DEBUG(2,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
227 return NT_STATUS_ACCOUNT_LOCKED_OUT;
230 /* Test account expire time */
231 if (now > acct_expiry) {
232 DEBUG(2,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
233 DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
234 nt_time_string(mem_ctx, acct_expiry)));
235 return NT_STATUS_ACCOUNT_EXPIRED;
238 /* check for immediate expiry "must change at next logon" (but not if this is a password change request) */
239 if ((must_change_time == 0) && !password_change) {
240 DEBUG(2,("sam_account_ok: Account for user '%s' password must change!.\n",
241 name_for_logs));
242 return NT_STATUS_PASSWORD_MUST_CHANGE;
245 /* check for expired password (but not if this is a password change request) */
246 if ((must_change_time < now) && !password_change) {
247 DEBUG(2,("sam_account_ok: Account for user '%s' password expired!.\n",
248 name_for_logs));
249 DEBUG(2,("sam_account_ok: Password expired at '%s' unix time.\n",
250 nt_time_string(mem_ctx, must_change_time)));
251 return NT_STATUS_PASSWORD_EXPIRED;
254 /* Test workstation. Workstation list is comma separated. */
255 if (logon_workstation && workstation_list && *workstation_list) {
256 bool invalid_ws = true;
257 int i;
258 char **workstations = str_list_make(mem_ctx, workstation_list, ",");
260 for (i = 0; workstations && workstations[i]; i++) {
261 DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
262 workstations[i], logon_workstation));
264 if (strequal(workstations[i], logon_workstation)) {
265 invalid_ws = false;
266 break;
270 talloc_free(workstations);
272 if (invalid_ws) {
273 return NT_STATUS_INVALID_WORKSTATION;
277 if (!logon_hours_ok(msg, name_for_logs)) {
278 return NT_STATUS_INVALID_LOGON_HOURS;
281 if (!allow_domain_trust) {
282 if (acct_flags & ACB_DOMTRUST) {
283 DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
284 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
287 if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
288 if (acct_flags & ACB_SVRTRUST) {
289 DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
290 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
293 if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
294 /* TODO: this fails with current solaris client. We
295 need to work with Gordon to work out why */
296 if (acct_flags & ACB_WSTRUST) {
297 DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
298 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
302 return NT_STATUS_OK;
305 static NTSTATUS authsam_domain_group_filter(TALLOC_CTX *mem_ctx,
306 char **_filter)
308 char *filter = NULL;
310 *_filter = NULL;
312 filter = talloc_strdup(mem_ctx, "(&(objectClass=group)");
315 * Skip all builtin groups, they're added later.
317 talloc_asprintf_addbuf(&filter,
318 "(!(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
319 GROUP_TYPE_BUILTIN_LOCAL_GROUP);
321 * Only include security groups.
323 talloc_asprintf_addbuf(&filter,
324 "(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
325 GROUP_TYPE_SECURITY_ENABLED);
326 if (filter == NULL) {
327 return NT_STATUS_NO_MEMORY;
330 *_filter = filter;
331 return NT_STATUS_OK;
334 _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
335 struct ldb_context *sam_ctx,
336 const char *netbios_name,
337 const char *domain_name,
338 const char *dns_domain_name,
339 struct ldb_dn *domain_dn,
340 const struct ldb_message *msg,
341 DATA_BLOB user_sess_key,
342 DATA_BLOB lm_sess_key,
343 struct auth_user_info_dc **_user_info_dc)
345 NTSTATUS status;
346 int ret;
347 struct auth_user_info_dc *user_info_dc;
348 struct auth_user_info *info;
349 const char *str = NULL;
350 char *filter = NULL;
351 /* SIDs for the account and his primary group */
352 struct dom_sid *account_sid;
353 struct dom_sid_buf buf;
354 const char *primary_group_dn_str = NULL;
355 DATA_BLOB primary_group_blob;
356 struct ldb_dn *primary_group_dn = NULL;
357 struct ldb_message *primary_group_msg = NULL;
358 unsigned primary_group_type;
359 /* SID structures for the expanded group memberships */
360 struct auth_SidAttr *sids = NULL;
361 uint32_t num_sids = 0;
362 unsigned int i;
363 struct dom_sid *domain_sid;
364 TALLOC_CTX *tmp_ctx;
365 struct ldb_message_element *el;
366 static const char * const group_type_attrs[] = { "groupType", NULL };
368 user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
369 NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
371 tmp_ctx = talloc_new(user_info_dc);
372 if (tmp_ctx == NULL) {
373 TALLOC_FREE(user_info_dc);
374 return NT_STATUS_NO_MEMORY;
378 * We'll typically store three SIDs: the SID of the user, the SID of the
379 * primary group, and a copy of the latter if it's not a resource
380 * group. Allocate enough memory for these three SIDs.
382 sids = talloc_zero_array(user_info_dc, struct auth_SidAttr, 3);
383 if (sids == NULL) {
384 TALLOC_FREE(user_info_dc);
385 return NT_STATUS_NO_MEMORY;
388 num_sids = 2;
390 account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
391 if (account_sid == NULL) {
392 TALLOC_FREE(user_info_dc);
393 return NT_STATUS_NO_MEMORY;
396 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
397 if (!NT_STATUS_IS_OK(status)) {
398 talloc_free(user_info_dc);
399 return status;
402 sids[PRIMARY_USER_SID_INDEX].sid = *account_sid;
403 sids[PRIMARY_USER_SID_INDEX].attrs = SE_GROUP_DEFAULT_FLAGS;
404 sids[PRIMARY_GROUP_SID_INDEX].sid = *domain_sid;
405 sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX].sid, ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
406 sids[PRIMARY_GROUP_SID_INDEX].attrs = SE_GROUP_DEFAULT_FLAGS;
409 * Filter out builtin groups from this token. We will search
410 * for builtin groups later, and not include them in the PAC
411 * or SamLogon validation info.
413 status = authsam_domain_group_filter(tmp_ctx, &filter);
414 if (!NT_STATUS_IS_OK(status)) {
415 TALLOC_FREE(user_info_dc);
416 return status;
419 primary_group_dn_str = talloc_asprintf(
420 tmp_ctx,
421 "<SID=%s>",
422 dom_sid_str_buf(&sids[PRIMARY_GROUP_SID_INDEX].sid, &buf));
423 if (primary_group_dn_str == NULL) {
424 TALLOC_FREE(user_info_dc);
425 return NT_STATUS_NO_MEMORY;
428 /* Get the DN of the primary group. */
429 primary_group_dn = ldb_dn_new(tmp_ctx, sam_ctx, primary_group_dn_str);
430 if (primary_group_dn == NULL) {
431 TALLOC_FREE(user_info_dc);
432 return NT_STATUS_NO_MEMORY;
436 * Do a search for the primary group, for the purpose of checking
437 * whether it's a resource group.
439 ret = dsdb_search_one(sam_ctx, tmp_ctx,
440 &primary_group_msg,
441 primary_group_dn,
442 LDB_SCOPE_BASE,
443 group_type_attrs,
445 NULL);
446 if (ret != LDB_SUCCESS) {
447 talloc_free(user_info_dc);
448 return NT_STATUS_INTERNAL_DB_CORRUPTION;
451 /* Check the type of the primary group. */
452 primary_group_type = ldb_msg_find_attr_as_uint(primary_group_msg, "groupType", 0);
453 if (primary_group_type & GROUP_TYPE_RESOURCE_GROUP) {
455 * If it's a resource group, we might as well indicate that in
456 * its attributes. At any rate, the primary group's attributes
457 * are unlikely to be used in the code, as there's nowhere to
458 * store them.
460 sids[PRIMARY_GROUP_SID_INDEX].attrs |= SE_GROUP_RESOURCE;
461 } else {
463 * The primary group is not a resource group. Make a copy of its
464 * SID to ensure it is added to the Base SIDs in the PAC.
466 sids[REMAINING_SIDS_INDEX] = sids[PRIMARY_GROUP_SID_INDEX];
467 ++num_sids;
470 primary_group_blob = data_blob_string_const(primary_group_dn_str);
472 /* Expands the primary group - this function takes in
473 * memberOf-like values, so we fake one up with the
474 * <SID=S-...> format of DN and then let it expand
475 * them, as long as they meet the filter - so only
476 * domain groups, not builtin groups
478 * The primary group is still treated specially, so we set the
479 * 'only childs' flag to true
481 status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
482 user_info_dc, &sids, &num_sids);
483 if (!NT_STATUS_IS_OK(status)) {
484 talloc_free(user_info_dc);
485 return status;
488 /* Expands the additional groups */
489 el = ldb_msg_find_element(msg, "memberOf");
490 for (i = 0; el && i < el->num_values; i++) {
491 /* This function takes in memberOf values and expands
492 * them, as long as they meet the filter - so only
493 * domain groups, not builtin groups */
494 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
495 user_info_dc, &sids, &num_sids);
496 if (!NT_STATUS_IS_OK(status)) {
497 talloc_free(user_info_dc);
498 return status;
502 user_info_dc->sids = sids;
503 user_info_dc->num_sids = num_sids;
505 user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
506 NT_STATUS_HAVE_NO_MEMORY(user_info_dc->info);
508 str = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
509 info->account_name = talloc_strdup(info, str);
510 if (info->account_name == NULL) {
511 TALLOC_FREE(user_info_dc);
512 return NT_STATUS_NO_MEMORY;
515 str = ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL);
516 if (str == NULL && dns_domain_name != NULL) {
517 info->user_principal_name = talloc_asprintf(info, "%s@%s",
518 info->account_name,
519 dns_domain_name);
520 if (info->user_principal_name == NULL) {
521 TALLOC_FREE(user_info_dc);
522 return NT_STATUS_NO_MEMORY;
524 info->user_principal_constructed = true;
525 } else if (str != NULL) {
526 info->user_principal_name = talloc_strdup(info, str);
527 if (info->user_principal_name == NULL) {
528 TALLOC_FREE(user_info_dc);
529 return NT_STATUS_NO_MEMORY;
533 info->domain_name = talloc_strdup(info, domain_name);
534 if (info->domain_name == NULL) {
535 TALLOC_FREE(user_info_dc);
536 return NT_STATUS_NO_MEMORY;
539 if (dns_domain_name != NULL) {
540 info->dns_domain_name = talloc_strdup(info, dns_domain_name);
541 if (info->dns_domain_name == NULL) {
542 TALLOC_FREE(user_info_dc);
543 return NT_STATUS_NO_MEMORY;
547 str = ldb_msg_find_attr_as_string(msg, "displayName", "");
548 info->full_name = talloc_strdup(info, str);
549 if (info->full_name == NULL) {
550 TALLOC_FREE(user_info_dc);
551 return NT_STATUS_NO_MEMORY;
554 str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
555 info->logon_script = talloc_strdup(info, str);
556 if (info->logon_script == NULL) {
557 TALLOC_FREE(user_info_dc);
558 return NT_STATUS_NO_MEMORY;
561 str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
562 info->profile_path = talloc_strdup(info, str);
563 if (info->profile_path == NULL) {
564 TALLOC_FREE(user_info_dc);
565 return NT_STATUS_NO_MEMORY;
568 str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
569 info->home_directory = talloc_strdup(info, str);
570 if (info->home_directory == NULL) {
571 TALLOC_FREE(user_info_dc);
572 return NT_STATUS_NO_MEMORY;
575 str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
576 info->home_drive = talloc_strdup(info, str);
577 if (info->home_drive == NULL) {
578 TALLOC_FREE(user_info_dc);
579 return NT_STATUS_NO_MEMORY;
582 info->logon_server = talloc_strdup(info, netbios_name);
583 if (info->logon_server == NULL) {
584 TALLOC_FREE(user_info_dc);
585 return NT_STATUS_NO_MEMORY;
588 info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
589 info->last_logoff = samdb_result_last_logoff(msg);
590 info->acct_expiry = samdb_result_account_expires(msg);
591 info->last_password_change = samdb_result_nttime(msg,
592 "pwdLastSet", 0);
593 info->allow_password_change
594 = samdb_result_allow_password_change(sam_ctx, mem_ctx,
595 domain_dn, msg, "pwdLastSet");
596 info->force_password_change = samdb_result_nttime(msg,
597 "msDS-UserPasswordExpiryTimeComputed", 0);
598 info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
599 info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
602 info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
604 user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
605 user_sess_key.data,
606 user_sess_key.length);
607 if (user_sess_key.data) {
608 if (user_info_dc->user_session_key.data == NULL) {
609 TALLOC_FREE(user_info_dc);
610 return NT_STATUS_NO_MEMORY;
613 user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
614 lm_sess_key.data,
615 lm_sess_key.length);
616 if (lm_sess_key.data) {
617 if (user_info_dc->lm_session_key.data == NULL) {
618 TALLOC_FREE(user_info_dc);
619 return NT_STATUS_NO_MEMORY;
623 if (info->acct_flags & ACB_SVRTRUST) {
624 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
625 PAC */
626 user_info_dc->sids = talloc_realloc(user_info_dc,
627 user_info_dc->sids,
628 struct auth_SidAttr,
629 user_info_dc->num_sids+1);
630 if (user_info_dc->sids == NULL) {
631 TALLOC_FREE(user_info_dc);
632 return NT_STATUS_NO_MEMORY;
634 user_info_dc->sids[user_info_dc->num_sids].sid = global_sid_Enterprise_DCs;
635 user_info_dc->sids[user_info_dc->num_sids].attrs = SE_GROUP_DEFAULT_FLAGS;
636 user_info_dc->num_sids++;
639 if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
640 (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
641 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
642 user_info_dc->sids = talloc_realloc(user_info_dc,
643 user_info_dc->sids,
644 struct auth_SidAttr,
645 user_info_dc->num_sids+1);
646 if (user_info_dc->sids == NULL) {
647 TALLOC_FREE(user_info_dc);
648 return NT_STATUS_NO_MEMORY;
650 user_info_dc->sids[user_info_dc->num_sids].sid = *domain_sid;
651 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids].sid,
652 DOMAIN_RID_ENTERPRISE_READONLY_DCS);
653 user_info_dc->sids[user_info_dc->num_sids].attrs = SE_GROUP_DEFAULT_FLAGS;
654 user_info_dc->num_sids++;
657 info->user_flags = 0;
659 talloc_free(tmp_ctx);
660 *_user_info_dc = user_info_dc;
662 return NT_STATUS_OK;
665 _PUBLIC_ NTSTATUS authsam_update_user_info_dc(TALLOC_CTX *mem_ctx,
666 struct ldb_context *sam_ctx,
667 struct auth_user_info_dc *user_info_dc)
669 char *filter = NULL;
670 NTSTATUS status;
671 uint32_t i;
672 uint32_t n = 0;
675 * This function exists to expand group memberships
676 * in the local domain (forest), as the token
677 * may come from a different domain.
681 * Filter out builtin groups from this token. We will search
682 * for builtin groups later.
684 status = authsam_domain_group_filter(mem_ctx, &filter);
685 if (!NT_STATUS_IS_OK(status)) {
686 return status;
690 * We loop only over the existing number of
691 * sids.
693 n = user_info_dc->num_sids;
694 for (i = 0; i < n; i++) {
695 struct dom_sid *sid = &user_info_dc->sids[i].sid;
696 struct dom_sid_buf sid_buf;
697 char dn_str[sizeof(sid_buf.buf)*2];
698 DATA_BLOB dn_blob = data_blob_null;
700 snprintf(dn_str,
701 sizeof(dn_str),
702 "<SID=%s>",
703 dom_sid_str_buf(sid, &sid_buf));
704 dn_blob = data_blob_string_const(dn_str);
707 * We already have the SID in the token, so set
708 * 'only childs' flag to true and add all
709 * groups which match the filter.
711 status = dsdb_expand_nested_groups(sam_ctx, &dn_blob,
712 true, filter,
713 user_info_dc,
714 &user_info_dc->sids,
715 &user_info_dc->num_sids);
716 if (!NT_STATUS_IS_OK(status)) {
717 return status;
721 return NT_STATUS_OK;
724 NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
725 TALLOC_CTX *mem_ctx, const char *principal,
726 const char **attrs,
727 struct ldb_dn **domain_dn,
728 struct ldb_message **msg)
730 struct ldb_dn *user_dn;
731 NTSTATUS nt_status;
732 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
733 int ret;
735 if (!tmp_ctx) {
736 return NT_STATUS_NO_MEMORY;
739 nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
740 &user_dn, domain_dn);
741 if (!NT_STATUS_IS_OK(nt_status)) {
742 talloc_free(tmp_ctx);
743 return nt_status;
746 /* pull the user attributes */
747 ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
748 LDB_SCOPE_BASE, attrs,
749 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
750 "(objectClass=*)");
751 if (ret != LDB_SUCCESS) {
752 talloc_free(tmp_ctx);
753 return NT_STATUS_INTERNAL_DB_CORRUPTION;
755 talloc_steal(mem_ctx, *msg);
756 talloc_steal(mem_ctx, *domain_dn);
757 talloc_free(tmp_ctx);
759 return NT_STATUS_OK;
762 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
764 Supply either a principal or a DN
766 NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
767 struct loadparm_context *lp_ctx,
768 struct ldb_context *sam_ctx,
769 const char *principal,
770 struct ldb_dn *user_dn,
771 struct auth_user_info_dc **user_info_dc)
773 NTSTATUS nt_status;
774 DATA_BLOB user_sess_key = data_blob(NULL, 0);
775 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
777 struct ldb_message *msg;
778 struct ldb_dn *domain_dn;
780 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
781 if (!tmp_ctx) {
782 return NT_STATUS_NO_MEMORY;
785 if (principal) {
786 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
787 user_attrs, &domain_dn, &msg);
788 if (!NT_STATUS_IS_OK(nt_status)) {
789 talloc_free(tmp_ctx);
790 return nt_status;
792 } else if (user_dn) {
793 struct dom_sid *user_sid, *domain_sid;
794 int ret;
795 /* pull the user attributes */
796 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
797 LDB_SCOPE_BASE, user_attrs,
798 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
799 "(objectClass=*)");
800 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
801 talloc_free(tmp_ctx);
802 return NT_STATUS_NO_SUCH_USER;
803 } else if (ret != LDB_SUCCESS) {
804 talloc_free(tmp_ctx);
805 return NT_STATUS_INTERNAL_DB_CORRUPTION;
808 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
810 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
811 if (!NT_STATUS_IS_OK(nt_status)) {
812 return nt_status;
815 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
816 "(&(objectSid=%s)(objectClass=domain))",
817 ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
818 if (!domain_dn) {
819 struct dom_sid_buf buf;
820 DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
821 dom_sid_str_buf(domain_sid, &buf)));
822 return NT_STATUS_NO_SUCH_USER;
825 } else {
826 return NT_STATUS_INVALID_PARAMETER;
829 nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
830 lpcfg_netbios_name(lp_ctx),
831 lpcfg_sam_name(lp_ctx),
832 lpcfg_sam_dnsname(lp_ctx),
833 domain_dn,
834 msg,
835 user_sess_key, lm_sess_key,
836 user_info_dc);
837 if (!NT_STATUS_IS_OK(nt_status)) {
838 talloc_free(tmp_ctx);
839 return nt_status;
842 talloc_steal(mem_ctx, *user_info_dc);
843 talloc_free(tmp_ctx);
845 return NT_STATUS_OK;
849 * Returns the details for the Password Settings Object (PSO), if one applies
850 * the user.
852 static int authsam_get_user_pso(struct ldb_context *sam_ctx,
853 TALLOC_CTX *mem_ctx,
854 struct ldb_message *user_msg,
855 struct ldb_message **pso_msg)
857 const char *attrs[] = { "msDS-LockoutThreshold",
858 "msDS-LockoutObservationWindow",
859 NULL };
860 struct ldb_dn *pso_dn = NULL;
861 struct ldb_result *res = NULL;
862 int ret;
864 /* check if the user has a PSO that applies to it */
865 pso_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, user_msg,
866 "msDS-ResultantPSO");
868 if (pso_dn != NULL) {
869 ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, pso_dn, attrs, 0);
870 if (ret != LDB_SUCCESS) {
871 return ret;
874 *pso_msg = res->msgs[0];
877 return LDB_SUCCESS;
881 * Re-read the bad password and successful logon data for a user.
883 * The DN in the passed user record should contain the "objectGUID" in case the
884 * object DN has changed.
886 NTSTATUS authsam_reread_user_logon_data(
887 struct ldb_context *sam_ctx,
888 TALLOC_CTX *mem_ctx,
889 const struct ldb_message *user_msg,
890 struct ldb_message **current)
892 const struct ldb_val *v = NULL;
893 struct ldb_result *res = NULL;
894 uint16_t acct_flags = 0;
895 const char *attr_name = "msDS-User-Account-Control-Computed";
897 int ret;
900 * Re-read the account details, using the GUID in case the DN
901 * is being changed (this is automatic in LDB because the
902 * original search also used DSDB_SEARCH_SHOW_EXTENDED_DN)
904 * We re read all the attributes in user_attrs, rather than using a
905 * subset to ensure that we can reuse existing validation code.
907 ret = dsdb_search_dn(sam_ctx,
908 mem_ctx,
909 &res,
910 user_msg->dn,
911 user_attrs,
912 DSDB_SEARCH_SHOW_EXTENDED_DN);
913 if (ret != LDB_SUCCESS) {
914 DBG_ERR("Unable to re-read account control data for %s\n",
915 ldb_dn_get_linearized(user_msg->dn));
916 return NT_STATUS_INTERNAL_ERROR;
920 * Ensure the account has not been locked out by another request
922 v = ldb_msg_find_ldb_val(res->msgs[0], attr_name);
923 if (v == NULL || v->data == NULL) {
924 DBG_ERR("No %s attribute for %s\n",
925 attr_name,
926 ldb_dn_get_linearized(user_msg->dn));
927 TALLOC_FREE(res);
928 return NT_STATUS_INTERNAL_ERROR;
930 acct_flags = samdb_result_acct_flags(res->msgs[0], attr_name);
931 if (acct_flags & ACB_AUTOLOCK) {
932 DBG_WARNING(
933 "Account for user %s was locked out.\n",
934 ldb_dn_get_linearized(user_msg->dn));
935 TALLOC_FREE(res);
936 return NT_STATUS_ACCOUNT_LOCKED_OUT;
938 *current = talloc_steal(mem_ctx, res->msgs[0]);
939 TALLOC_FREE(res);
940 return NT_STATUS_OK;
943 static struct db_context *authsam_get_bad_password_db(
944 TALLOC_CTX *mem_ctx,
945 struct ldb_context *sam_ctx)
947 struct loadparm_context *lp_ctx = NULL;
948 const char *db_name = "bad_password";
949 struct db_context *db_ctx = NULL;
951 lp_ctx = ldb_get_opaque(sam_ctx, "loadparm");
952 if (lp_ctx == NULL) {
953 DBG_ERR("Unable to get loadparm_context\n");
954 return NULL;
957 db_ctx = cluster_db_tmp_open(mem_ctx, lp_ctx, db_name, TDB_DEFAULT);
958 if (db_ctx == NULL) {
959 DBG_ERR("Unable to open bad password attempts database\n");
960 return NULL;
962 return db_ctx;
965 static NTSTATUS get_object_sid_as_tdb_data(
966 TALLOC_CTX *mem_ctx,
967 const struct ldb_message *msg,
968 struct dom_sid_buf *buf,
969 TDB_DATA *key)
971 struct dom_sid *objectsid = NULL;
974 * Convert the objectSID to a human readable form to
975 * make debugging easier
977 objectsid = samdb_result_dom_sid(mem_ctx, msg, "objectSID");
978 if (objectsid == NULL) {
979 DBG_ERR("Unable to extract objectSID\n");
980 return NT_STATUS_INTERNAL_ERROR;
982 dom_sid_str_buf(objectsid, buf);
983 key->dptr = (unsigned char *)buf->buf;
984 key->dsize = strlen(buf->buf);
986 talloc_free(objectsid);
988 return NT_STATUS_OK;
992 * Add the users objectSID to the bad password attempt database
993 * to indicate that last authentication failed due to a bad password
995 static NTSTATUS authsam_set_bad_password_indicator(
996 struct ldb_context *sam_ctx,
997 TALLOC_CTX *mem_ctx,
998 const struct ldb_message *msg)
1000 NTSTATUS status = NT_STATUS_OK;
1001 struct dom_sid_buf buf;
1002 TDB_DATA key = {0};
1003 TDB_DATA value = {0};
1004 struct db_context *db = NULL;
1006 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1007 if (ctx == NULL) {
1008 return NT_STATUS_NO_MEMORY;
1011 db = authsam_get_bad_password_db(ctx, sam_ctx);
1012 if (db == NULL) {
1013 status = NT_STATUS_INTERNAL_ERROR;
1014 goto exit;
1017 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1018 if (!NT_STATUS_IS_OK(status)) {
1019 goto exit;
1022 status = dbwrap_store(db, key, value, 0);
1023 if (!NT_STATUS_IS_OK(status)) {
1024 DBG_ERR("Unable to store bad password indicator\n");
1026 exit:
1027 talloc_free(ctx);
1028 return status;
1032 * see if the users objectSID is in the bad password attempt database
1034 static NTSTATUS authsam_check_bad_password_indicator(
1035 struct ldb_context *sam_ctx,
1036 TALLOC_CTX *mem_ctx,
1037 bool *exists,
1038 const struct ldb_message *msg)
1040 NTSTATUS status = NT_STATUS_OK;
1041 struct dom_sid_buf buf;
1042 TDB_DATA key = {0};
1043 struct db_context *db = NULL;
1045 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1046 if (ctx == NULL) {
1047 return NT_STATUS_NO_MEMORY;
1050 db = authsam_get_bad_password_db(ctx, sam_ctx);
1051 if (db == NULL) {
1052 status = NT_STATUS_INTERNAL_ERROR;
1053 goto exit;
1056 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1057 if (!NT_STATUS_IS_OK(status)) {
1058 goto exit;
1061 *exists = dbwrap_exists(db, key);
1062 exit:
1063 talloc_free(ctx);
1064 return status;
1068 * Remove the users objectSID to the bad password attempt database
1069 * to indicate that last authentication succeeded.
1071 static NTSTATUS authsam_clear_bad_password_indicator(
1072 struct ldb_context *sam_ctx,
1073 TALLOC_CTX *mem_ctx,
1074 const struct ldb_message *msg)
1076 NTSTATUS status = NT_STATUS_OK;
1077 struct dom_sid_buf buf;
1078 TDB_DATA key = {0};
1079 struct db_context *db = NULL;
1081 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1082 if (ctx == NULL) {
1083 return NT_STATUS_NO_MEMORY;
1086 db = authsam_get_bad_password_db(ctx, sam_ctx);
1087 if (db == NULL) {
1088 status = NT_STATUS_INTERNAL_ERROR;
1089 goto exit;
1092 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1093 if (!NT_STATUS_IS_OK(status)) {
1094 goto exit;
1097 status = dbwrap_delete(db, key);
1098 if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, status)) {
1100 * Ok there was no bad password indicator this is expected
1102 status = NT_STATUS_OK;
1104 if (NT_STATUS_IS_ERR(status)) {
1105 DBG_ERR("Unable to delete bad password indicator, %s %s\n",
1106 nt_errstr(status),
1107 get_friendly_nt_error_msg(status));
1109 exit:
1110 talloc_free(ctx);
1111 return status;
1114 NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
1115 struct ldb_message *msg,
1116 struct ldb_dn *domain_dn)
1118 const char *attrs[] = { "lockoutThreshold",
1119 "lockOutObservationWindow",
1120 "lockoutDuration",
1121 "pwdProperties",
1122 NULL };
1123 int ret;
1124 NTSTATUS status;
1125 struct ldb_result *domain_res;
1126 struct ldb_message *msg_mod = NULL;
1127 struct ldb_message *current = NULL;
1128 struct ldb_message *pso_msg = NULL;
1129 bool txn_active = false;
1130 TALLOC_CTX *mem_ctx;
1132 mem_ctx = talloc_new(msg);
1133 if (mem_ctx == NULL) {
1134 return NT_STATUS_NO_MEMORY;
1137 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
1138 if (ret != LDB_SUCCESS) {
1139 TALLOC_FREE(mem_ctx);
1140 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1143 ret = authsam_get_user_pso(sam_ctx, mem_ctx, msg, &pso_msg);
1144 if (ret != LDB_SUCCESS) {
1147 * fallback to using the domain defaults so that we still
1148 * record the bad password attempt
1150 DBG_ERR("Error (%d) checking PSO for %s\n",
1151 ret, ldb_dn_get_linearized(msg->dn));
1155 * To ensure that the bad password count is updated atomically,
1156 * we need to:
1157 * begin a transaction
1158 * re-read the account details,
1159 * using the <GUID= part of the DN
1160 * update the bad password count
1161 * commit the transaction.
1165 * Start a new transaction
1167 ret = ldb_transaction_start(sam_ctx);
1168 if (ret != LDB_SUCCESS) {
1169 status = NT_STATUS_INTERNAL_ERROR;
1170 goto error;
1172 txn_active = true;
1175 * Re-read the account details, using the GUID in case the DN
1176 * is being changed.
1178 status = authsam_reread_user_logon_data(
1179 sam_ctx, mem_ctx, msg, &current);
1180 if (!NT_STATUS_IS_OK(status)) {
1181 /* The re-read can return account locked out, as well
1182 * as an internal error
1184 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
1186 * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
1187 * the transaction. Again to avoid cluttering the
1188 * audit logs with spurious errors
1190 goto exit;
1192 goto error;
1196 * Update the bad password count and if required lock the account
1198 status = dsdb_update_bad_pwd_count(
1199 mem_ctx,
1200 sam_ctx,
1201 current,
1202 domain_res->msgs[0],
1203 pso_msg,
1204 &msg_mod);
1205 if (!NT_STATUS_IS_OK(status)) {
1206 status = NT_STATUS_INTERNAL_ERROR;
1207 goto error;
1211 * Write the data back to disk if required.
1213 if (msg_mod != NULL) {
1214 struct ldb_request *req;
1216 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1217 msg_mod,
1218 NULL,
1219 NULL,
1220 ldb_op_default_callback,
1221 NULL);
1222 if (ret != LDB_SUCCESS) {
1223 TALLOC_FREE(msg_mod);
1224 status = NT_STATUS_INTERNAL_ERROR;
1225 goto error;
1228 ret = ldb_request_add_control(req,
1229 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1230 false, NULL);
1231 if (ret != LDB_SUCCESS) {
1232 talloc_free(req);
1233 status = NT_STATUS_INTERNAL_ERROR;
1234 goto error;
1238 * As we're in a transaction, make the ldb request directly
1239 * to avoid the nested transaction that would result if we
1240 * called dsdb_autotransaction_request
1242 ret = ldb_request(sam_ctx, req);
1243 if (ret == LDB_SUCCESS) {
1244 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1246 talloc_free(req);
1247 if (ret != LDB_SUCCESS) {
1248 status = NT_STATUS_INTERNAL_ERROR;
1249 goto error;
1251 status = authsam_set_bad_password_indicator(
1252 sam_ctx, mem_ctx, msg);
1253 if (!NT_STATUS_IS_OK(status)) {
1254 goto error;
1258 * Note that we may not have updated the user record, but
1259 * committing the transaction in that case is still the correct
1260 * thing to do.
1261 * If the transaction was cancelled, this would be logged by
1262 * the dsdb audit log as a failure. When in fact it is expected
1263 * behaviour.
1265 exit:
1266 TALLOC_FREE(mem_ctx);
1267 ret = ldb_transaction_commit(sam_ctx);
1268 if (ret != LDB_SUCCESS) {
1269 DBG_ERR("Error (%d) %s, committing transaction,"
1270 " while updating bad password count"
1271 " for (%s)\n",
1272 ret,
1273 ldb_errstring(sam_ctx),
1274 ldb_dn_get_linearized(msg->dn));
1275 return NT_STATUS_INTERNAL_ERROR;
1277 return status;
1279 error:
1280 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
1281 "set lockoutTime on %s: %s\n",
1282 ldb_dn_get_linearized(msg->dn),
1283 ldb_errstring(sam_ctx) != NULL ?
1284 ldb_errstring(sam_ctx) :nt_errstr(status));
1285 if (txn_active) {
1286 ret = ldb_transaction_cancel(sam_ctx);
1287 if (ret != LDB_SUCCESS) {
1288 DBG_ERR("Error rolling back transaction,"
1289 " while updating bad password count"
1290 " on %s: %s\n",
1291 ldb_dn_get_linearized(msg->dn),
1292 ldb_errstring(sam_ctx));
1295 TALLOC_FREE(mem_ctx);
1296 return status;
1301 * msDS-LogonTimeSyncInterval is an int32_t number of days.
1303 * The docs say: "the initial update, after the domain functional
1304 * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
1305 * 14 days minus a random percentage of 5 days", but we aren't doing
1306 * that. The blogosphere seems to think that this randomised update
1307 * happens everytime, but [MS-ADA1] doesn't agree.
1309 * Dochelp referred us to the following blog post:
1310 * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
1312 * when msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
1313 * not changed.
1316 static NTSTATUS authsam_calculate_lastlogon_sync_interval(
1317 struct ldb_context *sam_ctx,
1318 TALLOC_CTX *ctx,
1319 struct ldb_dn *domain_dn,
1320 NTTIME *sync_interval_nt)
1322 static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
1323 NULL };
1324 int ret;
1325 struct ldb_result *domain_res = NULL;
1326 TALLOC_CTX *mem_ctx = NULL;
1327 uint32_t sync_interval;
1329 mem_ctx = talloc_new(ctx);
1330 if (mem_ctx == NULL) {
1331 return NT_STATUS_NO_MEMORY;
1334 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
1336 if (ret != LDB_SUCCESS || domain_res->count != 1) {
1337 TALLOC_FREE(mem_ctx);
1338 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1341 sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
1342 "msDS-LogonTimeSyncInterval",
1343 14);
1344 DEBUG(5, ("sync interval is %d\n", sync_interval));
1345 if (sync_interval >= 5){
1347 * Subtract "a random percentage of 5" days. Presumably this
1348 * percentage is between 0 and 100, and modulus is accurate
1349 * enough.
1351 uint32_t r = generate_random() % 6;
1352 sync_interval -= r;
1353 DBG_INFO("randomised sync interval is %d (-%d)\n", sync_interval, r);
1355 /* In the case where sync_interval < 5 there is no randomisation */
1358 * msDS-LogonTimeSyncInterval is an int32_t number of days,
1359 * while lastLogonTimestamp (to be updated) is in the 64 bit
1360 * 100ns NTTIME format so we must convert.
1362 *sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
1363 TALLOC_FREE(mem_ctx);
1364 return NT_STATUS_OK;
1369 * We only set lastLogonTimestamp if the current value is older than
1370 * now - msDS-LogonTimeSyncInterval days.
1372 * lastLogonTimestamp is in the 64 bit 100ns NTTIME format
1374 static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
1375 struct ldb_message *msg_mod,
1376 struct ldb_dn *domain_dn,
1377 NTTIME old_timestamp,
1378 NTTIME now,
1379 NTTIME sync_interval_nt)
1381 int ret;
1382 DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
1383 (long long int)old_timestamp,
1384 (long long int)(now - sync_interval_nt),
1385 (long long int)(old_timestamp - now + sync_interval_nt)));
1387 if (sync_interval_nt == 0){
1389 * Setting msDS-LogonTimeSyncInterval to zero is how you ask
1390 * that nothing happens here.
1392 return NT_STATUS_OK;
1394 if (old_timestamp > now){
1395 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
1396 (long long int)old_timestamp, (long long int)now));
1397 /* then what? */
1399 } else if (old_timestamp < now - sync_interval_nt){
1400 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
1401 (long long int)now));
1403 /* The time has come to update lastLogonTimestamp */
1404 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1405 "lastLogonTimestamp", now);
1407 if (ret != LDB_SUCCESS) {
1408 return NT_STATUS_NO_MEMORY;
1411 return NT_STATUS_OK;
1414 /****************************************************************************
1415 Look for the specified user in the sam, return ldb result structures
1416 ****************************************************************************/
1418 NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
1419 const char *account_name,
1420 struct ldb_dn *domain_dn,
1421 struct ldb_message **ret_msg)
1423 int ret;
1425 /* pull the user attributes */
1426 ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
1427 user_attrs,
1428 DSDB_SEARCH_SHOW_EXTENDED_DN,
1429 "(&(sAMAccountName=%s)(objectclass=user))",
1430 ldb_binary_encode_string(mem_ctx, account_name));
1431 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1432 DEBUG(3,("sam_search_user: Couldn't find user [%s] in samdb, under %s\n",
1433 account_name, ldb_dn_get_linearized(domain_dn)));
1434 return NT_STATUS_NO_SUCH_USER;
1436 if (ret != LDB_SUCCESS) {
1437 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1440 return NT_STATUS_OK;
1444 /* Reset the badPwdCount to zero and update the lastLogon time. */
1445 NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
1446 const struct ldb_message *msg,
1447 struct ldb_dn *domain_dn,
1448 bool interactive_or_kerberos,
1449 TALLOC_CTX *send_to_sam_mem_ctx,
1450 struct netr_SendToSamBase **send_to_sam)
1452 int ret;
1453 NTSTATUS status;
1454 int badPwdCount;
1455 int dbBadPwdCount;
1456 int64_t lockoutTime;
1457 struct ldb_message *msg_mod;
1458 TALLOC_CTX *mem_ctx;
1459 struct timeval tv_now;
1460 NTTIME now;
1461 NTTIME lastLogonTimestamp;
1462 int64_t lockOutObservationWindow;
1463 NTTIME sync_interval_nt = 0;
1464 bool am_rodc = false;
1465 bool txn_active = false;
1466 bool need_db_reread;
1468 mem_ctx = talloc_new(msg);
1469 if (mem_ctx == NULL) {
1470 return NT_STATUS_NO_MEMORY;
1474 * Any update of the last logon data, needs to be done inside a
1475 * transaction.
1476 * And the user data needs to be re-read, and the account re-checked
1477 * for lockout.
1479 * Howevver we have long-running transactions like replication
1480 * that could otherwise grind the system to a halt so we first
1481 * determine if *this* account has seen a bad password,
1482 * otherwise we only start a transaction if there was a need
1483 * (because a change was to be made).
1486 status = authsam_check_bad_password_indicator(
1487 sam_ctx, mem_ctx, &need_db_reread, msg);
1488 if (!NT_STATUS_IS_OK(status)) {
1489 return status;
1492 if (interactive_or_kerberos == false) {
1494 * Avoid calculating this twice, it reads the PSO. A
1495 * race on this is unimportant.
1497 lockOutObservationWindow
1498 = samdb_result_msds_LockoutObservationWindow(
1499 sam_ctx, mem_ctx, domain_dn, msg);
1502 ret = samdb_rodc(sam_ctx, &am_rodc);
1503 if (ret != LDB_SUCCESS) {
1504 status = NT_STATUS_INTERNAL_ERROR;
1505 goto error;
1508 if (!am_rodc) {
1510 * Avoid reading the main domain DN twice. A race on
1511 * this is unimportant.
1513 status = authsam_calculate_lastlogon_sync_interval(
1514 sam_ctx, mem_ctx, domain_dn, &sync_interval_nt);
1516 if (!NT_STATUS_IS_OK(status)) {
1517 status = NT_STATUS_INTERNAL_ERROR;
1518 goto error;
1522 get_transaction:
1524 if (need_db_reread) {
1525 struct ldb_message *current = NULL;
1528 * Start a new transaction
1530 ret = ldb_transaction_start(sam_ctx);
1531 if (ret != LDB_SUCCESS) {
1532 status = NT_STATUS_INTERNAL_ERROR;
1533 goto error;
1536 txn_active = true;
1539 * Re-read the account details, using the GUID
1540 * embedded in DN so this is safe against a race where
1541 * it is being renamed.
1543 status = authsam_reread_user_logon_data(
1544 sam_ctx, mem_ctx, msg, &current);
1545 if (!NT_STATUS_IS_OK(status)) {
1547 * The re-read can return account locked out, as well
1548 * as an internal error
1550 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
1552 * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
1553 * the transaction. Again to avoid cluttering the
1554 * audit logs with spurious errors
1556 goto exit;
1558 goto error;
1560 msg = current;
1563 lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
1564 dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
1565 tv_now = timeval_current();
1566 now = timeval_to_nttime(&tv_now);
1568 if (interactive_or_kerberos) {
1569 badPwdCount = dbBadPwdCount;
1570 } else {
1572 * We get lockOutObservationWindow above, before the
1573 * transaction
1575 badPwdCount = dsdb_effective_badPwdCount(
1576 msg, lockOutObservationWindow, now);
1578 lastLogonTimestamp =
1579 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
1581 DEBUG(5, ("lastLogonTimestamp is %lld\n",
1582 (long long int)lastLogonTimestamp));
1584 msg_mod = ldb_msg_new(mem_ctx);
1585 if (msg_mod == NULL) {
1586 status = NT_STATUS_NO_MEMORY;
1587 goto error;
1591 * By using the DN from msg->dn directly, we allow LDB to
1592 * prefer the embedded GUID form, so this is actually quite
1593 * safe even in the case where DN has been changed
1595 msg_mod->dn = msg->dn;
1597 if (lockoutTime != 0) {
1599 * This implies "badPwdCount" = 0, see samldb_lockout_time()
1601 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
1602 if (ret != LDB_SUCCESS) {
1603 status = NT_STATUS_NO_MEMORY;
1604 goto error;
1606 } else if (badPwdCount != 0) {
1607 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
1608 if (ret != LDB_SUCCESS) {
1609 status = NT_STATUS_NO_MEMORY;
1610 goto error;
1614 if (interactive_or_kerberos ||
1615 (badPwdCount != 0 && lockoutTime == 0)) {
1616 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1617 "lastLogon", now);
1618 if (ret != LDB_SUCCESS) {
1619 status = NT_STATUS_NO_MEMORY;
1620 goto error;
1624 if (interactive_or_kerberos) {
1625 int logonCount;
1627 logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
1629 logonCount += 1;
1631 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1632 "logonCount", logonCount);
1633 if (ret != LDB_SUCCESS) {
1634 status = NT_STATUS_NO_MEMORY;
1635 goto error;
1637 } else {
1638 /* Set an unset logonCount to 0 on first successful login */
1639 if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
1640 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1641 "logonCount", 0);
1642 if (ret != LDB_SUCCESS) {
1643 TALLOC_FREE(mem_ctx);
1644 return NT_STATUS_NO_MEMORY;
1649 if (!am_rodc) {
1650 status = authsam_update_lastlogon_timestamp(
1651 sam_ctx,
1652 msg_mod,
1653 domain_dn,
1654 lastLogonTimestamp,
1655 now,
1656 sync_interval_nt);
1657 if (!NT_STATUS_IS_OK(status)) {
1658 status = NT_STATUS_NO_MEMORY;
1659 goto error;
1661 } else {
1662 /* Perform the (async) SendToSAM calls for MS-SAMS */
1663 if (dbBadPwdCount != 0 && send_to_sam != NULL) {
1664 struct netr_SendToSamBase *base_msg;
1665 struct GUID guid = samdb_result_guid(msg, "objectGUID");
1667 base_msg = talloc_zero(send_to_sam_mem_ctx,
1668 struct netr_SendToSamBase);
1669 if (base_msg == NULL) {
1670 status = NT_STATUS_NO_MEMORY;
1671 goto error;
1674 base_msg->message_type = SendToSamResetBadPasswordCount;
1675 base_msg->message_size = 16;
1676 base_msg->message.reset_bad_password.guid = guid;
1677 *send_to_sam = base_msg;
1681 if (msg_mod->num_elements > 0) {
1682 unsigned int i;
1683 struct ldb_request *req;
1686 * If it turns out we are going to update the DB, go
1687 * back to the start, get a transaction and the
1688 * current DB state and try again
1690 if (txn_active == false) {
1691 need_db_reread = true;
1692 goto get_transaction;
1695 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
1696 for (i=0;i<msg_mod->num_elements;i++) {
1697 msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1700 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1701 msg_mod,
1702 NULL,
1703 NULL,
1704 ldb_op_default_callback,
1705 NULL);
1706 if (ret != LDB_SUCCESS) {
1707 status = NT_STATUS_INTERNAL_ERROR;
1708 goto error;
1711 ret = ldb_request_add_control(req,
1712 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1713 false, NULL);
1714 if (ret != LDB_SUCCESS) {
1715 TALLOC_FREE(req);
1716 status = NT_STATUS_INTERNAL_ERROR;
1717 goto error;
1720 * As we're in a transaction, make the ldb request directly
1721 * to avoid the nested transaction that would result if we
1722 * called dsdb_autotransaction_request
1724 ret = ldb_request(sam_ctx, req);
1725 if (ret == LDB_SUCCESS) {
1726 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1728 TALLOC_FREE(req);
1729 if (ret != LDB_SUCCESS) {
1730 status = NT_STATUS_INTERNAL_ERROR;
1731 goto error;
1734 status = authsam_clear_bad_password_indicator(sam_ctx, mem_ctx, msg);
1735 if (!NT_STATUS_IS_OK(status)) {
1736 goto error;
1740 * Note that we may not have updated the user record, but
1741 * committing the transaction in that case is still the correct
1742 * thing to do.
1743 * If the transaction was cancelled, this would be logged by
1744 * the dsdb audit log as a failure. When in fact it is expected
1745 * behaviour.
1747 * Thankfully both TDB and LMDB seem to optimise for the empty
1748 * transaction case
1750 exit:
1751 TALLOC_FREE(mem_ctx);
1753 if (txn_active == false) {
1754 return status;
1757 ret = ldb_transaction_commit(sam_ctx);
1758 if (ret != LDB_SUCCESS) {
1759 DBG_ERR("Error (%d) %s, committing transaction,"
1760 " while updating successful logon accounting"
1761 " for (%s)\n",
1762 ret,
1763 ldb_errstring(sam_ctx),
1764 ldb_dn_get_linearized(msg->dn));
1765 return NT_STATUS_INTERNAL_ERROR;
1767 return status;
1769 error:
1770 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
1771 "set lockoutTime on %s: %s\n",
1772 ldb_dn_get_linearized(msg->dn),
1773 ldb_errstring(sam_ctx) != NULL ?
1774 ldb_errstring(sam_ctx) :nt_errstr(status));
1775 if (txn_active) {
1776 ret = ldb_transaction_cancel(sam_ctx);
1777 if (ret != LDB_SUCCESS) {
1778 DBG_ERR("Error rolling back transaction,"
1779 " while updating bad password count"
1780 " on %s: %s\n",
1781 ldb_dn_get_linearized(msg->dn),
1782 ldb_errstring(sam_ctx));
1785 TALLOC_FREE(mem_ctx);
1786 return status;