s3: smbd: Fix schedule_smb2_aio_read() to allow the last read in a compound to go...
[Samba.git] / source4 / auth / sam.c
blobf2e5ced6caf84e7b5d055bba3476f217e88ffdb5
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)");
313 if (filter == NULL) {
314 return NT_STATUS_NO_MEMORY;
318 * Skip all builtin groups, they're added later.
320 filter = talloc_asprintf_append_buffer(filter,
321 "(!(groupType:1.2.840.113556.1.4.803:=%u))",
322 GROUP_TYPE_BUILTIN_LOCAL_GROUP);
323 if (filter == NULL) {
324 return NT_STATUS_NO_MEMORY;
327 * Only include security groups.
329 filter = talloc_asprintf_append_buffer(filter,
330 "(groupType:1.2.840.113556.1.4.803:=%u))",
331 GROUP_TYPE_SECURITY_ENABLED);
332 if (filter == NULL) {
333 return NT_STATUS_NO_MEMORY;
336 *_filter = filter;
337 return NT_STATUS_OK;
340 _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
341 struct ldb_context *sam_ctx,
342 const char *netbios_name,
343 const char *domain_name,
344 const char *dns_domain_name,
345 struct ldb_dn *domain_dn,
346 struct ldb_message *msg,
347 DATA_BLOB user_sess_key,
348 DATA_BLOB lm_sess_key,
349 struct auth_user_info_dc **_user_info_dc)
351 NTSTATUS status;
352 struct auth_user_info_dc *user_info_dc;
353 struct auth_user_info *info;
354 const char *str = NULL;
355 char *filter = NULL;
356 /* SIDs for the account and his primary group */
357 struct dom_sid *account_sid;
358 struct dom_sid_buf buf;
359 const char *primary_group_dn;
360 DATA_BLOB primary_group_blob;
361 /* SID structures for the expanded group memberships */
362 struct dom_sid *sids = NULL;
363 unsigned int num_sids = 0, i;
364 struct dom_sid *domain_sid;
365 TALLOC_CTX *tmp_ctx;
366 struct ldb_message_element *el;
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;
377 sids = talloc_array(user_info_dc, struct dom_sid, 2);
378 if (sids == NULL) {
379 TALLOC_FREE(user_info_dc);
380 return NT_STATUS_NO_MEMORY;
383 num_sids = 2;
385 account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
386 if (account_sid == NULL) {
387 TALLOC_FREE(user_info_dc);
388 return NT_STATUS_NO_MEMORY;
391 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
392 if (!NT_STATUS_IS_OK(status)) {
393 talloc_free(user_info_dc);
394 return status;
397 sids[PRIMARY_USER_SID_INDEX] = *account_sid;
398 sids[PRIMARY_GROUP_SID_INDEX] = *domain_sid;
399 sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX], ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
402 * Filter out builtin groups from this token. We will search
403 * for builtin groups later, and not include them in the PAC
404 * or SamLogon validation info.
406 status = authsam_domain_group_filter(tmp_ctx, &filter);
407 if (!NT_STATUS_IS_OK(status)) {
408 TALLOC_FREE(user_info_dc);
409 return status;
412 primary_group_dn = talloc_asprintf(
413 tmp_ctx,
414 "<SID=%s>",
415 dom_sid_str_buf(&sids[PRIMARY_GROUP_SID_INDEX], &buf));
416 if (primary_group_dn == NULL) {
417 TALLOC_FREE(user_info_dc);
418 return NT_STATUS_NO_MEMORY;
421 primary_group_blob = data_blob_string_const(primary_group_dn);
423 /* Expands the primary group - this function takes in
424 * memberOf-like values, so we fake one up with the
425 * <SID=S-...> format of DN and then let it expand
426 * them, as long as they meet the filter - so only
427 * domain groups, not builtin groups
429 * The primary group is still treated specially, so we set the
430 * 'only childs' flag to true
432 status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
433 user_info_dc, &sids, &num_sids);
434 if (!NT_STATUS_IS_OK(status)) {
435 talloc_free(user_info_dc);
436 return status;
439 /* Expands the additional groups */
440 el = ldb_msg_find_element(msg, "memberOf");
441 for (i = 0; el && i < el->num_values; i++) {
442 /* This function takes in memberOf values and expands
443 * them, as long as they meet the filter - so only
444 * domain groups, not builtin groups */
445 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
446 user_info_dc, &sids, &num_sids);
447 if (!NT_STATUS_IS_OK(status)) {
448 talloc_free(user_info_dc);
449 return status;
453 user_info_dc->sids = sids;
454 user_info_dc->num_sids = num_sids;
456 user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
457 NT_STATUS_HAVE_NO_MEMORY(user_info_dc->info);
459 str = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
460 info->account_name = talloc_strdup(info, str);
461 if (info->account_name == NULL) {
462 TALLOC_FREE(user_info_dc);
463 return NT_STATUS_NO_MEMORY;
466 str = ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL);
467 if (str == NULL && dns_domain_name != NULL) {
468 info->user_principal_name = talloc_asprintf(info, "%s@%s",
469 info->account_name,
470 dns_domain_name);
471 if (info->user_principal_name == NULL) {
472 TALLOC_FREE(user_info_dc);
473 return NT_STATUS_NO_MEMORY;
475 info->user_principal_constructed = true;
476 } else if (str != NULL) {
477 info->user_principal_name = talloc_strdup(info, str);
478 if (info->user_principal_name == NULL) {
479 TALLOC_FREE(user_info_dc);
480 return NT_STATUS_NO_MEMORY;
484 info->domain_name = talloc_strdup(info, domain_name);
485 if (info->domain_name == NULL) {
486 TALLOC_FREE(user_info_dc);
487 return NT_STATUS_NO_MEMORY;
490 if (dns_domain_name != NULL) {
491 info->dns_domain_name = talloc_strdup(info, dns_domain_name);
492 if (info->dns_domain_name == NULL) {
493 TALLOC_FREE(user_info_dc);
494 return NT_STATUS_NO_MEMORY;
498 str = ldb_msg_find_attr_as_string(msg, "displayName", "");
499 info->full_name = talloc_strdup(info, str);
500 if (info->full_name == NULL) {
501 TALLOC_FREE(user_info_dc);
502 return NT_STATUS_NO_MEMORY;
505 str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
506 info->logon_script = talloc_strdup(info, str);
507 if (info->logon_script == NULL) {
508 TALLOC_FREE(user_info_dc);
509 return NT_STATUS_NO_MEMORY;
512 str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
513 info->profile_path = talloc_strdup(info, str);
514 if (info->profile_path == NULL) {
515 TALLOC_FREE(user_info_dc);
516 return NT_STATUS_NO_MEMORY;
519 str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
520 info->home_directory = talloc_strdup(info, str);
521 if (info->home_directory == NULL) {
522 TALLOC_FREE(user_info_dc);
523 return NT_STATUS_NO_MEMORY;
526 str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
527 info->home_drive = talloc_strdup(info, str);
528 if (info->home_drive == NULL) {
529 TALLOC_FREE(user_info_dc);
530 return NT_STATUS_NO_MEMORY;
533 info->logon_server = talloc_strdup(info, netbios_name);
534 if (info->logon_server == NULL) {
535 TALLOC_FREE(user_info_dc);
536 return NT_STATUS_NO_MEMORY;
539 info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
540 info->last_logoff = samdb_result_last_logoff(msg);
541 info->acct_expiry = samdb_result_account_expires(msg);
542 info->last_password_change = samdb_result_nttime(msg,
543 "pwdLastSet", 0);
544 info->allow_password_change
545 = samdb_result_allow_password_change(sam_ctx, mem_ctx,
546 domain_dn, msg, "pwdLastSet");
547 info->force_password_change = samdb_result_nttime(msg,
548 "msDS-UserPasswordExpiryTimeComputed", 0);
549 info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
550 info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
553 info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
555 user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
556 user_sess_key.data,
557 user_sess_key.length);
558 if (user_sess_key.data) {
559 if (user_info_dc->user_session_key.data == NULL) {
560 TALLOC_FREE(user_info_dc);
561 return NT_STATUS_NO_MEMORY;
564 user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
565 lm_sess_key.data,
566 lm_sess_key.length);
567 if (lm_sess_key.data) {
568 if (user_info_dc->lm_session_key.data == NULL) {
569 TALLOC_FREE(user_info_dc);
570 return NT_STATUS_NO_MEMORY;
574 if (info->acct_flags & ACB_SVRTRUST) {
575 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
576 PAC */
577 user_info_dc->sids = talloc_realloc(user_info_dc,
578 user_info_dc->sids,
579 struct dom_sid,
580 user_info_dc->num_sids+1);
581 if (user_info_dc->sids == NULL) {
582 TALLOC_FREE(user_info_dc);
583 return NT_STATUS_NO_MEMORY;
585 user_info_dc->sids[user_info_dc->num_sids] = global_sid_Enterprise_DCs;
586 user_info_dc->num_sids++;
589 if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
590 (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
591 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
592 user_info_dc->sids = talloc_realloc(user_info_dc,
593 user_info_dc->sids,
594 struct dom_sid,
595 user_info_dc->num_sids+1);
596 if (user_info_dc->sids == NULL) {
597 TALLOC_FREE(user_info_dc);
598 return NT_STATUS_NO_MEMORY;
600 user_info_dc->sids[user_info_dc->num_sids] = *domain_sid;
601 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids],
602 DOMAIN_RID_ENTERPRISE_READONLY_DCS);
603 user_info_dc->num_sids++;
606 info->authenticated = true;
608 talloc_free(tmp_ctx);
609 *_user_info_dc = user_info_dc;
611 return NT_STATUS_OK;
614 _PUBLIC_ NTSTATUS authsam_update_user_info_dc(TALLOC_CTX *mem_ctx,
615 struct ldb_context *sam_ctx,
616 struct auth_user_info_dc *user_info_dc)
618 char *filter = NULL;
619 NTSTATUS status;
620 uint32_t i;
621 uint32_t n = 0;
624 * This function exists to expand group memberships
625 * in the local domain (forest), as the token
626 * may come from a different domain.
630 * Filter out builtin groups from this token. We will search
631 * for builtin groups later.
633 status = authsam_domain_group_filter(mem_ctx, &filter);
634 if (!NT_STATUS_IS_OK(status)) {
635 TALLOC_FREE(user_info_dc);
636 return status;
640 * We loop only over the existing number of
641 * sids.
643 n = user_info_dc->num_sids;
644 for (i = 0; i < n; i++) {
645 struct dom_sid *sid = &user_info_dc->sids[i];
646 struct dom_sid_buf sid_buf;
647 char dn_str[sizeof(sid_buf.buf)*2];
648 DATA_BLOB dn_blob = data_blob_null;
650 snprintf(dn_str,
651 sizeof(dn_str),
652 "<SID=%s>",
653 dom_sid_str_buf(sid, &sid_buf));
654 dn_blob = data_blob_string_const(dn_str);
657 * We already have the SID in the token, so set
658 * 'only childs' flag to true and add all
659 * groups which match the filter.
661 status = dsdb_expand_nested_groups(sam_ctx, &dn_blob,
662 true, filter,
663 user_info_dc,
664 &user_info_dc->sids,
665 &user_info_dc->num_sids);
666 if (!NT_STATUS_IS_OK(status)) {
667 return status;
671 return NT_STATUS_OK;
674 NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
675 TALLOC_CTX *mem_ctx, const char *principal,
676 const char **attrs,
677 struct ldb_dn **domain_dn,
678 struct ldb_message **msg)
680 struct ldb_dn *user_dn;
681 NTSTATUS nt_status;
682 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
683 int ret;
685 if (!tmp_ctx) {
686 return NT_STATUS_NO_MEMORY;
689 nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
690 &user_dn, domain_dn);
691 if (!NT_STATUS_IS_OK(nt_status)) {
692 talloc_free(tmp_ctx);
693 return nt_status;
696 /* pull the user attributes */
697 ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
698 LDB_SCOPE_BASE, attrs,
699 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
700 "(objectClass=*)");
701 if (ret != LDB_SUCCESS) {
702 talloc_free(tmp_ctx);
703 return NT_STATUS_INTERNAL_DB_CORRUPTION;
705 talloc_steal(mem_ctx, *msg);
706 talloc_steal(mem_ctx, *domain_dn);
707 talloc_free(tmp_ctx);
709 return NT_STATUS_OK;
712 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
714 Supply either a principal or a DN
716 NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
717 struct loadparm_context *lp_ctx,
718 struct ldb_context *sam_ctx,
719 const char *principal,
720 struct ldb_dn *user_dn,
721 struct auth_user_info_dc **user_info_dc)
723 NTSTATUS nt_status;
724 DATA_BLOB user_sess_key = data_blob(NULL, 0);
725 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
727 struct ldb_message *msg;
728 struct ldb_dn *domain_dn;
730 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
731 if (!tmp_ctx) {
732 return NT_STATUS_NO_MEMORY;
735 if (principal) {
736 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
737 user_attrs, &domain_dn, &msg);
738 if (!NT_STATUS_IS_OK(nt_status)) {
739 talloc_free(tmp_ctx);
740 return nt_status;
742 } else if (user_dn) {
743 struct dom_sid *user_sid, *domain_sid;
744 int ret;
745 /* pull the user attributes */
746 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
747 LDB_SCOPE_BASE, user_attrs,
748 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
749 "(objectClass=*)");
750 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
751 talloc_free(tmp_ctx);
752 return NT_STATUS_NO_SUCH_USER;
753 } else if (ret != LDB_SUCCESS) {
754 talloc_free(tmp_ctx);
755 return NT_STATUS_INTERNAL_DB_CORRUPTION;
758 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
760 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
761 if (!NT_STATUS_IS_OK(nt_status)) {
762 return nt_status;
765 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
766 "(&(objectSid=%s)(objectClass=domain))",
767 ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
768 if (!domain_dn) {
769 struct dom_sid_buf buf;
770 DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
771 dom_sid_str_buf(domain_sid, &buf)));
772 return NT_STATUS_NO_SUCH_USER;
775 } else {
776 return NT_STATUS_INVALID_PARAMETER;
779 nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
780 lpcfg_netbios_name(lp_ctx),
781 lpcfg_sam_name(lp_ctx),
782 lpcfg_sam_dnsname(lp_ctx),
783 domain_dn,
784 msg,
785 user_sess_key, lm_sess_key,
786 user_info_dc);
787 if (!NT_STATUS_IS_OK(nt_status)) {
788 talloc_free(tmp_ctx);
789 return nt_status;
792 talloc_steal(mem_ctx, *user_info_dc);
793 talloc_free(tmp_ctx);
795 return NT_STATUS_OK;
799 * Returns the details for the Password Settings Object (PSO), if one applies
800 * the user.
802 static int authsam_get_user_pso(struct ldb_context *sam_ctx,
803 TALLOC_CTX *mem_ctx,
804 struct ldb_message *user_msg,
805 struct ldb_message **pso_msg)
807 const char *attrs[] = { "msDS-LockoutThreshold",
808 "msDS-LockoutObservationWindow",
809 NULL };
810 struct ldb_dn *pso_dn = NULL;
811 struct ldb_result *res = NULL;
812 int ret;
814 /* check if the user has a PSO that applies to it */
815 pso_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, user_msg,
816 "msDS-ResultantPSO");
818 if (pso_dn != NULL) {
819 ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, pso_dn, attrs, 0);
820 if (ret != LDB_SUCCESS) {
821 return ret;
824 *pso_msg = res->msgs[0];
827 return LDB_SUCCESS;
831 * Re-read the bad password and successful logon data for a user.
833 * The DN in the passed user record should contain the "objectGUID" in case the
834 * object DN has changed.
836 NTSTATUS authsam_reread_user_logon_data(
837 struct ldb_context *sam_ctx,
838 TALLOC_CTX *mem_ctx,
839 const struct ldb_message *user_msg,
840 struct ldb_message **current)
842 const struct ldb_val *v = NULL;
843 struct ldb_result *res = NULL;
844 uint16_t acct_flags = 0;
845 const char *attr_name = "msDS-User-Account-Control-Computed";
847 int ret;
850 * Re-read the account details, using the GUID in case the DN
851 * is being changed (this is automatic in LDB because the
852 * original search also used DSDB_SEARCH_SHOW_EXTENDED_DN)
854 * We re read all the attributes in user_attrs, rather than using a
855 * subset to ensure that we can reuse existing validation code.
857 ret = dsdb_search_dn(sam_ctx,
858 mem_ctx,
859 &res,
860 user_msg->dn,
861 user_attrs,
862 DSDB_SEARCH_SHOW_EXTENDED_DN);
863 if (ret != LDB_SUCCESS) {
864 DBG_ERR("Unable to re-read account control data for %s\n",
865 ldb_dn_get_linearized(user_msg->dn));
866 return NT_STATUS_INTERNAL_ERROR;
870 * Ensure the account has not been locked out by another request
872 v = ldb_msg_find_ldb_val(res->msgs[0], attr_name);
873 if (v == NULL || v->data == NULL) {
874 DBG_ERR("No %s attribute for %s\n",
875 attr_name,
876 ldb_dn_get_linearized(user_msg->dn));
877 TALLOC_FREE(res);
878 return NT_STATUS_INTERNAL_ERROR;
880 acct_flags = samdb_result_acct_flags(res->msgs[0], attr_name);
881 if (acct_flags & ACB_AUTOLOCK) {
882 DBG_WARNING(
883 "Account for user %s was locked out.\n",
884 ldb_dn_get_linearized(user_msg->dn));
885 TALLOC_FREE(res);
886 return NT_STATUS_ACCOUNT_LOCKED_OUT;
888 *current = talloc_steal(mem_ctx, res->msgs[0]);
889 TALLOC_FREE(res);
890 return NT_STATUS_OK;
893 static struct db_context *authsam_get_bad_password_db(
894 TALLOC_CTX *mem_ctx,
895 struct ldb_context *sam_ctx)
897 struct loadparm_context *lp_ctx = NULL;
898 const char *db_name = "bad_password";
899 struct db_context *db_ctx = NULL;
901 lp_ctx = ldb_get_opaque(sam_ctx, "loadparm");
902 if (lp_ctx == NULL) {
903 DBG_ERR("Unable to get loadparm_context\n");
904 return NULL;
907 db_ctx = cluster_db_tmp_open(mem_ctx, lp_ctx, db_name, TDB_DEFAULT);
908 if (db_ctx == NULL) {
909 DBG_ERR("Unable to open bad password attempts database\n");
910 return NULL;
912 return db_ctx;
915 static NTSTATUS get_object_sid_as_tdb_data(
916 TALLOC_CTX *mem_ctx,
917 const struct ldb_message *msg,
918 struct dom_sid_buf *buf,
919 TDB_DATA *key)
921 struct dom_sid *objectsid = NULL;
924 * Convert the objectSID to a human readable form to
925 * make debugging easier
927 objectsid = samdb_result_dom_sid(mem_ctx, msg, "objectSID");
928 if (objectsid == NULL) {
929 DBG_ERR("Unable to extract objectSID\n");
930 return NT_STATUS_INTERNAL_ERROR;
932 dom_sid_str_buf(objectsid, buf);
933 key->dptr = (unsigned char *)buf->buf;
934 key->dsize = strlen(buf->buf);
936 talloc_free(objectsid);
938 return NT_STATUS_OK;
942 * Add the users objectSID to the bad password attempt database
943 * to indicate that last authentication failed due to a bad password
945 static NTSTATUS authsam_set_bad_password_indicator(
946 struct ldb_context *sam_ctx,
947 TALLOC_CTX *mem_ctx,
948 const struct ldb_message *msg)
950 NTSTATUS status = NT_STATUS_OK;
951 struct dom_sid_buf buf;
952 TDB_DATA key = {0};
953 TDB_DATA value = {0};
954 struct db_context *db = NULL;
956 TALLOC_CTX *ctx = talloc_new(mem_ctx);
957 if (ctx == NULL) {
958 return NT_STATUS_NO_MEMORY;
961 db = authsam_get_bad_password_db(ctx, sam_ctx);
962 if (db == NULL) {
963 status = NT_STATUS_INTERNAL_ERROR;
964 goto exit;
967 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
968 if (!NT_STATUS_IS_OK(status)) {
969 goto exit;
972 status = dbwrap_store(db, key, value, 0);
973 if (!NT_STATUS_IS_OK(status)) {
974 DBG_ERR("Unable to store bad password indicator\n");
976 exit:
977 talloc_free(ctx);
978 return status;
982 * see if the users objectSID is in the bad password attempt database
984 static NTSTATUS authsam_check_bad_password_indicator(
985 struct ldb_context *sam_ctx,
986 TALLOC_CTX *mem_ctx,
987 bool *exists,
988 const struct ldb_message *msg)
990 NTSTATUS status = NT_STATUS_OK;
991 struct dom_sid_buf buf;
992 TDB_DATA key = {0};
993 struct db_context *db = NULL;
995 TALLOC_CTX *ctx = talloc_new(mem_ctx);
996 if (ctx == NULL) {
997 return NT_STATUS_NO_MEMORY;
1000 db = authsam_get_bad_password_db(ctx, sam_ctx);
1001 if (db == NULL) {
1002 status = NT_STATUS_INTERNAL_ERROR;
1003 goto exit;
1006 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1007 if (!NT_STATUS_IS_OK(status)) {
1008 goto exit;
1011 *exists = dbwrap_exists(db, key);
1012 exit:
1013 talloc_free(ctx);
1014 return status;
1018 * Remove the users objectSID to the bad password attempt database
1019 * to indicate that last authentication succeeded.
1021 static NTSTATUS authsam_clear_bad_password_indicator(
1022 struct ldb_context *sam_ctx,
1023 TALLOC_CTX *mem_ctx,
1024 const struct ldb_message *msg)
1026 NTSTATUS status = NT_STATUS_OK;
1027 struct dom_sid_buf buf;
1028 TDB_DATA key = {0};
1029 struct db_context *db = NULL;
1031 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1032 if (ctx == NULL) {
1033 return NT_STATUS_NO_MEMORY;
1036 db = authsam_get_bad_password_db(ctx, sam_ctx);
1037 if (db == NULL) {
1038 status = NT_STATUS_INTERNAL_ERROR;
1039 goto exit;
1042 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1043 if (!NT_STATUS_IS_OK(status)) {
1044 goto exit;
1047 status = dbwrap_delete(db, key);
1048 if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, status)) {
1050 * Ok there was no bad password indicator this is expected
1052 status = NT_STATUS_OK;
1054 if (NT_STATUS_IS_ERR(status)) {
1055 DBG_ERR("Unable to delete bad password indicator, %s %s\n",
1056 nt_errstr(status),
1057 get_friendly_nt_error_msg(status));
1059 exit:
1060 talloc_free(ctx);
1061 return status;
1064 NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
1065 struct ldb_message *msg,
1066 struct ldb_dn *domain_dn)
1068 const char *attrs[] = { "lockoutThreshold",
1069 "lockOutObservationWindow",
1070 "lockoutDuration",
1071 "pwdProperties",
1072 NULL };
1073 int ret;
1074 NTSTATUS status;
1075 struct ldb_result *domain_res;
1076 struct ldb_message *msg_mod = NULL;
1077 struct ldb_message *current = NULL;
1078 struct ldb_message *pso_msg = NULL;
1079 bool txn_active = false;
1080 TALLOC_CTX *mem_ctx;
1082 mem_ctx = talloc_new(msg);
1083 if (mem_ctx == NULL) {
1084 return NT_STATUS_NO_MEMORY;
1087 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
1088 if (ret != LDB_SUCCESS) {
1089 TALLOC_FREE(mem_ctx);
1090 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1093 ret = authsam_get_user_pso(sam_ctx, mem_ctx, msg, &pso_msg);
1094 if (ret != LDB_SUCCESS) {
1097 * fallback to using the domain defaults so that we still
1098 * record the bad password attempt
1100 DBG_ERR("Error (%d) checking PSO for %s\n",
1101 ret, ldb_dn_get_linearized(msg->dn));
1105 * To ensure that the bad password count is updated atomically,
1106 * we need to:
1107 * begin a transaction
1108 * re-read the account details,
1109 * using the <GUID= part of the DN
1110 * update the bad password count
1111 * commit the transaction.
1115 * Start a new transaction
1117 ret = ldb_transaction_start(sam_ctx);
1118 if (ret != LDB_SUCCESS) {
1119 status = NT_STATUS_INTERNAL_ERROR;
1120 goto error;
1122 txn_active = true;
1125 * Re-read the account details, using the GUID in case the DN
1126 * is being changed.
1128 status = authsam_reread_user_logon_data(
1129 sam_ctx, mem_ctx, msg, &current);
1130 if (!NT_STATUS_IS_OK(status)) {
1131 /* The re-read can return account locked out, as well
1132 * as an internal error
1134 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
1136 * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
1137 * the transaction. Again to avoid cluttering the
1138 * audit logs with spurious errors
1140 goto exit;
1142 goto error;
1146 * Update the bad password count and if required lock the account
1148 status = dsdb_update_bad_pwd_count(
1149 mem_ctx,
1150 sam_ctx,
1151 current,
1152 domain_res->msgs[0],
1153 pso_msg,
1154 &msg_mod);
1155 if (!NT_STATUS_IS_OK(status)) {
1156 status = NT_STATUS_INTERNAL_ERROR;
1157 goto error;
1161 * Write the data back to disk if required.
1163 if (msg_mod != NULL) {
1164 struct ldb_request *req;
1166 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1167 msg_mod,
1168 NULL,
1169 NULL,
1170 ldb_op_default_callback,
1171 NULL);
1172 if (ret != LDB_SUCCESS) {
1173 TALLOC_FREE(msg_mod);
1174 status = NT_STATUS_INTERNAL_ERROR;
1175 goto error;
1178 ret = ldb_request_add_control(req,
1179 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1180 false, NULL);
1181 if (ret != LDB_SUCCESS) {
1182 talloc_free(req);
1183 status = NT_STATUS_INTERNAL_ERROR;
1184 goto error;
1188 * As we're in a transaction, make the ldb request directly
1189 * to avoid the nested transaction that would result if we
1190 * called dsdb_autotransaction_request
1192 ret = ldb_request(sam_ctx, req);
1193 if (ret == LDB_SUCCESS) {
1194 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1196 talloc_free(req);
1197 if (ret != LDB_SUCCESS) {
1198 status = NT_STATUS_INTERNAL_ERROR;
1199 goto error;
1201 status = authsam_set_bad_password_indicator(
1202 sam_ctx, mem_ctx, msg);
1203 if (!NT_STATUS_IS_OK(status)) {
1204 goto error;
1208 * Note that we may not have updated the user record, but
1209 * committing the transaction in that case is still the correct
1210 * thing to do.
1211 * If the transaction was cancelled, this would be logged by
1212 * the dsdb audit log as a failure. When in fact it is expected
1213 * behaviour.
1215 exit:
1216 TALLOC_FREE(mem_ctx);
1217 ret = ldb_transaction_commit(sam_ctx);
1218 if (ret != LDB_SUCCESS) {
1219 DBG_ERR("Error (%d) %s, committing transaction,"
1220 " while updating bad password count"
1221 " for (%s)\n",
1222 ret,
1223 ldb_errstring(sam_ctx),
1224 ldb_dn_get_linearized(msg->dn));
1225 return NT_STATUS_INTERNAL_ERROR;
1227 return status;
1229 error:
1230 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
1231 "set lockoutTime on %s: %s\n",
1232 ldb_dn_get_linearized(msg->dn),
1233 ldb_errstring(sam_ctx) != NULL ?
1234 ldb_errstring(sam_ctx) :nt_errstr(status));
1235 if (txn_active) {
1236 ret = ldb_transaction_cancel(sam_ctx);
1237 if (ret != LDB_SUCCESS) {
1238 DBG_ERR("Error rolling back transaction,"
1239 " while updating bad password count"
1240 " on %s: %s\n",
1241 ldb_dn_get_linearized(msg->dn),
1242 ldb_errstring(sam_ctx));
1245 TALLOC_FREE(mem_ctx);
1246 return status;
1251 * msDS-LogonTimeSyncInterval is an int32_t number of days.
1253 * The docs say: "the initial update, after the domain functional
1254 * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
1255 * 14 days minus a random percentage of 5 days", but we aren't doing
1256 * that. The blogosphere seems to think that this randomised update
1257 * happens everytime, but [MS-ADA1] doesn't agree.
1259 * Dochelp referred us to the following blog post:
1260 * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
1262 * when msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
1263 * not changed.
1266 static NTSTATUS authsam_calculate_lastlogon_sync_interval(
1267 struct ldb_context *sam_ctx,
1268 TALLOC_CTX *ctx,
1269 struct ldb_dn *domain_dn,
1270 NTTIME *sync_interval_nt)
1272 static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
1273 NULL };
1274 int ret;
1275 struct ldb_result *domain_res = NULL;
1276 TALLOC_CTX *mem_ctx = NULL;
1277 uint32_t sync_interval;
1279 mem_ctx = talloc_new(ctx);
1280 if (mem_ctx == NULL) {
1281 return NT_STATUS_NO_MEMORY;
1284 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
1286 if (ret != LDB_SUCCESS || domain_res->count != 1) {
1287 TALLOC_FREE(mem_ctx);
1288 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1291 sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
1292 "msDS-LogonTimeSyncInterval",
1293 14);
1294 DEBUG(5, ("sync interval is %d\n", sync_interval));
1295 if (sync_interval >= 5){
1297 * Subtract "a random percentage of 5" days. Presumably this
1298 * percentage is between 0 and 100, and modulus is accurate
1299 * enough.
1301 uint32_t r = generate_random() % 6;
1302 sync_interval -= r;
1303 DBG_INFO("randomised sync interval is %d (-%d)\n", sync_interval, r);
1305 /* In the case where sync_interval < 5 there is no randomisation */
1308 * msDS-LogonTimeSyncInterval is an int32_t number of days,
1309 * while lastLogonTimestamp (to be updated) is in the 64 bit
1310 * 100ns NTTIME format so we must convert.
1312 *sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
1313 TALLOC_FREE(mem_ctx);
1314 return NT_STATUS_OK;
1319 * We only set lastLogonTimestamp if the current value is older than
1320 * now - msDS-LogonTimeSyncInterval days.
1322 * lastLogonTimestamp is in the 64 bit 100ns NTTIME format
1324 static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
1325 struct ldb_message *msg_mod,
1326 struct ldb_dn *domain_dn,
1327 NTTIME old_timestamp,
1328 NTTIME now,
1329 NTTIME sync_interval_nt)
1331 int ret;
1332 DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
1333 (long long int)old_timestamp,
1334 (long long int)(now - sync_interval_nt),
1335 (long long int)(old_timestamp - now + sync_interval_nt)));
1337 if (sync_interval_nt == 0){
1339 * Setting msDS-LogonTimeSyncInterval to zero is how you ask
1340 * that nothing happens here.
1342 return NT_STATUS_OK;
1344 if (old_timestamp > now){
1345 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
1346 (long long int)old_timestamp, (long long int)now));
1347 /* then what? */
1349 } else if (old_timestamp < now - sync_interval_nt){
1350 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
1351 (long long int)now));
1353 /* The time has come to update lastLogonTimestamp */
1354 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1355 "lastLogonTimestamp", now);
1357 if (ret != LDB_SUCCESS) {
1358 return NT_STATUS_NO_MEMORY;
1361 return NT_STATUS_OK;
1364 /****************************************************************************
1365 Look for the specified user in the sam, return ldb result structures
1366 ****************************************************************************/
1368 NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
1369 const char *account_name,
1370 struct ldb_dn *domain_dn,
1371 struct ldb_message **ret_msg)
1373 int ret;
1375 /* pull the user attributes */
1376 ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
1377 user_attrs,
1378 DSDB_SEARCH_SHOW_EXTENDED_DN,
1379 "(&(sAMAccountName=%s)(objectclass=user))",
1380 ldb_binary_encode_string(mem_ctx, account_name));
1381 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1382 DEBUG(3,("sam_search_user: Couldn't find user [%s] in samdb, under %s\n",
1383 account_name, ldb_dn_get_linearized(domain_dn)));
1384 return NT_STATUS_NO_SUCH_USER;
1386 if (ret != LDB_SUCCESS) {
1387 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1390 return NT_STATUS_OK;
1394 /* Reset the badPwdCount to zero and update the lastLogon time. */
1395 NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
1396 const struct ldb_message *msg,
1397 struct ldb_dn *domain_dn,
1398 bool interactive_or_kerberos,
1399 TALLOC_CTX *send_to_sam_mem_ctx,
1400 struct netr_SendToSamBase **send_to_sam)
1402 int ret;
1403 NTSTATUS status;
1404 int badPwdCount;
1405 int dbBadPwdCount;
1406 int64_t lockoutTime;
1407 struct ldb_message *msg_mod;
1408 TALLOC_CTX *mem_ctx;
1409 struct timeval tv_now;
1410 NTTIME now;
1411 NTTIME lastLogonTimestamp;
1412 int64_t lockOutObservationWindow;
1413 NTTIME sync_interval_nt = 0;
1414 bool am_rodc = false;
1415 bool txn_active = false;
1416 bool need_db_reread;
1418 mem_ctx = talloc_new(msg);
1419 if (mem_ctx == NULL) {
1420 return NT_STATUS_NO_MEMORY;
1424 * Any update of the last logon data, needs to be done inside a
1425 * transaction.
1426 * And the user data needs to be re-read, and the account re-checked
1427 * for lockout.
1429 * Howevver we have long-running transactions like replication
1430 * that could otherwise grind the system to a halt so we first
1431 * determine if *this* account has seen a bad password,
1432 * otherwise we only start a transaction if there was a need
1433 * (because a change was to be made).
1436 status = authsam_check_bad_password_indicator(
1437 sam_ctx, mem_ctx, &need_db_reread, msg);
1438 if (!NT_STATUS_IS_OK(status)) {
1439 return status;
1442 if (interactive_or_kerberos == false) {
1444 * Avoid calculating this twice, it reads the PSO. A
1445 * race on this is unimportant.
1447 lockOutObservationWindow
1448 = samdb_result_msds_LockoutObservationWindow(
1449 sam_ctx, mem_ctx, domain_dn, msg);
1452 ret = samdb_rodc(sam_ctx, &am_rodc);
1453 if (ret != LDB_SUCCESS) {
1454 status = NT_STATUS_INTERNAL_ERROR;
1455 goto error;
1458 if (!am_rodc) {
1460 * Avoid reading the main domain DN twice. A race on
1461 * this is unimportant.
1463 status = authsam_calculate_lastlogon_sync_interval(
1464 sam_ctx, mem_ctx, domain_dn, &sync_interval_nt);
1466 if (!NT_STATUS_IS_OK(status)) {
1467 status = NT_STATUS_INTERNAL_ERROR;
1468 goto error;
1472 get_transaction:
1474 if (need_db_reread) {
1475 struct ldb_message *current = NULL;
1478 * Start a new transaction
1480 ret = ldb_transaction_start(sam_ctx);
1481 if (ret != LDB_SUCCESS) {
1482 status = NT_STATUS_INTERNAL_ERROR;
1483 goto error;
1486 txn_active = true;
1489 * Re-read the account details, using the GUID
1490 * embedded in DN so this is safe against a race where
1491 * it is being renamed.
1493 status = authsam_reread_user_logon_data(
1494 sam_ctx, mem_ctx, msg, &current);
1495 if (!NT_STATUS_IS_OK(status)) {
1497 * The re-read can return account locked out, as well
1498 * as an internal error
1500 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
1502 * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
1503 * the transaction. Again to avoid cluttering the
1504 * audit logs with spurious errors
1506 goto exit;
1508 goto error;
1510 msg = current;
1513 lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
1514 dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
1515 tv_now = timeval_current();
1516 now = timeval_to_nttime(&tv_now);
1518 if (interactive_or_kerberos) {
1519 badPwdCount = dbBadPwdCount;
1520 } else {
1522 * We get lockOutObservationWindow above, before the
1523 * transaction
1525 badPwdCount = dsdb_effective_badPwdCount(
1526 msg, lockOutObservationWindow, now);
1528 lastLogonTimestamp =
1529 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
1531 DEBUG(5, ("lastLogonTimestamp is %lld\n",
1532 (long long int)lastLogonTimestamp));
1534 msg_mod = ldb_msg_new(mem_ctx);
1535 if (msg_mod == NULL) {
1536 status = NT_STATUS_NO_MEMORY;
1537 goto error;
1541 * By using the DN from msg->dn directly, we allow LDB to
1542 * prefer the embedded GUID form, so this is actually quite
1543 * safe even in the case where DN has been changed
1545 msg_mod->dn = msg->dn;
1547 if (lockoutTime != 0) {
1549 * This implies "badPwdCount" = 0, see samldb_lockout_time()
1551 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
1552 if (ret != LDB_SUCCESS) {
1553 status = NT_STATUS_NO_MEMORY;
1554 goto error;
1556 } else if (badPwdCount != 0) {
1557 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
1558 if (ret != LDB_SUCCESS) {
1559 status = NT_STATUS_NO_MEMORY;
1560 goto error;
1564 if (interactive_or_kerberos ||
1565 (badPwdCount != 0 && lockoutTime == 0)) {
1566 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1567 "lastLogon", now);
1568 if (ret != LDB_SUCCESS) {
1569 status = NT_STATUS_NO_MEMORY;
1570 goto error;
1574 if (interactive_or_kerberos) {
1575 int logonCount;
1577 logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
1579 logonCount += 1;
1581 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1582 "logonCount", logonCount);
1583 if (ret != LDB_SUCCESS) {
1584 status = NT_STATUS_NO_MEMORY;
1585 goto error;
1587 } else {
1588 /* Set an unset logonCount to 0 on first successful login */
1589 if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
1590 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1591 "logonCount", 0);
1592 if (ret != LDB_SUCCESS) {
1593 TALLOC_FREE(mem_ctx);
1594 return NT_STATUS_NO_MEMORY;
1599 if (!am_rodc) {
1600 status = authsam_update_lastlogon_timestamp(
1601 sam_ctx,
1602 msg_mod,
1603 domain_dn,
1604 lastLogonTimestamp,
1605 now,
1606 sync_interval_nt);
1607 if (!NT_STATUS_IS_OK(status)) {
1608 status = NT_STATUS_NO_MEMORY;
1609 goto error;
1611 } else {
1612 /* Perform the (async) SendToSAM calls for MS-SAMS */
1613 if (dbBadPwdCount != 0 && send_to_sam != NULL) {
1614 struct netr_SendToSamBase *base_msg;
1615 struct GUID guid = samdb_result_guid(msg, "objectGUID");
1617 base_msg = talloc_zero(send_to_sam_mem_ctx,
1618 struct netr_SendToSamBase);
1619 if (base_msg == NULL) {
1620 status = NT_STATUS_NO_MEMORY;
1621 goto error;
1624 base_msg->message_type = SendToSamResetBadPasswordCount;
1625 base_msg->message_size = 16;
1626 base_msg->message.reset_bad_password.guid = guid;
1627 *send_to_sam = base_msg;
1631 if (msg_mod->num_elements > 0) {
1632 unsigned int i;
1633 struct ldb_request *req;
1636 * If it turns out we are going to update the DB, go
1637 * back to the start, get a transaction and the
1638 * current DB state and try again
1640 if (txn_active == false) {
1641 need_db_reread = true;
1642 goto get_transaction;
1645 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
1646 for (i=0;i<msg_mod->num_elements;i++) {
1647 msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1650 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1651 msg_mod,
1652 NULL,
1653 NULL,
1654 ldb_op_default_callback,
1655 NULL);
1656 if (ret != LDB_SUCCESS) {
1657 status = NT_STATUS_INTERNAL_ERROR;
1658 goto error;
1661 ret = ldb_request_add_control(req,
1662 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1663 false, NULL);
1664 if (ret != LDB_SUCCESS) {
1665 TALLOC_FREE(req);
1666 status = NT_STATUS_INTERNAL_ERROR;
1667 goto error;
1670 * As we're in a transaction, make the ldb request directly
1671 * to avoid the nested transaction that would result if we
1672 * called dsdb_autotransaction_request
1674 ret = ldb_request(sam_ctx, req);
1675 if (ret == LDB_SUCCESS) {
1676 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1678 TALLOC_FREE(req);
1679 if (ret != LDB_SUCCESS) {
1680 status = NT_STATUS_INTERNAL_ERROR;
1681 goto error;
1684 status = authsam_clear_bad_password_indicator(sam_ctx, mem_ctx, msg);
1685 if (!NT_STATUS_IS_OK(status)) {
1686 goto error;
1690 * Note that we may not have updated the user record, but
1691 * committing the transaction in that case is still the correct
1692 * thing to do.
1693 * If the transaction was cancelled, this would be logged by
1694 * the dsdb audit log as a failure. When in fact it is expected
1695 * behaviour.
1697 * Thankfully both TDB and LMDB seem to optimise for the empty
1698 * transaction case
1700 exit:
1701 TALLOC_FREE(mem_ctx);
1703 if (txn_active == false) {
1704 return status;
1707 ret = ldb_transaction_commit(sam_ctx);
1708 if (ret != LDB_SUCCESS) {
1709 DBG_ERR("Error (%d) %s, committing transaction,"
1710 " while updating successful logon accounting"
1711 " for (%s)\n",
1712 ret,
1713 ldb_errstring(sam_ctx),
1714 ldb_dn_get_linearized(msg->dn));
1715 return NT_STATUS_INTERNAL_ERROR;
1717 return status;
1719 error:
1720 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
1721 "set lockoutTime on %s: %s\n",
1722 ldb_dn_get_linearized(msg->dn),
1723 ldb_errstring(sam_ctx) != NULL ?
1724 ldb_errstring(sam_ctx) :nt_errstr(status));
1725 if (txn_active) {
1726 ret = ldb_transaction_cancel(sam_ctx);
1727 if (ret != LDB_SUCCESS) {
1728 DBG_ERR("Error rolling back transaction,"
1729 " while updating bad password count"
1730 " on %s: %s\n",
1731 ldb_dn_get_linearized(msg->dn),
1732 ldb_errstring(sam_ctx));
1735 TALLOC_FREE(mem_ctx);
1736 return status;