smbd: Don't talloc_zero where we assign the struct a line below
[Samba.git] / source4 / auth / sam.c
blobf21827118cf4f811cab99ad07adcf37deeccb443
1 /*
2 Unix SMB/CIFS implementation.
3 Password and authentication handling
4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2010
5 Copyright (C) Gerald Carter 2003
6 Copyright (C) Stefan Metzmacher 2005
7 Copyright (C) Matthias Dieter Wallnöfer 2009
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "system/time.h"
25 #include "auth/auth.h"
26 #include <ldb.h>
27 #include "dsdb/samdb/samdb.h"
28 #include "libcli/security/security.h"
29 #include "auth/auth_sam.h"
30 #include "dsdb/common/util.h"
31 #include "libcli/ldap/ldap_ndr.h"
32 #include "param/param.h"
33 #include "librpc/gen_ndr/ndr_winbind_c.h"
34 #include "lib/dbwrap/dbwrap.h"
35 #include "cluster/cluster.h"
37 #undef DBGC_CLASS
38 #define DBGC_CLASS DBGC_AUTH
40 #define KRBTGT_ATTRS \
41 /* required for the krb5 kdc */ \
42 "objectClass", \
43 "sAMAccountName", \
44 "userPrincipalName", \
45 "servicePrincipalName", \
46 "msDS-KeyVersionNumber", \
47 "msDS-SecondaryKrbTgtNumber", \
48 "msDS-SupportedEncryptionTypes", \
49 "supplementalCredentials", \
50 "msDS-AllowedToDelegateTo", \
51 "msDS-AllowedToActOnBehalfOfOtherIdentity", \
53 /* passwords */ \
54 "unicodePwd", \
56 "userAccountControl", \
57 "msDS-User-Account-Control-Computed", \
58 "objectSid", \
60 "pwdLastSet", \
61 "msDS-UserPasswordExpiryTimeComputed", \
62 "accountExpires", \
64 /* Needed for RODC rule processing */ \
65 "msDS-KrbTgtLinkBL", \
67 /* Required for Group Managed Service Accounts. */ \
68 "msDS-ManagedPasswordId", \
69 "msDS-ManagedPasswordInterval", \
70 "whenCreated"
72 #define AUTHN_POLICY_ATTRS \
73 /* Required for authentication policies / silos */ \
74 "msDS-AssignedAuthNPolicy", \
75 "msDS-AssignedAuthNPolicySilo"
77 const char *krbtgt_attrs[] = {
79 * Authentication policies will not be enforced on the TGS
80 * account. Don’t include the relevant attributes in the account search.
82 KRBTGT_ATTRS, NULL
85 const char *server_attrs[] = {
86 KRBTGT_ATTRS,
87 AUTHN_POLICY_ATTRS,
88 NULL
91 const char *user_attrs[] = {
93 * This ordering (having msDS-ResultantPSO first) is
94 * important. By processing this attribute first it is
95 * available in the operational module for the other PSO
96 * attribute calculations to use.
98 "msDS-ResultantPSO",
100 KRBTGT_ATTRS,
101 AUTHN_POLICY_ATTRS,
103 "logonHours",
106 * To allow us to zero the badPwdCount and lockoutTime on
107 * successful logon, without database churn
109 "lockoutTime",
112 * Needed for SendToSAM requests
114 "objectGUID",
116 /* check 'allowed workstations' */
117 "userWorkstations",
119 /* required for user_info_dc, not access control: */
120 "displayName",
121 "scriptPath",
122 "profilePath",
123 "homeDirectory",
124 "homeDrive",
125 "lastLogon",
126 "lastLogonTimestamp",
127 "lastLogoff",
128 "accountExpires",
129 "badPwdCount",
130 "logonCount",
131 "primaryGroupID",
132 "memberOf",
133 "badPasswordTime",
134 "lmPwdHistory",
135 "ntPwdHistory",
136 NULL,
139 /****************************************************************************
140 Check if a user is allowed to logon at this time. Note this is the
141 servers local time, as logon hours are just specified as a weekly
142 bitmask.
143 ****************************************************************************/
145 static bool logon_hours_ok(struct ldb_message *msg, const char *name_for_logs)
147 /* In logon hours first bit is Sunday from 12AM to 1AM */
148 const struct ldb_val *hours;
149 struct tm *utctime;
150 time_t lasttime;
151 const char *asct;
152 uint8_t bitmask, bitpos;
154 hours = ldb_msg_find_ldb_val(msg, "logonHours");
155 if (!hours) {
156 DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n", name_for_logs));
157 return true;
160 if (hours->length != 168/8) {
161 DEBUG(5,("logon_hours_ok: malformed logon hours restrictions for user %s\n", name_for_logs));
162 return true;
165 lasttime = time(NULL);
166 utctime = gmtime(&lasttime);
167 if (!utctime) {
168 DEBUG(1, ("logon_hours_ok: failed to get gmtime. Failing logon for user %s\n",
169 name_for_logs));
170 return false;
173 /* find the corresponding byte and bit */
174 bitpos = (utctime->tm_wday * 24 + utctime->tm_hour) % 168;
175 bitmask = 1 << (bitpos % 8);
177 if (! (hours->data[bitpos/8] & bitmask)) {
178 struct tm *t = localtime(&lasttime);
179 if (!t) {
180 asct = "INVALID TIME";
181 } else {
182 asct = asctime(t);
183 if (!asct) {
184 asct = "INVALID TIME";
188 DEBUG(1, ("logon_hours_ok: Account for user %s not allowed to "
189 "logon at this time (%s).\n",
190 name_for_logs, asct ));
191 return false;
194 asct = asctime(utctime);
195 DEBUG(5,("logon_hours_ok: user %s allowed to logon at this time (%s)\n",
196 name_for_logs, asct ? asct : "UNKNOWN TIME" ));
198 return true;
201 /****************************************************************************
202 Do a specific test for a SAM_ACCOUNT being valid for this connection
203 (ie not disabled, expired and the like).
204 ****************************************************************************/
205 _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
206 struct ldb_context *sam_ctx,
207 NTTIME now,
208 uint32_t logon_parameters,
209 struct ldb_dn *domain_dn,
210 struct ldb_message *msg,
211 const char *logon_workstation,
212 const char *name_for_logs,
213 bool allow_domain_trust,
214 bool password_change)
216 uint32_t acct_flags;
217 const char *workstation_list;
218 NTTIME acct_expiry;
219 NTTIME must_change_time;
221 DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
223 acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
225 acct_expiry = samdb_result_account_expires(msg);
227 /* Check for when we must change this password, taking the
228 * userAccountControl flags into account */
229 must_change_time = samdb_result_nttime(msg,
230 "msDS-UserPasswordExpiryTimeComputed", 0);
232 workstation_list = ldb_msg_find_attr_as_string(msg, "userWorkstations", NULL);
234 /* Quit if the account was disabled. */
235 if (acct_flags & ACB_DISABLED) {
236 DEBUG(2,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
237 return NT_STATUS_ACCOUNT_DISABLED;
240 /* Quit if the account was locked out. */
241 if (acct_flags & ACB_AUTOLOCK) {
242 DEBUG(2,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
243 return NT_STATUS_ACCOUNT_LOCKED_OUT;
246 /* Test account expire time */
247 if (now > acct_expiry) {
248 DEBUG(2,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
249 DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
250 nt_time_string(mem_ctx, acct_expiry)));
251 return NT_STATUS_ACCOUNT_EXPIRED;
254 /* check for immediate expiry "must change at next logon" (but not if this is a password change request) */
255 if ((must_change_time == 0) && !password_change) {
256 DEBUG(2,("sam_account_ok: Account for user '%s' password must change!.\n",
257 name_for_logs));
258 return NT_STATUS_PASSWORD_MUST_CHANGE;
261 /* check for expired password (but not if this is a password change request) */
262 if ((acct_flags & ACB_PW_EXPIRED) && !password_change) {
263 DEBUG(2,("sam_account_ok: Account for user '%s' password expired!.\n",
264 name_for_logs));
265 DEBUG(2,("sam_account_ok: Password expired at '%s' unix time.\n",
266 nt_time_string(mem_ctx, must_change_time)));
267 return NT_STATUS_PASSWORD_EXPIRED;
270 /* Test workstation. Workstation list is comma separated. */
271 if (logon_workstation && workstation_list && *workstation_list) {
272 bool invalid_ws = true;
273 int i;
274 char **workstations = str_list_make(mem_ctx, workstation_list, ",");
276 for (i = 0; workstations && workstations[i]; i++) {
277 DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
278 workstations[i], logon_workstation));
280 if (strequal(workstations[i], logon_workstation)) {
281 invalid_ws = false;
282 break;
286 talloc_free(workstations);
288 if (invalid_ws) {
289 return NT_STATUS_INVALID_WORKSTATION;
293 if (!logon_hours_ok(msg, name_for_logs)) {
294 return NT_STATUS_INVALID_LOGON_HOURS;
297 if (!allow_domain_trust) {
298 if (acct_flags & ACB_DOMTRUST) {
299 DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
300 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
303 if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
304 if (acct_flags & ACB_SVRTRUST) {
305 DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
306 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
309 if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
310 /* TODO: this fails with current solaris client. We
311 need to work with Gordon to work out why */
312 if (acct_flags & ACB_WSTRUST) {
313 DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
314 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
318 return NT_STATUS_OK;
321 static NTSTATUS authsam_domain_group_filter(TALLOC_CTX *mem_ctx,
322 char **_filter)
324 char *filter = NULL;
326 *_filter = NULL;
328 filter = talloc_strdup(mem_ctx, "(&(objectClass=group)");
329 if (filter == NULL) {
330 return NT_STATUS_NO_MEMORY;
334 * Skip all builtin groups, they're added later.
336 talloc_asprintf_addbuf(&filter,
337 "(!(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
338 GROUP_TYPE_BUILTIN_LOCAL_GROUP);
339 if (filter == NULL) {
340 return NT_STATUS_NO_MEMORY;
343 * Only include security groups.
345 talloc_asprintf_addbuf(&filter,
346 "(groupType:"LDB_OID_COMPARATOR_AND":=%u))",
347 GROUP_TYPE_SECURITY_ENABLED);
348 if (filter == NULL) {
349 return NT_STATUS_NO_MEMORY;
352 *_filter = filter;
353 return NT_STATUS_OK;
356 _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
357 struct ldb_context *sam_ctx,
358 const char *netbios_name,
359 const char *domain_name,
360 const char *dns_domain_name,
361 struct ldb_dn *domain_dn,
362 const struct ldb_message *msg,
363 DATA_BLOB user_sess_key,
364 DATA_BLOB lm_sess_key,
365 struct auth_user_info_dc **_user_info_dc)
367 NTSTATUS status;
368 int ret;
369 struct auth_user_info_dc *user_info_dc;
370 struct auth_user_info *info;
371 const char *str = NULL;
372 char *filter = NULL;
373 /* SIDs for the account and his primary group */
374 struct dom_sid *account_sid;
375 struct dom_sid_buf buf;
376 const char *primary_group_dn_str = NULL;
377 DATA_BLOB primary_group_blob;
378 struct ldb_dn *primary_group_dn = NULL;
379 struct ldb_message *primary_group_msg = NULL;
380 unsigned primary_group_type;
381 /* SID structures for the expanded group memberships */
382 struct auth_SidAttr *sids = NULL;
383 uint32_t num_sids = 0;
384 unsigned int i;
385 struct dom_sid *domain_sid;
386 TALLOC_CTX *tmp_ctx;
387 struct ldb_message_element *el;
388 static const char * const group_type_attrs[] = { "groupType", NULL };
390 if (msg == NULL) {
391 return NT_STATUS_INVALID_PARAMETER;
394 user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
395 NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
397 tmp_ctx = talloc_new(user_info_dc);
398 if (tmp_ctx == NULL) {
399 TALLOC_FREE(user_info_dc);
400 return NT_STATUS_NO_MEMORY;
404 * We'll typically store three SIDs: the SID of the user, the SID of the
405 * primary group, and a copy of the latter if it's not a resource
406 * group. Allocate enough memory for these three SIDs.
408 sids = talloc_zero_array(user_info_dc, struct auth_SidAttr, 3);
409 if (sids == NULL) {
410 TALLOC_FREE(user_info_dc);
411 return NT_STATUS_NO_MEMORY;
414 num_sids = 2;
416 account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
417 if (account_sid == NULL) {
418 TALLOC_FREE(user_info_dc);
419 return NT_STATUS_NO_MEMORY;
422 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
423 if (!NT_STATUS_IS_OK(status)) {
424 talloc_free(user_info_dc);
425 return status;
428 sids[PRIMARY_USER_SID_INDEX].sid = *account_sid;
429 sids[PRIMARY_USER_SID_INDEX].attrs = SE_GROUP_DEFAULT_FLAGS;
430 sids[PRIMARY_GROUP_SID_INDEX].sid = *domain_sid;
431 sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX].sid, ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
432 sids[PRIMARY_GROUP_SID_INDEX].attrs = SE_GROUP_DEFAULT_FLAGS;
435 * Filter out builtin groups from this token. We will search
436 * for builtin groups later, and not include them in the PAC
437 * or SamLogon validation info.
439 status = authsam_domain_group_filter(tmp_ctx, &filter);
440 if (!NT_STATUS_IS_OK(status)) {
441 TALLOC_FREE(user_info_dc);
442 return status;
445 primary_group_dn_str = talloc_asprintf(
446 tmp_ctx,
447 "<SID=%s>",
448 dom_sid_str_buf(&sids[PRIMARY_GROUP_SID_INDEX].sid, &buf));
449 if (primary_group_dn_str == NULL) {
450 TALLOC_FREE(user_info_dc);
451 return NT_STATUS_NO_MEMORY;
454 /* Get the DN of the primary group. */
455 primary_group_dn = ldb_dn_new(tmp_ctx, sam_ctx, primary_group_dn_str);
456 if (primary_group_dn == NULL) {
457 TALLOC_FREE(user_info_dc);
458 return NT_STATUS_NO_MEMORY;
462 * Do a search for the primary group, for the purpose of checking
463 * whether it's a resource group.
465 ret = dsdb_search_one(sam_ctx, tmp_ctx,
466 &primary_group_msg,
467 primary_group_dn,
468 LDB_SCOPE_BASE,
469 group_type_attrs,
471 NULL);
472 if (ret != LDB_SUCCESS) {
473 talloc_free(user_info_dc);
474 return NT_STATUS_INTERNAL_DB_CORRUPTION;
477 /* Check the type of the primary group. */
478 primary_group_type = ldb_msg_find_attr_as_uint(primary_group_msg, "groupType", 0);
479 if (primary_group_type & GROUP_TYPE_RESOURCE_GROUP) {
481 * If it's a resource group, we might as well indicate that in
482 * its attributes. At any rate, the primary group's attributes
483 * are unlikely to be used in the code, as there's nowhere to
484 * store them.
486 sids[PRIMARY_GROUP_SID_INDEX].attrs |= SE_GROUP_RESOURCE;
487 } else {
489 * The primary group is not a resource group. Make a copy of its
490 * SID to ensure it is added to the Base SIDs in the PAC.
492 sids[REMAINING_SIDS_INDEX] = sids[PRIMARY_GROUP_SID_INDEX];
493 ++num_sids;
496 primary_group_blob = data_blob_string_const(primary_group_dn_str);
498 /* Expands the primary group - this function takes in
499 * memberOf-like values, so we fake one up with the
500 * <SID=S-...> format of DN and then let it expand
501 * them, as long as they meet the filter - so only
502 * domain groups, not builtin groups
504 * The primary group is still treated specially, so we set the
505 * 'only childs' flag to true
507 status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
508 user_info_dc, &sids, &num_sids);
509 if (!NT_STATUS_IS_OK(status)) {
510 talloc_free(user_info_dc);
511 return status;
514 /* Expands the additional groups */
515 el = ldb_msg_find_element(msg, "memberOf");
516 for (i = 0; el && i < el->num_values; i++) {
517 /* This function takes in memberOf values and expands
518 * them, as long as they meet the filter - so only
519 * domain groups, not builtin groups */
520 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
521 user_info_dc, &sids, &num_sids);
522 if (!NT_STATUS_IS_OK(status)) {
523 talloc_free(user_info_dc);
524 return status;
528 user_info_dc->sids = sids;
529 user_info_dc->num_sids = num_sids;
531 user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
532 if (user_info_dc->info == NULL) {
533 talloc_free(user_info_dc);
534 return NT_STATUS_NO_MEMORY;
537 str = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
538 info->account_name = talloc_strdup(info, str);
539 if (info->account_name == NULL) {
540 TALLOC_FREE(user_info_dc);
541 return NT_STATUS_NO_MEMORY;
544 str = ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL);
545 if (str == NULL && dns_domain_name != NULL) {
546 info->user_principal_name = talloc_asprintf(info, "%s@%s",
547 info->account_name,
548 dns_domain_name);
549 if (info->user_principal_name == NULL) {
550 TALLOC_FREE(user_info_dc);
551 return NT_STATUS_NO_MEMORY;
553 info->user_principal_constructed = true;
554 } else if (str != NULL) {
555 info->user_principal_name = talloc_strdup(info, str);
556 if (info->user_principal_name == NULL) {
557 TALLOC_FREE(user_info_dc);
558 return NT_STATUS_NO_MEMORY;
562 info->domain_name = talloc_strdup(info, domain_name);
563 if (info->domain_name == NULL) {
564 TALLOC_FREE(user_info_dc);
565 return NT_STATUS_NO_MEMORY;
568 if (dns_domain_name != NULL) {
569 info->dns_domain_name = talloc_strdup(info, dns_domain_name);
570 if (info->dns_domain_name == NULL) {
571 TALLOC_FREE(user_info_dc);
572 return NT_STATUS_NO_MEMORY;
576 str = ldb_msg_find_attr_as_string(msg, "displayName", "");
577 info->full_name = talloc_strdup(info, str);
578 if (info->full_name == NULL) {
579 TALLOC_FREE(user_info_dc);
580 return NT_STATUS_NO_MEMORY;
583 str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
584 info->logon_script = talloc_strdup(info, str);
585 if (info->logon_script == NULL) {
586 TALLOC_FREE(user_info_dc);
587 return NT_STATUS_NO_MEMORY;
590 str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
591 info->profile_path = talloc_strdup(info, str);
592 if (info->profile_path == NULL) {
593 TALLOC_FREE(user_info_dc);
594 return NT_STATUS_NO_MEMORY;
597 str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
598 info->home_directory = talloc_strdup(info, str);
599 if (info->home_directory == NULL) {
600 TALLOC_FREE(user_info_dc);
601 return NT_STATUS_NO_MEMORY;
604 str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
605 info->home_drive = talloc_strdup(info, str);
606 if (info->home_drive == NULL) {
607 TALLOC_FREE(user_info_dc);
608 return NT_STATUS_NO_MEMORY;
611 info->logon_server = talloc_strdup(info, netbios_name);
612 if (info->logon_server == NULL) {
613 TALLOC_FREE(user_info_dc);
614 return NT_STATUS_NO_MEMORY;
617 info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
618 info->last_logoff = samdb_result_last_logoff(msg);
619 info->acct_expiry = samdb_result_account_expires(msg);
620 info->last_password_change = samdb_result_nttime(msg,
621 "pwdLastSet", 0);
622 info->allow_password_change
623 = samdb_result_allow_password_change(sam_ctx, mem_ctx,
624 domain_dn, msg, "pwdLastSet");
625 info->force_password_change = samdb_result_nttime(msg,
626 "msDS-UserPasswordExpiryTimeComputed", 0);
627 info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
628 info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
631 info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
633 user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
634 user_sess_key.data,
635 user_sess_key.length);
636 if (user_sess_key.data) {
637 if (user_info_dc->user_session_key.data == NULL) {
638 TALLOC_FREE(user_info_dc);
639 return NT_STATUS_NO_MEMORY;
642 user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
643 lm_sess_key.data,
644 lm_sess_key.length);
645 if (lm_sess_key.data) {
646 if (user_info_dc->lm_session_key.data == NULL) {
647 TALLOC_FREE(user_info_dc);
648 return NT_STATUS_NO_MEMORY;
652 if (info->acct_flags & ACB_SVRTRUST) {
653 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
654 PAC */
655 user_info_dc->sids = talloc_realloc(user_info_dc,
656 user_info_dc->sids,
657 struct auth_SidAttr,
658 user_info_dc->num_sids+1);
659 if (user_info_dc->sids == NULL) {
660 TALLOC_FREE(user_info_dc);
661 return NT_STATUS_NO_MEMORY;
663 user_info_dc->sids[user_info_dc->num_sids].sid = global_sid_Enterprise_DCs;
664 user_info_dc->sids[user_info_dc->num_sids].attrs = SE_GROUP_DEFAULT_FLAGS;
665 user_info_dc->num_sids++;
668 if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
669 (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
670 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
671 user_info_dc->sids = talloc_realloc(user_info_dc,
672 user_info_dc->sids,
673 struct auth_SidAttr,
674 user_info_dc->num_sids+1);
675 if (user_info_dc->sids == NULL) {
676 TALLOC_FREE(user_info_dc);
677 return NT_STATUS_NO_MEMORY;
679 user_info_dc->sids[user_info_dc->num_sids].sid = *domain_sid;
680 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids].sid,
681 DOMAIN_RID_ENTERPRISE_READONLY_DCS);
682 user_info_dc->sids[user_info_dc->num_sids].attrs = SE_GROUP_DEFAULT_FLAGS;
683 user_info_dc->num_sids++;
686 info->user_flags = 0;
688 talloc_free(tmp_ctx);
689 *_user_info_dc = user_info_dc;
691 return NT_STATUS_OK;
694 _PUBLIC_ NTSTATUS authsam_update_user_info_dc(TALLOC_CTX *mem_ctx,
695 struct ldb_context *sam_ctx,
696 struct auth_user_info_dc *user_info_dc)
698 char *filter = NULL;
699 NTSTATUS status;
700 uint32_t i;
701 uint32_t n = 0;
704 * This function exists to expand group memberships
705 * in the local domain (forest), as the token
706 * may come from a different domain.
710 * Filter out builtin groups from this token. We will search
711 * for builtin groups later.
713 status = authsam_domain_group_filter(mem_ctx, &filter);
714 if (!NT_STATUS_IS_OK(status)) {
715 return status;
719 * We loop only over the existing number of
720 * sids.
722 n = user_info_dc->num_sids;
723 for (i = 0; i < n; i++) {
724 struct dom_sid *sid = &user_info_dc->sids[i].sid;
725 struct dom_sid_buf sid_buf;
726 char dn_str[sizeof(sid_buf.buf)*2];
727 DATA_BLOB dn_blob = data_blob_null;
729 snprintf(dn_str,
730 sizeof(dn_str),
731 "<SID=%s>",
732 dom_sid_str_buf(sid, &sid_buf));
733 dn_blob = data_blob_string_const(dn_str);
736 * We already have the SID in the token, so set
737 * 'only childs' flag to true and add all
738 * groups which match the filter.
740 status = dsdb_expand_nested_groups(sam_ctx, &dn_blob,
741 true, filter,
742 user_info_dc,
743 &user_info_dc->sids,
744 &user_info_dc->num_sids);
745 if (!NT_STATUS_IS_OK(status)) {
746 talloc_free(filter);
747 return status;
751 talloc_free(filter);
752 return NT_STATUS_OK;
756 * Make a shallow copy of a talloc-allocated user_info_dc structure, holding a
757 * reference to each of the original fields.
759 NTSTATUS authsam_shallow_copy_user_info_dc(TALLOC_CTX *mem_ctx,
760 const struct auth_user_info_dc *user_info_dc_in,
761 struct auth_user_info_dc **user_info_dc_out)
763 struct auth_user_info_dc *user_info_dc = NULL;
764 NTSTATUS status = NT_STATUS_OK;
766 if (user_info_dc_in == NULL) {
767 return NT_STATUS_INVALID_PARAMETER;
770 if (user_info_dc_out == NULL) {
771 return NT_STATUS_INVALID_PARAMETER;
774 user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
775 if (user_info_dc == NULL) {
776 status = NT_STATUS_NO_MEMORY;
777 goto out;
780 *user_info_dc = *user_info_dc_in;
782 if (user_info_dc->info != NULL) {
783 if (talloc_reference(user_info_dc, user_info_dc->info) == NULL) {
784 status = NT_STATUS_NO_MEMORY;
785 goto out;
789 if (user_info_dc->user_session_key.data != NULL) {
790 if (talloc_reference(user_info_dc, user_info_dc->user_session_key.data) == NULL) {
791 status = NT_STATUS_NO_MEMORY;
792 goto out;
796 if (user_info_dc->lm_session_key.data != NULL) {
797 if (talloc_reference(user_info_dc, user_info_dc->lm_session_key.data) == NULL) {
798 status = NT_STATUS_NO_MEMORY;
799 goto out;
803 if (user_info_dc->sids != NULL) {
805 * Because we want to modify the SIDs in the user_info_dc
806 * structure, adding various well-known SIDs such as Asserted
807 * Identity or Claims Valid, make a copy of the SID array to
808 * guard against modification of the original.
810 * It’s better not to make a reference, because anything that
811 * tries to call talloc_realloc() on the original or the copy
812 * will fail when called for any referenced talloc context.
814 user_info_dc->sids = talloc_memdup(user_info_dc,
815 user_info_dc->sids,
816 talloc_get_size(user_info_dc->sids));
817 if (user_info_dc->sids == NULL) {
818 status = NT_STATUS_NO_MEMORY;
819 goto out;
823 *user_info_dc_out = user_info_dc;
824 user_info_dc = NULL;
826 out:
827 talloc_free(user_info_dc);
828 return status;
831 NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
832 TALLOC_CTX *mem_ctx, const char *principal,
833 const char **attrs,
834 const uint32_t dsdb_flags,
835 struct ldb_dn **domain_dn,
836 struct ldb_message **msg)
838 struct ldb_dn *user_dn;
839 NTSTATUS nt_status;
840 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
841 int ret;
843 if (!tmp_ctx) {
844 return NT_STATUS_NO_MEMORY;
847 nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
848 &user_dn, domain_dn);
849 if (!NT_STATUS_IS_OK(nt_status)) {
850 talloc_free(tmp_ctx);
851 return nt_status;
854 /* pull the user attributes */
855 ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
856 LDB_SCOPE_BASE, attrs,
857 dsdb_flags | DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
858 "(objectClass=*)");
859 if (ret != LDB_SUCCESS) {
860 talloc_free(tmp_ctx);
861 return NT_STATUS_INTERNAL_DB_CORRUPTION;
863 talloc_steal(mem_ctx, *msg);
864 talloc_steal(mem_ctx, *domain_dn);
865 talloc_free(tmp_ctx);
867 return NT_STATUS_OK;
870 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
872 Supply either a principal or a DN
874 NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
875 struct loadparm_context *lp_ctx,
876 struct ldb_context *sam_ctx,
877 const char *principal,
878 struct ldb_dn *user_dn,
879 struct auth_user_info_dc **user_info_dc)
881 NTSTATUS nt_status;
882 DATA_BLOB user_sess_key = data_blob(NULL, 0);
883 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
885 struct ldb_message *msg;
886 struct ldb_dn *domain_dn;
888 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
889 if (!tmp_ctx) {
890 return NT_STATUS_NO_MEMORY;
893 if (principal) {
894 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
895 user_attrs, DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS, &domain_dn, &msg);
896 if (!NT_STATUS_IS_OK(nt_status)) {
897 talloc_free(tmp_ctx);
898 return nt_status;
900 } else if (user_dn) {
901 struct dom_sid *user_sid, *domain_sid;
902 int ret;
903 /* pull the user attributes */
904 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
905 LDB_SCOPE_BASE, user_attrs,
906 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG | DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS,
907 "(objectClass=*)");
908 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
909 talloc_free(tmp_ctx);
910 return NT_STATUS_NO_SUCH_USER;
911 } else if (ret != LDB_SUCCESS) {
912 talloc_free(tmp_ctx);
913 return NT_STATUS_INTERNAL_DB_CORRUPTION;
916 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
918 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
919 if (!NT_STATUS_IS_OK(nt_status)) {
920 talloc_free(tmp_ctx);
921 return nt_status;
924 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
925 "(&(objectSid=%s)(objectClass=domain))",
926 ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
927 if (!domain_dn) {
928 struct dom_sid_buf buf;
929 DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
930 dom_sid_str_buf(domain_sid, &buf)));
931 talloc_free(tmp_ctx);
932 return NT_STATUS_NO_SUCH_USER;
935 } else {
936 talloc_free(tmp_ctx);
937 return NT_STATUS_INVALID_PARAMETER;
940 nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
941 lpcfg_netbios_name(lp_ctx),
942 lpcfg_sam_name(lp_ctx),
943 lpcfg_sam_dnsname(lp_ctx),
944 domain_dn,
945 msg,
946 user_sess_key, lm_sess_key,
947 user_info_dc);
948 if (!NT_STATUS_IS_OK(nt_status)) {
949 talloc_free(tmp_ctx);
950 return nt_status;
953 talloc_steal(mem_ctx, *user_info_dc);
954 talloc_free(tmp_ctx);
956 return NT_STATUS_OK;
960 * Returns the details for the Password Settings Object (PSO), if one applies
961 * the user.
963 static int authsam_get_user_pso(struct ldb_context *sam_ctx,
964 TALLOC_CTX *mem_ctx,
965 struct ldb_message *user_msg,
966 struct ldb_message **pso_msg)
968 const char *attrs[] = { "msDS-LockoutThreshold",
969 "msDS-LockoutObservationWindow",
970 NULL };
971 struct ldb_dn *pso_dn = NULL;
972 struct ldb_result *res = NULL;
973 int ret;
975 /* check if the user has a PSO that applies to it */
976 pso_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, user_msg,
977 "msDS-ResultantPSO");
979 if (pso_dn != NULL) {
980 ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, pso_dn, attrs, 0);
981 if (ret != LDB_SUCCESS) {
982 return ret;
985 *pso_msg = res->msgs[0];
988 return LDB_SUCCESS;
992 * Re-read the bad password and successful logon data for a user.
994 * The DN in the passed user record should contain the "objectGUID" in case the
995 * object DN has changed.
997 NTSTATUS authsam_reread_user_logon_data(
998 struct ldb_context *sam_ctx,
999 TALLOC_CTX *mem_ctx,
1000 const struct ldb_message *user_msg,
1001 struct ldb_message **current)
1003 TALLOC_CTX *tmp_ctx = NULL;
1004 const struct ldb_val *v = NULL;
1005 struct ldb_result *res = NULL;
1006 uint32_t acct_flags = 0;
1007 const char *attr_name = "msDS-User-Account-Control-Computed";
1008 NTSTATUS status = NT_STATUS_OK;
1009 int ret;
1011 tmp_ctx = talloc_new(mem_ctx);
1012 if (tmp_ctx == NULL) {
1013 status = NT_STATUS_NO_MEMORY;
1014 goto out;
1018 * Re-read the account details, using the GUID in case the DN
1019 * is being changed (this is automatic in LDB because the
1020 * original search also used DSDB_SEARCH_SHOW_EXTENDED_DN)
1022 * We re read all the attributes in user_attrs, rather than using a
1023 * subset to ensure that we can reuse existing validation code.
1025 ret = dsdb_search_dn(sam_ctx,
1026 tmp_ctx,
1027 &res,
1028 user_msg->dn,
1029 user_attrs,
1030 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS);
1031 if (ret != LDB_SUCCESS) {
1032 DBG_ERR("Unable to re-read account control data for %s\n",
1033 ldb_dn_get_linearized(user_msg->dn));
1034 status = NT_STATUS_INTERNAL_ERROR;
1035 goto out;
1039 * Ensure the account has not been locked out by another request
1041 v = ldb_msg_find_ldb_val(res->msgs[0], attr_name);
1042 if (v == NULL || v->data == NULL) {
1043 DBG_ERR("No %s attribute for %s\n",
1044 attr_name,
1045 ldb_dn_get_linearized(user_msg->dn));
1046 status = NT_STATUS_INTERNAL_ERROR;
1047 goto out;
1049 acct_flags = samdb_result_acct_flags(res->msgs[0], attr_name);
1050 if (acct_flags & ACB_AUTOLOCK) {
1051 DBG_WARNING(
1052 "Account for user %s was locked out.\n",
1053 ldb_dn_get_linearized(user_msg->dn));
1054 status = NT_STATUS_ACCOUNT_LOCKED_OUT;
1055 goto out;
1057 *current = talloc_steal(mem_ctx, res->msgs[0]);
1058 out:
1059 TALLOC_FREE(tmp_ctx);
1060 return status;
1063 static struct db_context *authsam_get_bad_password_db(
1064 TALLOC_CTX *mem_ctx,
1065 struct ldb_context *sam_ctx)
1067 struct loadparm_context *lp_ctx = NULL;
1068 const char *db_name = "bad_password";
1069 struct db_context *db_ctx = NULL;
1071 lp_ctx = ldb_get_opaque(sam_ctx, "loadparm");
1072 if (lp_ctx == NULL) {
1073 DBG_ERR("Unable to get loadparm_context\n");
1074 return NULL;
1077 db_ctx = cluster_db_tmp_open(mem_ctx, lp_ctx, db_name, TDB_DEFAULT);
1078 if (db_ctx == NULL) {
1079 DBG_ERR("Unable to open bad password attempts database\n");
1080 return NULL;
1082 return db_ctx;
1085 static NTSTATUS get_object_sid_as_tdb_data(
1086 TALLOC_CTX *mem_ctx,
1087 const struct ldb_message *msg,
1088 struct dom_sid_buf *buf,
1089 TDB_DATA *key)
1091 struct dom_sid *objectsid = NULL;
1094 * Convert the objectSID to a human readable form to
1095 * make debugging easier
1097 objectsid = samdb_result_dom_sid(mem_ctx, msg, "objectSID");
1098 if (objectsid == NULL) {
1099 DBG_ERR("Unable to extract objectSID\n");
1100 return NT_STATUS_INTERNAL_ERROR;
1102 dom_sid_str_buf(objectsid, buf);
1103 key->dptr = (unsigned char *)buf->buf;
1104 key->dsize = strlen(buf->buf);
1106 talloc_free(objectsid);
1108 return NT_STATUS_OK;
1112 * Add the users objectSID to the bad password attempt database
1113 * to indicate that last authentication failed due to a bad password
1115 static NTSTATUS authsam_set_bad_password_indicator(
1116 struct ldb_context *sam_ctx,
1117 TALLOC_CTX *mem_ctx,
1118 const struct ldb_message *msg)
1120 NTSTATUS status = NT_STATUS_OK;
1121 struct dom_sid_buf buf;
1122 TDB_DATA key = {0};
1123 TDB_DATA value = {0};
1124 struct db_context *db = NULL;
1126 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1127 if (ctx == NULL) {
1128 return NT_STATUS_NO_MEMORY;
1131 db = authsam_get_bad_password_db(ctx, sam_ctx);
1132 if (db == NULL) {
1133 status = NT_STATUS_INTERNAL_ERROR;
1134 goto exit;
1137 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1138 if (!NT_STATUS_IS_OK(status)) {
1139 goto exit;
1142 status = dbwrap_store(db, key, value, 0);
1143 if (!NT_STATUS_IS_OK(status)) {
1144 DBG_ERR("Unable to store bad password indicator\n");
1146 exit:
1147 talloc_free(ctx);
1148 return status;
1152 * see if the users objectSID is in the bad password attempt database
1154 static NTSTATUS authsam_check_bad_password_indicator(
1155 struct ldb_context *sam_ctx,
1156 TALLOC_CTX *mem_ctx,
1157 bool *exists,
1158 const struct ldb_message *msg)
1160 NTSTATUS status = NT_STATUS_OK;
1161 struct dom_sid_buf buf;
1162 TDB_DATA key = {0};
1163 struct db_context *db = NULL;
1165 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1166 if (ctx == NULL) {
1167 return NT_STATUS_NO_MEMORY;
1170 db = authsam_get_bad_password_db(ctx, sam_ctx);
1171 if (db == NULL) {
1172 status = NT_STATUS_INTERNAL_ERROR;
1173 goto exit;
1176 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1177 if (!NT_STATUS_IS_OK(status)) {
1178 goto exit;
1181 *exists = dbwrap_exists(db, key);
1182 exit:
1183 talloc_free(ctx);
1184 return status;
1188 * Remove the users objectSID to the bad password attempt database
1189 * to indicate that last authentication succeeded.
1191 static NTSTATUS authsam_clear_bad_password_indicator(
1192 struct ldb_context *sam_ctx,
1193 TALLOC_CTX *mem_ctx,
1194 const struct ldb_message *msg)
1196 NTSTATUS status = NT_STATUS_OK;
1197 struct dom_sid_buf buf;
1198 TDB_DATA key = {0};
1199 struct db_context *db = NULL;
1201 TALLOC_CTX *ctx = talloc_new(mem_ctx);
1202 if (ctx == NULL) {
1203 return NT_STATUS_NO_MEMORY;
1206 db = authsam_get_bad_password_db(ctx, sam_ctx);
1207 if (db == NULL) {
1208 status = NT_STATUS_INTERNAL_ERROR;
1209 goto exit;
1212 status = get_object_sid_as_tdb_data(ctx, msg, &buf, &key);
1213 if (!NT_STATUS_IS_OK(status)) {
1214 goto exit;
1217 status = dbwrap_delete(db, key);
1218 if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, status)) {
1220 * Ok there was no bad password indicator this is expected
1222 status = NT_STATUS_OK;
1224 if (NT_STATUS_IS_ERR(status)) {
1225 DBG_ERR("Unable to delete bad password indicator, %s %s\n",
1226 nt_errstr(status),
1227 get_friendly_nt_error_msg(status));
1229 exit:
1230 talloc_free(ctx);
1231 return status;
1234 NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
1235 struct ldb_message *msg,
1236 struct ldb_dn *domain_dn)
1238 const char *attrs[] = { "lockoutThreshold",
1239 "lockOutObservationWindow",
1240 "lockoutDuration",
1241 "pwdProperties",
1242 NULL };
1243 int ret;
1244 NTSTATUS status;
1245 struct ldb_result *domain_res;
1246 struct ldb_message *msg_mod = NULL;
1247 struct ldb_message *current = NULL;
1248 struct ldb_message *pso_msg = NULL;
1249 bool txn_active = false;
1250 TALLOC_CTX *mem_ctx;
1252 mem_ctx = talloc_new(msg);
1253 if (mem_ctx == NULL) {
1254 return NT_STATUS_NO_MEMORY;
1257 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
1258 if (ret != LDB_SUCCESS) {
1259 TALLOC_FREE(mem_ctx);
1260 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1263 ret = authsam_get_user_pso(sam_ctx, mem_ctx, msg, &pso_msg);
1264 if (ret != LDB_SUCCESS) {
1267 * fallback to using the domain defaults so that we still
1268 * record the bad password attempt
1270 DBG_ERR("Error (%d) checking PSO for %s\n",
1271 ret, ldb_dn_get_linearized(msg->dn));
1275 * To ensure that the bad password count is updated atomically,
1276 * we need to:
1277 * begin a transaction
1278 * re-read the account details,
1279 * using the <GUID= part of the DN
1280 * update the bad password count
1281 * commit the transaction.
1285 * Start a new transaction
1287 ret = ldb_transaction_start(sam_ctx);
1288 if (ret != LDB_SUCCESS) {
1289 status = NT_STATUS_INTERNAL_ERROR;
1290 goto error;
1292 txn_active = true;
1295 * Re-read the account details, using the GUID in case the DN
1296 * is being changed.
1298 status = authsam_reread_user_logon_data(
1299 sam_ctx, mem_ctx, msg, &current);
1300 if (!NT_STATUS_IS_OK(status)) {
1301 /* The re-read can return account locked out, as well
1302 * as an internal error
1304 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
1306 * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
1307 * the transaction. Again to avoid cluttering the
1308 * audit logs with spurious errors
1310 goto exit;
1312 goto error;
1316 * Update the bad password count and if required lock the account
1318 status = dsdb_update_bad_pwd_count(
1319 mem_ctx,
1320 sam_ctx,
1321 current,
1322 domain_res->msgs[0],
1323 pso_msg,
1324 &msg_mod);
1325 if (!NT_STATUS_IS_OK(status)) {
1326 status = NT_STATUS_INTERNAL_ERROR;
1327 goto error;
1331 * Write the data back to disk if required.
1333 if (msg_mod != NULL) {
1334 struct ldb_request *req;
1336 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1337 msg_mod,
1338 NULL,
1339 NULL,
1340 ldb_op_default_callback,
1341 NULL);
1342 if (ret != LDB_SUCCESS) {
1343 TALLOC_FREE(msg_mod);
1344 status = NT_STATUS_INTERNAL_ERROR;
1345 goto error;
1348 ret = ldb_request_add_control(req,
1349 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1350 false, NULL);
1351 if (ret != LDB_SUCCESS) {
1352 talloc_free(req);
1353 status = NT_STATUS_INTERNAL_ERROR;
1354 goto error;
1358 * As we're in a transaction, make the ldb request directly
1359 * to avoid the nested transaction that would result if we
1360 * called dsdb_autotransaction_request
1362 ret = ldb_request(sam_ctx, req);
1363 if (ret == LDB_SUCCESS) {
1364 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1366 talloc_free(req);
1367 if (ret != LDB_SUCCESS) {
1368 status = NT_STATUS_INTERNAL_ERROR;
1369 goto error;
1371 status = authsam_set_bad_password_indicator(
1372 sam_ctx, mem_ctx, msg);
1373 if (!NT_STATUS_IS_OK(status)) {
1374 goto error;
1378 * Note that we may not have updated the user record, but
1379 * committing the transaction in that case is still the correct
1380 * thing to do.
1381 * If the transaction was cancelled, this would be logged by
1382 * the dsdb audit log as a failure. When in fact it is expected
1383 * behaviour.
1385 exit:
1386 TALLOC_FREE(mem_ctx);
1387 ret = ldb_transaction_commit(sam_ctx);
1388 if (ret != LDB_SUCCESS) {
1389 DBG_ERR("Error (%d) %s, committing transaction,"
1390 " while updating bad password count"
1391 " for (%s)\n",
1392 ret,
1393 ldb_errstring(sam_ctx),
1394 ldb_dn_get_linearized(msg->dn));
1395 return NT_STATUS_INTERNAL_ERROR;
1397 return status;
1399 error:
1400 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
1401 "set lockoutTime on %s: %s\n",
1402 ldb_dn_get_linearized(msg->dn),
1403 ldb_errstring(sam_ctx) != NULL ?
1404 ldb_errstring(sam_ctx) :nt_errstr(status));
1405 if (txn_active) {
1406 ret = ldb_transaction_cancel(sam_ctx);
1407 if (ret != LDB_SUCCESS) {
1408 DBG_ERR("Error rolling back transaction,"
1409 " while updating bad password count"
1410 " on %s: %s\n",
1411 ldb_dn_get_linearized(msg->dn),
1412 ldb_errstring(sam_ctx));
1415 TALLOC_FREE(mem_ctx);
1416 return status;
1421 * msDS-LogonTimeSyncInterval is an int32_t number of days.
1423 * The docs say: "the initial update, after the domain functional
1424 * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
1425 * 14 days minus a random percentage of 5 days", but we aren't doing
1426 * that. The blogosphere seems to think that this randomised update
1427 * happens every time, but [MS-ADA1] doesn't agree.
1429 * Dochelp referred us to the following blog post:
1430 * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
1432 * when msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
1433 * not changed.
1436 static NTSTATUS authsam_calculate_lastlogon_sync_interval(
1437 struct ldb_context *sam_ctx,
1438 TALLOC_CTX *ctx,
1439 struct ldb_dn *domain_dn,
1440 NTTIME *sync_interval_nt)
1442 static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
1443 NULL };
1444 int ret;
1445 struct ldb_result *domain_res = NULL;
1446 TALLOC_CTX *mem_ctx = NULL;
1447 uint32_t sync_interval;
1449 mem_ctx = talloc_new(ctx);
1450 if (mem_ctx == NULL) {
1451 return NT_STATUS_NO_MEMORY;
1454 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
1456 if (ret != LDB_SUCCESS || domain_res->count != 1) {
1457 TALLOC_FREE(mem_ctx);
1458 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1461 sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
1462 "msDS-LogonTimeSyncInterval",
1463 14);
1464 DEBUG(5, ("sync interval is %d\n", sync_interval));
1465 if (sync_interval >= 5){
1467 * Subtract "a random percentage of 5" days. Presumably this
1468 * percentage is between 0 and 100, and modulus is accurate
1469 * enough.
1471 uint32_t r = generate_random() % 6;
1472 sync_interval -= r;
1473 DBG_INFO("randomised sync interval is %d (-%d)\n", sync_interval, r);
1475 /* In the case where sync_interval < 5 there is no randomisation */
1478 * msDS-LogonTimeSyncInterval is an int32_t number of days,
1479 * while lastLogonTimestamp (to be updated) is in the 64 bit
1480 * 100ns NTTIME format so we must convert.
1482 *sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
1483 TALLOC_FREE(mem_ctx);
1484 return NT_STATUS_OK;
1489 * We only set lastLogonTimestamp if the current value is older than
1490 * now - msDS-LogonTimeSyncInterval days.
1492 * lastLogonTimestamp is in the 64 bit 100ns NTTIME format
1494 static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
1495 struct ldb_message *msg_mod,
1496 struct ldb_dn *domain_dn,
1497 NTTIME old_timestamp,
1498 NTTIME now,
1499 NTTIME sync_interval_nt)
1501 int ret;
1502 DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
1503 (long long int)old_timestamp,
1504 (long long int)(now - sync_interval_nt),
1505 (long long int)(old_timestamp - now + sync_interval_nt)));
1507 if (sync_interval_nt == 0){
1509 * Setting msDS-LogonTimeSyncInterval to zero is how you ask
1510 * that nothing happens here.
1512 return NT_STATUS_OK;
1514 if (old_timestamp > now){
1515 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
1516 (long long int)old_timestamp, (long long int)now));
1517 /* then what? */
1519 } else if (old_timestamp < now - sync_interval_nt){
1520 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
1521 (long long int)now));
1523 /* The time has come to update lastLogonTimestamp */
1524 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1525 "lastLogonTimestamp", now);
1527 if (ret != LDB_SUCCESS) {
1528 return NT_STATUS_NO_MEMORY;
1531 return NT_STATUS_OK;
1534 /****************************************************************************
1535 Look for the specified user in the sam, return ldb result structures
1536 ****************************************************************************/
1538 NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
1539 const char *account_name,
1540 struct ldb_dn *domain_dn,
1541 struct ldb_message **ret_msg)
1543 int ret;
1544 char *account_name_encoded = NULL;
1546 account_name_encoded = ldb_binary_encode_string(mem_ctx, account_name);
1547 if (account_name_encoded == NULL) {
1548 return NT_STATUS_NO_MEMORY;
1551 /* pull the user attributes */
1552 ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
1553 user_attrs,
1554 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS,
1555 "(&(sAMAccountName=%s)(objectclass=user))",
1556 account_name_encoded);
1557 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1558 DEBUG(3,("authsam_search_account: Couldn't find user [%s] in samdb, under %s\n",
1559 account_name, ldb_dn_get_linearized(domain_dn)));
1560 return NT_STATUS_NO_SUCH_USER;
1562 if (ret != LDB_SUCCESS) {
1563 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1566 return NT_STATUS_OK;
1570 /* Reset the badPwdCount to zero and update the lastLogon time. */
1571 NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
1572 const struct ldb_message *msg,
1573 struct ldb_dn *domain_dn,
1574 bool interactive_or_kerberos,
1575 TALLOC_CTX *send_to_sam_mem_ctx,
1576 struct netr_SendToSamBase **send_to_sam)
1578 int ret;
1579 NTSTATUS status;
1580 int badPwdCount;
1581 int dbBadPwdCount;
1582 int64_t lockoutTime;
1583 struct ldb_message *msg_mod;
1584 TALLOC_CTX *mem_ctx;
1585 struct timeval tv_now;
1586 NTTIME now;
1587 NTTIME lastLogonTimestamp;
1588 int64_t lockOutObservationWindow;
1589 NTTIME sync_interval_nt = 0;
1590 bool am_rodc = false;
1591 bool txn_active = false;
1592 bool need_db_reread = false;
1594 mem_ctx = talloc_new(msg);
1595 if (mem_ctx == NULL) {
1596 return NT_STATUS_NO_MEMORY;
1600 * Any update of the last logon data, needs to be done inside a
1601 * transaction.
1602 * And the user data needs to be re-read, and the account re-checked
1603 * for lockout.
1605 * However we have long-running transactions like replication
1606 * that could otherwise grind the system to a halt so we first
1607 * determine if *this* account has seen a bad password,
1608 * otherwise we only start a transaction if there was a need
1609 * (because a change was to be made).
1612 status = authsam_check_bad_password_indicator(
1613 sam_ctx, mem_ctx, &need_db_reread, msg);
1614 if (!NT_STATUS_IS_OK(status)) {
1615 TALLOC_FREE(mem_ctx);
1616 return status;
1619 if (interactive_or_kerberos == false) {
1621 * Avoid calculating this twice, it reads the PSO. A
1622 * race on this is unimportant.
1624 lockOutObservationWindow
1625 = samdb_result_msds_LockoutObservationWindow(
1626 sam_ctx, mem_ctx, domain_dn, msg);
1629 ret = samdb_rodc(sam_ctx, &am_rodc);
1630 if (ret != LDB_SUCCESS) {
1631 status = NT_STATUS_INTERNAL_ERROR;
1632 goto error;
1635 if (!am_rodc) {
1637 * Avoid reading the main domain DN twice. A race on
1638 * this is unimportant.
1640 status = authsam_calculate_lastlogon_sync_interval(
1641 sam_ctx, mem_ctx, domain_dn, &sync_interval_nt);
1643 if (!NT_STATUS_IS_OK(status)) {
1644 status = NT_STATUS_INTERNAL_ERROR;
1645 goto error;
1649 get_transaction:
1651 if (need_db_reread) {
1652 struct ldb_message *current = NULL;
1655 * Start a new transaction
1657 ret = ldb_transaction_start(sam_ctx);
1658 if (ret != LDB_SUCCESS) {
1659 status = NT_STATUS_INTERNAL_ERROR;
1660 goto error;
1663 txn_active = true;
1666 * Re-read the account details, using the GUID
1667 * embedded in DN so this is safe against a race where
1668 * it is being renamed.
1670 status = authsam_reread_user_logon_data(
1671 sam_ctx, mem_ctx, msg, &current);
1672 if (!NT_STATUS_IS_OK(status)) {
1674 * The re-read can return account locked out, as well
1675 * as an internal error
1677 if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
1679 * For NT_STATUS_ACCOUNT_LOCKED_OUT we want to commit
1680 * the transaction. Again to avoid cluttering the
1681 * audit logs with spurious errors
1683 goto exit;
1685 goto error;
1687 msg = current;
1690 lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
1691 dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
1692 tv_now = timeval_current();
1693 now = timeval_to_nttime(&tv_now);
1695 if (interactive_or_kerberos) {
1696 badPwdCount = dbBadPwdCount;
1697 } else {
1699 * We get lockOutObservationWindow above, before the
1700 * transaction
1702 badPwdCount = dsdb_effective_badPwdCount(
1703 msg, lockOutObservationWindow, now);
1705 lastLogonTimestamp =
1706 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
1708 DEBUG(5, ("lastLogonTimestamp is %lld\n",
1709 (long long int)lastLogonTimestamp));
1711 msg_mod = ldb_msg_new(mem_ctx);
1712 if (msg_mod == NULL) {
1713 status = NT_STATUS_NO_MEMORY;
1714 goto error;
1718 * By using the DN from msg->dn directly, we allow LDB to
1719 * prefer the embedded GUID form, so this is actually quite
1720 * safe even in the case where DN has been changed
1722 msg_mod->dn = msg->dn;
1724 if (lockoutTime != 0) {
1726 * This implies "badPwdCount" = 0, see samldb_lockout_time()
1728 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
1729 if (ret != LDB_SUCCESS) {
1730 status = NT_STATUS_NO_MEMORY;
1731 goto error;
1733 } else if (badPwdCount != 0) {
1734 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
1735 if (ret != LDB_SUCCESS) {
1736 status = NT_STATUS_NO_MEMORY;
1737 goto error;
1741 if (interactive_or_kerberos ||
1742 (badPwdCount != 0 && lockoutTime == 0)) {
1743 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1744 "lastLogon", now);
1745 if (ret != LDB_SUCCESS) {
1746 status = NT_STATUS_NO_MEMORY;
1747 goto error;
1751 if (interactive_or_kerberos) {
1752 int logonCount;
1754 logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
1756 logonCount += 1;
1758 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1759 "logonCount", logonCount);
1760 if (ret != LDB_SUCCESS) {
1761 status = NT_STATUS_NO_MEMORY;
1762 goto error;
1764 } else {
1765 /* Set an unset logonCount to 0 on first successful login */
1766 if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
1767 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1768 "logonCount", 0);
1769 if (ret != LDB_SUCCESS) {
1770 TALLOC_FREE(mem_ctx);
1771 return NT_STATUS_NO_MEMORY;
1776 if (!am_rodc) {
1777 status = authsam_update_lastlogon_timestamp(
1778 sam_ctx,
1779 msg_mod,
1780 domain_dn,
1781 lastLogonTimestamp,
1782 now,
1783 sync_interval_nt);
1784 if (!NT_STATUS_IS_OK(status)) {
1785 status = NT_STATUS_NO_MEMORY;
1786 goto error;
1788 } else {
1789 /* Perform the (async) SendToSAM calls for MS-SAMS */
1790 if (dbBadPwdCount != 0 && send_to_sam != NULL) {
1791 struct netr_SendToSamBase *base_msg;
1792 struct GUID guid = samdb_result_guid(msg, "objectGUID");
1794 base_msg = talloc_zero(send_to_sam_mem_ctx,
1795 struct netr_SendToSamBase);
1796 if (base_msg == NULL) {
1797 status = NT_STATUS_NO_MEMORY;
1798 goto error;
1801 base_msg->message_type = SendToSamResetBadPasswordCount;
1802 base_msg->message_size = 16;
1803 base_msg->message.reset_bad_password.guid = guid;
1804 *send_to_sam = base_msg;
1808 if (msg_mod->num_elements > 0) {
1809 unsigned int i;
1810 struct ldb_request *req;
1813 * If it turns out we are going to update the DB, go
1814 * back to the start, get a transaction and the
1815 * current DB state and try again
1817 if (txn_active == false) {
1818 need_db_reread = true;
1819 goto get_transaction;
1822 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
1823 for (i=0;i<msg_mod->num_elements;i++) {
1824 msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1827 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1828 msg_mod,
1829 NULL,
1830 NULL,
1831 ldb_op_default_callback,
1832 NULL);
1833 if (ret != LDB_SUCCESS) {
1834 status = NT_STATUS_INTERNAL_ERROR;
1835 goto error;
1838 ret = ldb_request_add_control(req,
1839 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1840 false, NULL);
1841 if (ret != LDB_SUCCESS) {
1842 TALLOC_FREE(req);
1843 status = NT_STATUS_INTERNAL_ERROR;
1844 goto error;
1847 * As we're in a transaction, make the ldb request directly
1848 * to avoid the nested transaction that would result if we
1849 * called dsdb_autotransaction_request
1851 ret = ldb_request(sam_ctx, req);
1852 if (ret == LDB_SUCCESS) {
1853 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
1855 TALLOC_FREE(req);
1856 if (ret != LDB_SUCCESS) {
1857 status = NT_STATUS_INTERNAL_ERROR;
1858 goto error;
1861 status = authsam_clear_bad_password_indicator(sam_ctx, mem_ctx, msg);
1862 if (!NT_STATUS_IS_OK(status)) {
1863 goto error;
1867 * Note that we may not have updated the user record, but
1868 * committing the transaction in that case is still the correct
1869 * thing to do.
1870 * If the transaction was cancelled, this would be logged by
1871 * the dsdb audit log as a failure. When in fact it is expected
1872 * behaviour.
1874 * Thankfully both TDB and LMDB seem to optimise for the empty
1875 * transaction case
1877 exit:
1878 TALLOC_FREE(mem_ctx);
1880 if (txn_active == false) {
1881 return status;
1884 ret = ldb_transaction_commit(sam_ctx);
1885 if (ret != LDB_SUCCESS) {
1886 DBG_ERR("Error (%d) %s, committing transaction,"
1887 " while updating successful logon accounting"
1888 " for (%s)\n",
1889 ret,
1890 ldb_errstring(sam_ctx),
1891 ldb_dn_get_linearized(msg->dn));
1892 return NT_STATUS_INTERNAL_ERROR;
1894 return status;
1896 error:
1897 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
1898 "set lockoutTime on %s: %s\n",
1899 ldb_dn_get_linearized(msg->dn),
1900 ldb_errstring(sam_ctx) != NULL ?
1901 ldb_errstring(sam_ctx) :nt_errstr(status));
1902 if (txn_active) {
1903 ret = ldb_transaction_cancel(sam_ctx);
1904 if (ret != LDB_SUCCESS) {
1905 DBG_ERR("Error rolling back transaction,"
1906 " while updating bad password count"
1907 " on %s: %s\n",
1908 ldb_dn_get_linearized(msg->dn),
1909 ldb_errstring(sam_ctx));
1912 TALLOC_FREE(mem_ctx);
1913 return status;