CVE-2020-25718 kdc: Confirm the RODC was allowed to issue a particular ticket
[Samba.git] / source4 / auth / sam.c
blob93b41be3b21093ef59e46b5918abc175105abb4b
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"
35 #undef DBGC_CLASS
36 #define DBGC_CLASS DBGC_AUTH
38 #define KRBTGT_ATTRS \
39 /* required for the krb5 kdc */ \
40 "objectClass", \
41 "sAMAccountName", \
42 "userPrincipalName", \
43 "servicePrincipalName", \
44 "msDS-KeyVersionNumber", \
45 "msDS-SecondaryKrbTgtNumber", \
46 "msDS-SupportedEncryptionTypes", \
47 "supplementalCredentials", \
48 "msDS-AllowedToDelegateTo", \
50 /* passwords */ \
51 "dBCSPwd", \
52 "unicodePwd", \
54 "userAccountControl", \
55 "msDS-User-Account-Control-Computed", \
56 "objectSid", \
58 "pwdLastSet", \
59 "msDS-UserPasswordExpiryTimeComputed", \
60 "accountExpires", \
62 /* Needed for RODC rule processing */ \
63 "msDS-KrbTgtLinkBL"
65 const char *krbtgt_attrs[] = {
66 KRBTGT_ATTRS, NULL
69 const char *server_attrs[] = {
70 KRBTGT_ATTRS, NULL
73 const char *user_attrs[] = {
75 * This ordering (having msDS-ResultantPSO first) is
76 * important. By processing this attribute first it is
77 * available in the operational module for the other PSO
78 * attribute calcuations to use.
80 "msDS-ResultantPSO",
82 KRBTGT_ATTRS,
84 "logonHours",
87 * To allow us to zero the badPwdCount and lockoutTime on
88 * successful logon, without database churn
90 "lockoutTime",
93 * Needed for SendToSAM requests
95 "objectGUID",
97 /* check 'allowed workstations' */
98 "userWorkstations",
100 /* required for user_info_dc, not access control: */
101 "displayName",
102 "scriptPath",
103 "profilePath",
104 "homeDirectory",
105 "homeDrive",
106 "lastLogon",
107 "lastLogonTimestamp",
108 "lastLogoff",
109 "accountExpires",
110 "badPwdCount",
111 "logonCount",
112 "primaryGroupID",
113 "memberOf",
114 "badPasswordTime",
115 "lmPwdHistory",
116 "ntPwdHistory",
117 NULL,
120 /****************************************************************************
121 Check if a user is allowed to logon at this time. Note this is the
122 servers local time, as logon hours are just specified as a weekly
123 bitmask.
124 ****************************************************************************/
126 static bool logon_hours_ok(struct ldb_message *msg, const char *name_for_logs)
128 /* In logon hours first bit is Sunday from 12AM to 1AM */
129 const struct ldb_val *hours;
130 struct tm *utctime;
131 time_t lasttime;
132 const char *asct;
133 uint8_t bitmask, bitpos;
135 hours = ldb_msg_find_ldb_val(msg, "logonHours");
136 if (!hours) {
137 DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n", name_for_logs));
138 return true;
141 if (hours->length != 168/8) {
142 DEBUG(5,("logon_hours_ok: malformed logon hours restrictions for user %s\n", name_for_logs));
143 return true;
146 lasttime = time(NULL);
147 utctime = gmtime(&lasttime);
148 if (!utctime) {
149 DEBUG(1, ("logon_hours_ok: failed to get gmtime. Failing logon for user %s\n",
150 name_for_logs));
151 return false;
154 /* find the corresponding byte and bit */
155 bitpos = (utctime->tm_wday * 24 + utctime->tm_hour) % 168;
156 bitmask = 1 << (bitpos % 8);
158 if (! (hours->data[bitpos/8] & bitmask)) {
159 struct tm *t = localtime(&lasttime);
160 if (!t) {
161 asct = "INVALID TIME";
162 } else {
163 asct = asctime(t);
164 if (!asct) {
165 asct = "INVALID TIME";
169 DEBUG(1, ("logon_hours_ok: Account for user %s not allowed to "
170 "logon at this time (%s).\n",
171 name_for_logs, asct ));
172 return false;
175 asct = asctime(utctime);
176 DEBUG(5,("logon_hours_ok: user %s allowed to logon at this time (%s)\n",
177 name_for_logs, asct ? asct : "UNKNOWN TIME" ));
179 return true;
182 /****************************************************************************
183 Do a specific test for a SAM_ACCOUNT being valid for this connection
184 (ie not disabled, expired and the like).
185 ****************************************************************************/
186 _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
187 struct ldb_context *sam_ctx,
188 uint32_t logon_parameters,
189 struct ldb_dn *domain_dn,
190 struct ldb_message *msg,
191 const char *logon_workstation,
192 const char *name_for_logs,
193 bool allow_domain_trust,
194 bool password_change)
196 uint16_t acct_flags;
197 const char *workstation_list;
198 NTTIME acct_expiry;
199 NTTIME must_change_time;
200 struct timeval tv_now = timeval_current();
201 NTTIME now = timeval_to_nttime(&tv_now);
203 DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
205 acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
207 acct_expiry = samdb_result_account_expires(msg);
209 /* Check for when we must change this password, taking the
210 * userAccountControl flags into account */
211 must_change_time = samdb_result_nttime(msg,
212 "msDS-UserPasswordExpiryTimeComputed", 0);
214 workstation_list = ldb_msg_find_attr_as_string(msg, "userWorkstations", NULL);
216 /* Quit if the account was disabled. */
217 if (acct_flags & ACB_DISABLED) {
218 DEBUG(2,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
219 return NT_STATUS_ACCOUNT_DISABLED;
222 /* Quit if the account was locked out. */
223 if (acct_flags & ACB_AUTOLOCK) {
224 DEBUG(2,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
225 return NT_STATUS_ACCOUNT_LOCKED_OUT;
228 /* Test account expire time */
229 if (now > acct_expiry) {
230 DEBUG(2,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
231 DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
232 nt_time_string(mem_ctx, acct_expiry)));
233 return NT_STATUS_ACCOUNT_EXPIRED;
236 /* check for immediate expiry "must change at next logon" (but not if this is a password change request) */
237 if ((must_change_time == 0) && !password_change) {
238 DEBUG(2,("sam_account_ok: Account for user '%s' password must change!.\n",
239 name_for_logs));
240 return NT_STATUS_PASSWORD_MUST_CHANGE;
243 /* check for expired password (but not if this is a password change request) */
244 if ((must_change_time < now) && !password_change) {
245 DEBUG(2,("sam_account_ok: Account for user '%s' password expired!.\n",
246 name_for_logs));
247 DEBUG(2,("sam_account_ok: Password expired at '%s' unix time.\n",
248 nt_time_string(mem_ctx, must_change_time)));
249 return NT_STATUS_PASSWORD_EXPIRED;
252 /* Test workstation. Workstation list is comma separated. */
253 if (logon_workstation && workstation_list && *workstation_list) {
254 bool invalid_ws = true;
255 int i;
256 char **workstations = str_list_make(mem_ctx, workstation_list, ",");
258 for (i = 0; workstations && workstations[i]; i++) {
259 DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
260 workstations[i], logon_workstation));
262 if (strequal(workstations[i], logon_workstation)) {
263 invalid_ws = false;
264 break;
268 talloc_free(workstations);
270 if (invalid_ws) {
271 return NT_STATUS_INVALID_WORKSTATION;
275 if (!logon_hours_ok(msg, name_for_logs)) {
276 return NT_STATUS_INVALID_LOGON_HOURS;
279 if (!allow_domain_trust) {
280 if (acct_flags & ACB_DOMTRUST) {
281 DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
282 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
285 if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
286 if (acct_flags & ACB_SVRTRUST) {
287 DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
288 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
291 if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
292 /* TODO: this fails with current solaris client. We
293 need to work with Gordon to work out why */
294 if (acct_flags & ACB_WSTRUST) {
295 DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
296 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
300 return NT_STATUS_OK;
303 static NTSTATUS authsam_domain_group_filter(TALLOC_CTX *mem_ctx,
304 char **_filter)
306 char *filter = NULL;
308 *_filter = NULL;
310 filter = talloc_strdup(mem_ctx, "(&(objectClass=group)");
311 if (filter == NULL) {
312 return NT_STATUS_NO_MEMORY;
316 * Skip all builtin groups, they're added later.
318 filter = talloc_asprintf_append_buffer(filter,
319 "(!(groupType:1.2.840.113556.1.4.803:=%u))",
320 GROUP_TYPE_BUILTIN_LOCAL_GROUP);
321 if (filter == NULL) {
322 return NT_STATUS_NO_MEMORY;
325 * Only include security groups.
327 filter = talloc_asprintf_append_buffer(filter,
328 "(groupType:1.2.840.113556.1.4.803:=%u))",
329 GROUP_TYPE_SECURITY_ENABLED);
330 if (filter == NULL) {
331 return NT_STATUS_NO_MEMORY;
334 *_filter = filter;
335 return NT_STATUS_OK;
338 _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
339 struct ldb_context *sam_ctx,
340 const char *netbios_name,
341 const char *domain_name,
342 const char *dns_domain_name,
343 struct ldb_dn *domain_dn,
344 struct ldb_message *msg,
345 DATA_BLOB user_sess_key,
346 DATA_BLOB lm_sess_key,
347 struct auth_user_info_dc **_user_info_dc)
349 NTSTATUS status;
350 struct auth_user_info_dc *user_info_dc;
351 struct auth_user_info *info;
352 const char *str = NULL;
353 char *filter = NULL;
354 /* SIDs for the account and his primary group */
355 struct dom_sid *account_sid;
356 struct dom_sid_buf buf;
357 const char *primary_group_dn;
358 DATA_BLOB primary_group_blob;
359 /* SID structures for the expanded group memberships */
360 struct dom_sid *sids = NULL;
361 unsigned int num_sids = 0, i;
362 struct dom_sid *domain_sid;
363 TALLOC_CTX *tmp_ctx;
364 struct ldb_message_element *el;
366 user_info_dc = talloc(mem_ctx, struct auth_user_info_dc);
367 NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
369 tmp_ctx = talloc_new(user_info_dc);
370 if (tmp_ctx == NULL) {
371 TALLOC_FREE(user_info_dc);
372 return NT_STATUS_NO_MEMORY;
375 sids = talloc_array(user_info_dc, struct dom_sid, 2);
376 if (sids == NULL) {
377 TALLOC_FREE(user_info_dc);
378 return NT_STATUS_NO_MEMORY;
381 num_sids = 2;
383 account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
384 if (account_sid == NULL) {
385 TALLOC_FREE(user_info_dc);
386 return NT_STATUS_NO_MEMORY;
389 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
390 if (!NT_STATUS_IS_OK(status)) {
391 talloc_free(user_info_dc);
392 return status;
395 sids[PRIMARY_USER_SID_INDEX] = *account_sid;
396 sids[PRIMARY_GROUP_SID_INDEX] = *domain_sid;
397 sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX], ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
400 * Filter out builtin groups from this token. We will search
401 * for builtin groups later, and not include them in the PAC
402 * or SamLogon validation info.
404 status = authsam_domain_group_filter(tmp_ctx, &filter);
405 if (!NT_STATUS_IS_OK(status)) {
406 TALLOC_FREE(user_info_dc);
407 return status;
410 primary_group_dn = talloc_asprintf(
411 tmp_ctx,
412 "<SID=%s>",
413 dom_sid_str_buf(&sids[PRIMARY_GROUP_SID_INDEX], &buf));
414 if (primary_group_dn == NULL) {
415 TALLOC_FREE(user_info_dc);
416 return NT_STATUS_NO_MEMORY;
419 primary_group_blob = data_blob_string_const(primary_group_dn);
421 /* Expands the primary group - this function takes in
422 * memberOf-like values, so we fake one up with the
423 * <SID=S-...> format of DN and then let it expand
424 * them, as long as they meet the filter - so only
425 * domain groups, not builtin groups
427 * The primary group is still treated specially, so we set the
428 * 'only childs' flag to true
430 status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
431 user_info_dc, &sids, &num_sids);
432 if (!NT_STATUS_IS_OK(status)) {
433 talloc_free(user_info_dc);
434 return status;
437 /* Expands the additional groups */
438 el = ldb_msg_find_element(msg, "memberOf");
439 for (i = 0; el && i < el->num_values; i++) {
440 /* This function takes in memberOf values and expands
441 * them, as long as they meet the filter - so only
442 * domain groups, not builtin groups */
443 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
444 user_info_dc, &sids, &num_sids);
445 if (!NT_STATUS_IS_OK(status)) {
446 talloc_free(user_info_dc);
447 return status;
451 user_info_dc->sids = sids;
452 user_info_dc->num_sids = num_sids;
454 user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
455 NT_STATUS_HAVE_NO_MEMORY(user_info_dc->info);
457 info->account_name = talloc_steal(info,
458 ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL));
460 info->user_principal_name = talloc_steal(info,
461 ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL));
462 if (info->user_principal_name == NULL && dns_domain_name != NULL) {
463 info->user_principal_name = talloc_asprintf(info, "%s@%s",
464 info->account_name,
465 dns_domain_name);
466 if (info->user_principal_name == NULL) {
467 TALLOC_FREE(user_info_dc);
468 return NT_STATUS_NO_MEMORY;
470 info->user_principal_constructed = true;
473 info->domain_name = talloc_strdup(info, domain_name);
474 if (info->domain_name == NULL) {
475 TALLOC_FREE(user_info_dc);
476 return NT_STATUS_NO_MEMORY;
479 if (dns_domain_name != NULL) {
480 info->dns_domain_name = talloc_strdup(info, dns_domain_name);
481 if (info->dns_domain_name == NULL) {
482 TALLOC_FREE(user_info_dc);
483 return NT_STATUS_NO_MEMORY;
487 str = ldb_msg_find_attr_as_string(msg, "displayName", "");
488 info->full_name = talloc_strdup(info, str);
489 if (info->full_name == NULL) {
490 TALLOC_FREE(user_info_dc);
491 return NT_STATUS_NO_MEMORY;
494 str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
495 info->logon_script = talloc_strdup(info, str);
496 if (info->logon_script == NULL) {
497 TALLOC_FREE(user_info_dc);
498 return NT_STATUS_NO_MEMORY;
501 str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
502 info->profile_path = talloc_strdup(info, str);
503 if (info->profile_path == NULL) {
504 TALLOC_FREE(user_info_dc);
505 return NT_STATUS_NO_MEMORY;
508 str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
509 info->home_directory = talloc_strdup(info, str);
510 if (info->home_directory == NULL) {
511 TALLOC_FREE(user_info_dc);
512 return NT_STATUS_NO_MEMORY;
515 str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
516 info->home_drive = talloc_strdup(info, str);
517 if (info->home_drive == NULL) {
518 TALLOC_FREE(user_info_dc);
519 return NT_STATUS_NO_MEMORY;
522 info->logon_server = talloc_strdup(info, netbios_name);
523 if (info->logon_server == NULL) {
524 TALLOC_FREE(user_info_dc);
525 return NT_STATUS_NO_MEMORY;
528 info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
529 info->last_logoff = samdb_result_last_logoff(msg);
530 info->acct_expiry = samdb_result_account_expires(msg);
531 info->last_password_change = samdb_result_nttime(msg,
532 "pwdLastSet", 0);
533 info->allow_password_change
534 = samdb_result_allow_password_change(sam_ctx, mem_ctx,
535 domain_dn, msg, "pwdLastSet");
536 info->force_password_change = samdb_result_nttime(msg,
537 "msDS-UserPasswordExpiryTimeComputed", 0);
538 info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
539 info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
542 info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
544 user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
545 user_sess_key.data,
546 user_sess_key.length);
547 if (user_sess_key.data) {
548 if (user_info_dc->user_session_key.data == NULL) {
549 TALLOC_FREE(user_info_dc);
550 return NT_STATUS_NO_MEMORY;
553 user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
554 lm_sess_key.data,
555 lm_sess_key.length);
556 if (lm_sess_key.data) {
557 if (user_info_dc->lm_session_key.data == NULL) {
558 TALLOC_FREE(user_info_dc);
559 return NT_STATUS_NO_MEMORY;
563 if (info->acct_flags & ACB_SVRTRUST) {
564 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
565 PAC */
566 user_info_dc->sids = talloc_realloc(user_info_dc,
567 user_info_dc->sids,
568 struct dom_sid,
569 user_info_dc->num_sids+1);
570 if (user_info_dc->sids == NULL) {
571 TALLOC_FREE(user_info_dc);
572 return NT_STATUS_NO_MEMORY;
574 user_info_dc->sids[user_info_dc->num_sids] = global_sid_Enterprise_DCs;
575 user_info_dc->num_sids++;
578 if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
579 (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
580 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
581 user_info_dc->sids = talloc_realloc(user_info_dc,
582 user_info_dc->sids,
583 struct dom_sid,
584 user_info_dc->num_sids+1);
585 if (user_info_dc->sids == NULL) {
586 TALLOC_FREE(user_info_dc);
587 return NT_STATUS_NO_MEMORY;
589 user_info_dc->sids[user_info_dc->num_sids] = *domain_sid;
590 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids],
591 DOMAIN_RID_ENTERPRISE_READONLY_DCS);
592 user_info_dc->num_sids++;
595 info->authenticated = true;
597 talloc_free(tmp_ctx);
598 *_user_info_dc = user_info_dc;
600 return NT_STATUS_OK;
603 _PUBLIC_ NTSTATUS authsam_update_user_info_dc(TALLOC_CTX *mem_ctx,
604 struct ldb_context *sam_ctx,
605 struct auth_user_info_dc *user_info_dc)
607 char *filter = NULL;
608 NTSTATUS status;
609 uint32_t i;
610 uint32_t n = 0;
613 * This function exists to expand group memberships
614 * in the local domain (forest), as the token
615 * may come from a different domain.
619 * Filter out builtin groups from this token. We will search
620 * for builtin groups later.
622 status = authsam_domain_group_filter(mem_ctx, &filter);
623 if (!NT_STATUS_IS_OK(status)) {
624 TALLOC_FREE(user_info_dc);
625 return status;
629 * We loop only over the existing number of
630 * sids.
632 n = user_info_dc->num_sids;
633 for (i = 0; i < n; i++) {
634 struct dom_sid *sid = &user_info_dc->sids[i];
635 struct dom_sid_buf sid_buf;
636 char dn_str[sizeof(sid_buf.buf)*2];
637 DATA_BLOB dn_blob = data_blob_null;
639 snprintf(dn_str,
640 sizeof(dn_str),
641 "<SID=%s>",
642 dom_sid_str_buf(sid, &sid_buf));
643 dn_blob = data_blob_string_const(dn_str);
646 * We already have the SID in the token, so set
647 * 'only childs' flag to true and add all
648 * groups which match the filter.
650 status = dsdb_expand_nested_groups(sam_ctx, &dn_blob,
651 true, filter,
652 user_info_dc,
653 &user_info_dc->sids,
654 &user_info_dc->num_sids);
655 if (!NT_STATUS_IS_OK(status)) {
656 return status;
660 return NT_STATUS_OK;
663 NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
664 TALLOC_CTX *mem_ctx, const char *principal,
665 const char **attrs,
666 struct ldb_dn **domain_dn,
667 struct ldb_message **msg)
669 struct ldb_dn *user_dn;
670 NTSTATUS nt_status;
671 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
672 int ret;
674 if (!tmp_ctx) {
675 return NT_STATUS_NO_MEMORY;
678 nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
679 &user_dn, domain_dn);
680 if (!NT_STATUS_IS_OK(nt_status)) {
681 talloc_free(tmp_ctx);
682 return nt_status;
685 /* pull the user attributes */
686 ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
687 LDB_SCOPE_BASE, attrs,
688 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
689 "(objectClass=*)");
690 if (ret != LDB_SUCCESS) {
691 talloc_free(tmp_ctx);
692 return NT_STATUS_INTERNAL_DB_CORRUPTION;
694 talloc_steal(mem_ctx, *msg);
695 talloc_steal(mem_ctx, *domain_dn);
696 talloc_free(tmp_ctx);
698 return NT_STATUS_OK;
701 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
703 Supply either a principal or a DN
705 NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
706 struct loadparm_context *lp_ctx,
707 struct ldb_context *sam_ctx,
708 const char *principal,
709 struct ldb_dn *user_dn,
710 struct auth_user_info_dc **user_info_dc)
712 NTSTATUS nt_status;
713 DATA_BLOB user_sess_key = data_blob(NULL, 0);
714 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
716 struct ldb_message *msg;
717 struct ldb_dn *domain_dn;
719 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
720 if (!tmp_ctx) {
721 return NT_STATUS_NO_MEMORY;
724 if (principal) {
725 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
726 user_attrs, &domain_dn, &msg);
727 if (!NT_STATUS_IS_OK(nt_status)) {
728 talloc_free(tmp_ctx);
729 return nt_status;
731 } else if (user_dn) {
732 struct dom_sid *user_sid, *domain_sid;
733 int ret;
734 /* pull the user attributes */
735 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
736 LDB_SCOPE_BASE, user_attrs,
737 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
738 "(objectClass=*)");
739 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
740 talloc_free(tmp_ctx);
741 return NT_STATUS_NO_SUCH_USER;
742 } else if (ret != LDB_SUCCESS) {
743 talloc_free(tmp_ctx);
744 return NT_STATUS_INTERNAL_DB_CORRUPTION;
747 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
749 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
750 if (!NT_STATUS_IS_OK(nt_status)) {
751 return nt_status;
754 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
755 "(&(objectSid=%s)(objectClass=domain))",
756 ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
757 if (!domain_dn) {
758 struct dom_sid_buf buf;
759 DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
760 dom_sid_str_buf(domain_sid, &buf)));
761 return NT_STATUS_NO_SUCH_USER;
764 } else {
765 return NT_STATUS_INVALID_PARAMETER;
768 nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
769 lpcfg_netbios_name(lp_ctx),
770 lpcfg_sam_name(lp_ctx),
771 lpcfg_sam_dnsname(lp_ctx),
772 domain_dn,
773 msg,
774 user_sess_key, lm_sess_key,
775 user_info_dc);
776 if (!NT_STATUS_IS_OK(nt_status)) {
777 talloc_free(tmp_ctx);
778 return nt_status;
781 talloc_steal(mem_ctx, *user_info_dc);
782 talloc_free(tmp_ctx);
784 return NT_STATUS_OK;
788 * Returns the details for the Password Settings Object (PSO), if one applies
789 * the user.
791 static int authsam_get_user_pso(struct ldb_context *sam_ctx,
792 TALLOC_CTX *mem_ctx,
793 struct ldb_message *user_msg,
794 struct ldb_message **pso_msg)
796 const char *attrs[] = { "msDS-LockoutThreshold",
797 "msDS-LockoutObservationWindow",
798 NULL };
799 struct ldb_dn *pso_dn = NULL;
800 struct ldb_result *res = NULL;
801 int ret;
803 /* check if the user has a PSO that applies to it */
804 pso_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, user_msg,
805 "msDS-ResultantPSO");
807 if (pso_dn != NULL) {
808 ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, pso_dn, attrs, 0);
809 if (ret != LDB_SUCCESS) {
810 return ret;
813 *pso_msg = res->msgs[0];
816 return LDB_SUCCESS;
819 NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
820 struct ldb_message *msg,
821 struct ldb_dn *domain_dn)
823 const char *attrs[] = { "lockoutThreshold",
824 "lockOutObservationWindow",
825 "lockoutDuration",
826 "pwdProperties",
827 NULL };
828 int ret;
829 NTSTATUS status;
830 struct ldb_result *domain_res;
831 struct ldb_message *msg_mod = NULL;
832 struct ldb_message *pso_msg = NULL;
833 TALLOC_CTX *mem_ctx;
835 mem_ctx = talloc_new(msg);
836 if (mem_ctx == NULL) {
837 return NT_STATUS_NO_MEMORY;
840 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
841 if (ret != LDB_SUCCESS) {
842 TALLOC_FREE(mem_ctx);
843 return NT_STATUS_INTERNAL_DB_CORRUPTION;
846 ret = authsam_get_user_pso(sam_ctx, mem_ctx, msg, &pso_msg);
847 if (ret != LDB_SUCCESS) {
850 * fallback to using the domain defaults so that we still
851 * record the bad password attempt
853 DBG_ERR("Error (%d) checking PSO for %s",
854 ret, ldb_dn_get_linearized(msg->dn));
857 status = dsdb_update_bad_pwd_count(mem_ctx, sam_ctx,
858 msg, domain_res->msgs[0], pso_msg,
859 &msg_mod);
860 if (!NT_STATUS_IS_OK(status)) {
861 TALLOC_FREE(mem_ctx);
862 return status;
865 if (msg_mod != NULL) {
866 struct ldb_request *req;
868 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
869 msg_mod,
870 NULL,
871 NULL,
872 ldb_op_default_callback,
873 NULL);
874 if (ret != LDB_SUCCESS) {
875 goto done;
878 ret = ldb_request_add_control(req,
879 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
880 false, NULL);
881 if (ret != LDB_SUCCESS) {
882 talloc_free(req);
883 goto done;
886 ret = dsdb_autotransaction_request(sam_ctx, req);
887 talloc_free(req);
890 done:
891 if (ret != LDB_SUCCESS) {
892 DBG_ERR("Failed to update badPwdCount, badPasswordTime or "
893 "set lockoutTime on %s: %s\n",
894 ldb_dn_get_linearized(msg->dn),
895 ldb_errstring(sam_ctx));
896 TALLOC_FREE(mem_ctx);
897 return NT_STATUS_INTERNAL_ERROR;
900 TALLOC_FREE(mem_ctx);
901 return NT_STATUS_OK;
905 static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
906 struct ldb_message *msg_mod,
907 struct ldb_dn *domain_dn,
908 NTTIME old_timestamp,
909 NTTIME now)
912 * We only set lastLogonTimestamp if the current value is older than
913 * now - msDS-LogonTimeSyncInterval days.
915 * msDS-LogonTimeSyncInterval is an int32_t number of days, while
916 * lastLogonTimestamp is in the 64 bit 100ns NTTIME format.
918 * The docs say: "the initial update, after the domain functional
919 * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
920 * 14 days minus a random percentage of 5 days", but we aren't doing
921 * that. The blogosphere seems to think that this randomised update
922 * happens everytime, but [MS-ADA1] doesn't agree.
924 * Dochelp referred us to the following blog post:
925 * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
927 * en msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
928 * not changed.
930 static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
931 NULL };
932 int ret;
933 struct ldb_result *domain_res = NULL;
934 TALLOC_CTX *mem_ctx = NULL;
935 int32_t sync_interval;
936 NTTIME sync_interval_nt;
938 mem_ctx = talloc_new(msg_mod);
939 if (mem_ctx == NULL) {
940 return NT_STATUS_NO_MEMORY;
943 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
945 if (ret != LDB_SUCCESS || domain_res->count != 1) {
946 TALLOC_FREE(mem_ctx);
947 return NT_STATUS_INTERNAL_DB_CORRUPTION;
950 sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
951 "msDS-LogonTimeSyncInterval",
952 14);
953 DEBUG(5, ("sync interval is %d\n", sync_interval));
954 if (sync_interval == 0){
956 * Setting msDS-LogonTimeSyncInterval to zero is how you ask
957 * that nothing happens here.
959 TALLOC_FREE(mem_ctx);
960 return NT_STATUS_OK;
962 else if (sync_interval >= 5){
964 * Subtract "a random percentage of 5" days. Presumably this
965 * percentage is between 0 and 100, and modulus is accurate
966 * enough.
968 uint32_t r = generate_random() % 6;
969 sync_interval -= r;
970 DEBUG(5, ("randomised sync interval is %d (-%d)\n", sync_interval, r));
972 /* In the case where sync_interval < 5 there is no randomisation */
974 sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
976 DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
977 (long long int)old_timestamp,
978 (long long int)(now - sync_interval_nt),
979 (long long int)(old_timestamp - now + sync_interval_nt)));
981 if (old_timestamp > now){
982 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
983 (long long int)old_timestamp, (long long int)now));
984 /* then what? */
986 } else if (old_timestamp < now - sync_interval_nt){
987 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
988 (long long int)now));
990 /* The time has come to update lastLogonTimestamp */
991 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
992 "lastLogonTimestamp", now);
994 if (ret != LDB_SUCCESS) {
995 TALLOC_FREE(mem_ctx);
996 return NT_STATUS_NO_MEMORY;
999 TALLOC_FREE(mem_ctx);
1000 return NT_STATUS_OK;
1003 /****************************************************************************
1004 Look for the specified user in the sam, return ldb result structures
1005 ****************************************************************************/
1007 NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
1008 const char *account_name,
1009 struct ldb_dn *domain_dn,
1010 struct ldb_message **ret_msg)
1012 int ret;
1014 /* pull the user attributes */
1015 ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
1016 user_attrs,
1017 DSDB_SEARCH_SHOW_EXTENDED_DN,
1018 "(&(sAMAccountName=%s)(objectclass=user))",
1019 ldb_binary_encode_string(mem_ctx, account_name));
1020 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1021 DEBUG(3,("sam_search_user: Couldn't find user [%s] in samdb, under %s\n",
1022 account_name, ldb_dn_get_linearized(domain_dn)));
1023 return NT_STATUS_NO_SUCH_USER;
1025 if (ret != LDB_SUCCESS) {
1026 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1029 return NT_STATUS_OK;
1033 /* Reset the badPwdCount to zero and update the lastLogon time. */
1034 NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
1035 const struct ldb_message *msg,
1036 struct ldb_dn *domain_dn,
1037 bool interactive_or_kerberos,
1038 struct netr_SendToSamBase **send_to_sam)
1040 int ret;
1041 NTSTATUS status;
1042 int badPwdCount;
1043 int dbBadPwdCount;
1044 int64_t lockoutTime;
1045 struct ldb_message *msg_mod;
1046 TALLOC_CTX *mem_ctx;
1047 struct timeval tv_now;
1048 NTTIME now;
1049 NTTIME lastLogonTimestamp;
1050 bool am_rodc = false;
1052 mem_ctx = talloc_new(msg);
1053 if (mem_ctx == NULL) {
1054 return NT_STATUS_NO_MEMORY;
1057 lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
1058 dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
1059 if (interactive_or_kerberos) {
1060 badPwdCount = dbBadPwdCount;
1061 } else {
1062 badPwdCount = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx,
1063 domain_dn, msg);
1065 lastLogonTimestamp =
1066 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
1068 DEBUG(5, ("lastLogonTimestamp is %lld\n",
1069 (long long int)lastLogonTimestamp));
1071 msg_mod = ldb_msg_new(mem_ctx);
1072 if (msg_mod == NULL) {
1073 TALLOC_FREE(mem_ctx);
1074 return NT_STATUS_NO_MEMORY;
1076 msg_mod->dn = msg->dn;
1078 if (lockoutTime != 0) {
1080 * This implies "badPwdCount" = 0, see samldb_lockout_time()
1082 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
1083 if (ret != LDB_SUCCESS) {
1084 TALLOC_FREE(mem_ctx);
1085 return NT_STATUS_NO_MEMORY;
1087 } else if (badPwdCount != 0) {
1088 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
1089 if (ret != LDB_SUCCESS) {
1090 TALLOC_FREE(mem_ctx);
1091 return NT_STATUS_NO_MEMORY;
1095 tv_now = timeval_current();
1096 now = timeval_to_nttime(&tv_now);
1098 if (interactive_or_kerberos ||
1099 (badPwdCount != 0 && lockoutTime == 0)) {
1100 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1101 "lastLogon", now);
1102 if (ret != LDB_SUCCESS) {
1103 TALLOC_FREE(mem_ctx);
1104 return NT_STATUS_NO_MEMORY;
1108 if (interactive_or_kerberos) {
1109 int logonCount;
1111 logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
1113 logonCount += 1;
1115 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1116 "logonCount", logonCount);
1117 if (ret != LDB_SUCCESS) {
1118 TALLOC_FREE(mem_ctx);
1119 return NT_STATUS_NO_MEMORY;
1121 } else {
1122 /* Set an unset logonCount to 0 on first successful login */
1123 if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
1124 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1125 "logonCount", 0);
1126 if (ret != LDB_SUCCESS) {
1127 TALLOC_FREE(mem_ctx);
1128 return NT_STATUS_NO_MEMORY;
1133 ret = samdb_rodc(sam_ctx, &am_rodc);
1134 if (ret != LDB_SUCCESS) {
1135 TALLOC_FREE(mem_ctx);
1136 return NT_STATUS_INTERNAL_ERROR;
1139 if (!am_rodc) {
1140 status = authsam_update_lastlogon_timestamp(sam_ctx, msg_mod, domain_dn,
1141 lastLogonTimestamp, now);
1142 if (!NT_STATUS_IS_OK(status)) {
1143 TALLOC_FREE(mem_ctx);
1144 return NT_STATUS_NO_MEMORY;
1146 } else {
1147 /* Perform the (async) SendToSAM calls for MS-SAMS */
1148 if (dbBadPwdCount != 0 && send_to_sam != NULL) {
1149 struct netr_SendToSamBase *base_msg;
1150 struct GUID guid = samdb_result_guid(msg, "objectGUID");
1151 base_msg = talloc_zero(msg, struct netr_SendToSamBase);
1153 base_msg->message_type = SendToSamResetBadPasswordCount;
1154 base_msg->message_size = 16;
1155 base_msg->message.reset_bad_password.guid = guid;
1156 *send_to_sam = base_msg;
1160 if (msg_mod->num_elements > 0) {
1161 unsigned int i;
1162 struct ldb_request *req;
1164 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
1165 for (i=0;i<msg_mod->num_elements;i++) {
1166 msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1169 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1170 msg_mod,
1171 NULL,
1172 NULL,
1173 ldb_op_default_callback,
1174 NULL);
1175 if (ret != LDB_SUCCESS) {
1176 goto done;
1179 ret = ldb_request_add_control(req,
1180 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1181 false, NULL);
1182 if (ret != LDB_SUCCESS) {
1183 talloc_free(req);
1184 goto done;
1187 ret = dsdb_autotransaction_request(sam_ctx, req);
1188 talloc_free(req);
1191 done:
1192 if (ret != LDB_SUCCESS) {
1193 DEBUG(0, ("Failed to set badPwdCount and lockoutTime "
1194 "to 0 and/or lastlogon to now (%lld) "
1195 "%s: %s\n", (long long int)now,
1196 ldb_dn_get_linearized(msg_mod->dn),
1197 ldb_errstring(sam_ctx)));
1198 TALLOC_FREE(mem_ctx);
1199 return NT_STATUS_INTERNAL_ERROR;
1202 TALLOC_FREE(mem_ctx);
1203 return NT_STATUS_OK;