sam.c: allocate account_sid on tmp_ctx
[Samba.git] / source4 / auth / sam.c
blob6c7fb221699713ba3c98c2a7ce7794a797b2a54e
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 const char *krbtgt_attrs[] = {
63 KRBTGT_ATTRS, NULL
66 const char *server_attrs[] = {
67 KRBTGT_ATTRS, NULL
70 const char *user_attrs[] = {
72 * This ordering (having msDS-ResultantPSO first) is
73 * important. By processing this attribute first it is
74 * available in the operational module for the other PSO
75 * attribute calcuations to use.
77 "msDS-ResultantPSO",
79 KRBTGT_ATTRS,
81 "logonHours",
84 * To allow us to zero the badPwdCount and lockoutTime on
85 * successful logon, without database churn
87 "lockoutTime",
90 * Needed for SendToSAM requests
92 "objectGUID",
94 /* check 'allowed workstations' */
95 "userWorkstations",
97 /* required for user_info_dc, not access control: */
98 "displayName",
99 "scriptPath",
100 "profilePath",
101 "homeDirectory",
102 "homeDrive",
103 "lastLogon",
104 "lastLogonTimestamp",
105 "lastLogoff",
106 "accountExpires",
107 "badPwdCount",
108 "logonCount",
109 "primaryGroupID",
110 "memberOf",
111 "badPasswordTime",
112 "lmPwdHistory",
113 "ntPwdHistory",
114 NULL,
117 /****************************************************************************
118 Check if a user is allowed to logon at this time. Note this is the
119 servers local time, as logon hours are just specified as a weekly
120 bitmask.
121 ****************************************************************************/
123 static bool logon_hours_ok(struct ldb_message *msg, const char *name_for_logs)
125 /* In logon hours first bit is Sunday from 12AM to 1AM */
126 const struct ldb_val *hours;
127 struct tm *utctime;
128 time_t lasttime;
129 const char *asct;
130 uint8_t bitmask, bitpos;
132 hours = ldb_msg_find_ldb_val(msg, "logonHours");
133 if (!hours) {
134 DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n", name_for_logs));
135 return true;
138 if (hours->length != 168/8) {
139 DEBUG(5,("logon_hours_ok: malformed logon hours restrictions for user %s\n", name_for_logs));
140 return true;
143 lasttime = time(NULL);
144 utctime = gmtime(&lasttime);
145 if (!utctime) {
146 DEBUG(1, ("logon_hours_ok: failed to get gmtime. Failing logon for user %s\n",
147 name_for_logs));
148 return false;
151 /* find the corresponding byte and bit */
152 bitpos = (utctime->tm_wday * 24 + utctime->tm_hour) % 168;
153 bitmask = 1 << (bitpos % 8);
155 if (! (hours->data[bitpos/8] & bitmask)) {
156 struct tm *t = localtime(&lasttime);
157 if (!t) {
158 asct = "INVALID TIME";
159 } else {
160 asct = asctime(t);
161 if (!asct) {
162 asct = "INVALID TIME";
166 DEBUG(1, ("logon_hours_ok: Account for user %s not allowed to "
167 "logon at this time (%s).\n",
168 name_for_logs, asct ));
169 return false;
172 asct = asctime(utctime);
173 DEBUG(5,("logon_hours_ok: user %s allowed to logon at this time (%s)\n",
174 name_for_logs, asct ? asct : "UNKNOWN TIME" ));
176 return true;
179 /****************************************************************************
180 Do a specific test for a SAM_ACCOUNT being valid for this connection
181 (ie not disabled, expired and the like).
182 ****************************************************************************/
183 _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
184 struct ldb_context *sam_ctx,
185 uint32_t logon_parameters,
186 struct ldb_dn *domain_dn,
187 struct ldb_message *msg,
188 const char *logon_workstation,
189 const char *name_for_logs,
190 bool allow_domain_trust,
191 bool password_change)
193 uint16_t acct_flags;
194 const char *workstation_list;
195 NTTIME acct_expiry;
196 NTTIME must_change_time;
197 struct timeval tv_now = timeval_current();
198 NTTIME now = timeval_to_nttime(&tv_now);
200 DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
202 acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
204 acct_expiry = samdb_result_account_expires(msg);
206 /* Check for when we must change this password, taking the
207 * userAccountControl flags into account */
208 must_change_time = samdb_result_nttime(msg,
209 "msDS-UserPasswordExpiryTimeComputed", 0);
211 workstation_list = ldb_msg_find_attr_as_string(msg, "userWorkstations", NULL);
213 /* Quit if the account was disabled. */
214 if (acct_flags & ACB_DISABLED) {
215 DEBUG(2,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
216 return NT_STATUS_ACCOUNT_DISABLED;
219 /* Quit if the account was locked out. */
220 if (acct_flags & ACB_AUTOLOCK) {
221 DEBUG(2,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
222 return NT_STATUS_ACCOUNT_LOCKED_OUT;
225 /* Test account expire time */
226 if (now > acct_expiry) {
227 DEBUG(2,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
228 DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
229 nt_time_string(mem_ctx, acct_expiry)));
230 return NT_STATUS_ACCOUNT_EXPIRED;
233 /* check for immediate expiry "must change at next logon" (but not if this is a password change request) */
234 if ((must_change_time == 0) && !password_change) {
235 DEBUG(2,("sam_account_ok: Account for user '%s' password must change!.\n",
236 name_for_logs));
237 return NT_STATUS_PASSWORD_MUST_CHANGE;
240 /* check for expired password (but not if this is a password change request) */
241 if ((must_change_time < now) && !password_change) {
242 DEBUG(2,("sam_account_ok: Account for user '%s' password expired!.\n",
243 name_for_logs));
244 DEBUG(2,("sam_account_ok: Password expired at '%s' unix time.\n",
245 nt_time_string(mem_ctx, must_change_time)));
246 return NT_STATUS_PASSWORD_EXPIRED;
249 /* Test workstation. Workstation list is comma separated. */
250 if (logon_workstation && workstation_list && *workstation_list) {
251 bool invalid_ws = true;
252 int i;
253 char **workstations = str_list_make(mem_ctx, workstation_list, ",");
255 for (i = 0; workstations && workstations[i]; i++) {
256 DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
257 workstations[i], logon_workstation));
259 if (strequal(workstations[i], logon_workstation)) {
260 invalid_ws = false;
261 break;
265 talloc_free(workstations);
267 if (invalid_ws) {
268 return NT_STATUS_INVALID_WORKSTATION;
272 if (!logon_hours_ok(msg, name_for_logs)) {
273 return NT_STATUS_INVALID_LOGON_HOURS;
276 if (!allow_domain_trust) {
277 if (acct_flags & ACB_DOMTRUST) {
278 DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
279 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
282 if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
283 if (acct_flags & ACB_SVRTRUST) {
284 DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
285 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
288 if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
289 /* TODO: this fails with current solaris client. We
290 need to work with Gordon to work out why */
291 if (acct_flags & ACB_WSTRUST) {
292 DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
293 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
297 return NT_STATUS_OK;
300 static NTSTATUS authsam_domain_group_filter(TALLOC_CTX *mem_ctx,
301 char **_filter)
303 char *filter = NULL;
305 *_filter = NULL;
307 filter = talloc_strdup(mem_ctx, "(&(objectClass=group)");
308 if (filter == NULL) {
309 return NT_STATUS_NO_MEMORY;
313 * Skip all builtin groups, they're added later.
315 filter = talloc_asprintf_append_buffer(filter,
316 "(!(groupType:1.2.840.113556.1.4.803:=%u))",
317 GROUP_TYPE_BUILTIN_LOCAL_GROUP);
318 if (filter == NULL) {
319 return NT_STATUS_NO_MEMORY;
322 * Only include security groups.
324 filter = talloc_asprintf_append_buffer(filter,
325 "(groupType:1.2.840.113556.1.4.803:=%u))",
326 GROUP_TYPE_SECURITY_ENABLED);
327 if (filter == NULL) {
328 return NT_STATUS_NO_MEMORY;
331 *_filter = filter;
332 return NT_STATUS_OK;
335 _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
336 struct ldb_context *sam_ctx,
337 const char *netbios_name,
338 const char *domain_name,
339 const char *dns_domain_name,
340 struct ldb_dn *domain_dn,
341 struct ldb_message *msg,
342 DATA_BLOB user_sess_key,
343 DATA_BLOB lm_sess_key,
344 struct auth_user_info_dc **_user_info_dc)
346 NTSTATUS status;
347 struct auth_user_info_dc *user_info_dc;
348 struct auth_user_info *info;
349 const char *str = NULL;
350 char *filter = NULL;
351 /* SIDs for the account and his primary group */
352 struct dom_sid *account_sid;
353 struct dom_sid_buf buf;
354 const char *primary_group_dn;
355 DATA_BLOB primary_group_blob;
356 /* SID structures for the expanded group memberships */
357 struct dom_sid *sids = NULL;
358 unsigned int num_sids = 0, i;
359 struct dom_sid *domain_sid;
360 TALLOC_CTX *tmp_ctx;
361 struct ldb_message_element *el;
363 user_info_dc = talloc(mem_ctx, struct auth_user_info_dc);
364 NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
366 tmp_ctx = talloc_new(user_info_dc);
367 if (tmp_ctx == NULL) {
368 TALLOC_FREE(user_info_dc);
369 return NT_STATUS_NO_MEMORY;
372 sids = talloc_array(user_info_dc, struct dom_sid, 2);
373 if (sids == NULL) {
374 TALLOC_FREE(user_info_dc);
375 return NT_STATUS_NO_MEMORY;
378 num_sids = 2;
380 account_sid = samdb_result_dom_sid(tmp_ctx, msg, "objectSid");
381 if (account_sid == NULL) {
382 TALLOC_FREE(user_info_dc);
383 return NT_STATUS_NO_MEMORY;
386 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
387 if (!NT_STATUS_IS_OK(status)) {
388 talloc_free(user_info_dc);
389 return status;
392 sids[PRIMARY_USER_SID_INDEX] = *account_sid;
393 sids[PRIMARY_GROUP_SID_INDEX] = *domain_sid;
394 sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX], ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
397 * Filter out builtin groups from this token. We will search
398 * for builtin groups later, and not include them in the PAC
399 * or SamLogon validation info.
401 status = authsam_domain_group_filter(tmp_ctx, &filter);
402 if (!NT_STATUS_IS_OK(status)) {
403 TALLOC_FREE(user_info_dc);
404 return status;
407 primary_group_dn = talloc_asprintf(
408 tmp_ctx,
409 "<SID=%s>",
410 dom_sid_str_buf(&sids[PRIMARY_GROUP_SID_INDEX], &buf));
411 if (primary_group_dn == NULL) {
412 TALLOC_FREE(user_info_dc);
413 return NT_STATUS_NO_MEMORY;
416 primary_group_blob = data_blob_string_const(primary_group_dn);
418 /* Expands the primary group - this function takes in
419 * memberOf-like values, so we fake one up with the
420 * <SID=S-...> format of DN and then let it expand
421 * them, as long as they meet the filter - so only
422 * domain groups, not builtin groups
424 * The primary group is still treated specially, so we set the
425 * 'only childs' flag to true
427 status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
428 user_info_dc, &sids, &num_sids);
429 if (!NT_STATUS_IS_OK(status)) {
430 talloc_free(user_info_dc);
431 return status;
434 /* Expands the additional groups */
435 el = ldb_msg_find_element(msg, "memberOf");
436 for (i = 0; el && i < el->num_values; i++) {
437 /* This function takes in memberOf values and expands
438 * them, as long as they meet the filter - so only
439 * domain groups, not builtin groups */
440 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
441 user_info_dc, &sids, &num_sids);
442 if (!NT_STATUS_IS_OK(status)) {
443 talloc_free(user_info_dc);
444 return status;
448 user_info_dc->sids = sids;
449 user_info_dc->num_sids = num_sids;
451 user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
452 NT_STATUS_HAVE_NO_MEMORY(user_info_dc->info);
454 info->account_name = talloc_steal(info,
455 ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL));
457 info->user_principal_name = talloc_steal(info,
458 ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL));
459 if (info->user_principal_name == NULL && dns_domain_name != NULL) {
460 info->user_principal_name = talloc_asprintf(info, "%s@%s",
461 info->account_name,
462 dns_domain_name);
463 if (info->user_principal_name == NULL) {
464 TALLOC_FREE(user_info_dc);
465 return NT_STATUS_NO_MEMORY;
467 info->user_principal_constructed = true;
470 info->domain_name = talloc_strdup(info, domain_name);
471 if (info->domain_name == NULL) {
472 TALLOC_FREE(user_info_dc);
473 return NT_STATUS_NO_MEMORY;
476 if (dns_domain_name != NULL) {
477 info->dns_domain_name = talloc_strdup(info, dns_domain_name);
478 if (info->dns_domain_name == NULL) {
479 TALLOC_FREE(user_info_dc);
480 return NT_STATUS_NO_MEMORY;
484 str = ldb_msg_find_attr_as_string(msg, "displayName", "");
485 info->full_name = talloc_strdup(info, str);
486 if (info->full_name == NULL) {
487 TALLOC_FREE(user_info_dc);
488 return NT_STATUS_NO_MEMORY;
491 str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
492 info->logon_script = talloc_strdup(info, str);
493 if (info->logon_script == NULL) {
494 TALLOC_FREE(user_info_dc);
495 return NT_STATUS_NO_MEMORY;
498 str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
499 info->profile_path = talloc_strdup(info, str);
500 if (info->profile_path == NULL) {
501 TALLOC_FREE(user_info_dc);
502 return NT_STATUS_NO_MEMORY;
505 str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
506 info->home_directory = talloc_strdup(info, str);
507 if (info->home_directory == NULL) {
508 TALLOC_FREE(user_info_dc);
509 return NT_STATUS_NO_MEMORY;
512 str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
513 info->home_drive = talloc_strdup(info, str);
514 if (info->home_drive == NULL) {
515 TALLOC_FREE(user_info_dc);
516 return NT_STATUS_NO_MEMORY;
519 info->logon_server = talloc_strdup(info, netbios_name);
520 if (info->logon_server == NULL) {
521 TALLOC_FREE(user_info_dc);
522 return NT_STATUS_NO_MEMORY;
525 info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
526 info->last_logoff = samdb_result_last_logoff(msg);
527 info->acct_expiry = samdb_result_account_expires(msg);
528 info->last_password_change = samdb_result_nttime(msg,
529 "pwdLastSet", 0);
530 info->allow_password_change
531 = samdb_result_allow_password_change(sam_ctx, mem_ctx,
532 domain_dn, msg, "pwdLastSet");
533 info->force_password_change = samdb_result_nttime(msg,
534 "msDS-UserPasswordExpiryTimeComputed", 0);
535 info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
536 info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
539 info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
541 user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
542 user_sess_key.data,
543 user_sess_key.length);
544 if (user_sess_key.data) {
545 if (user_info_dc->user_session_key.data == NULL) {
546 TALLOC_FREE(user_info_dc);
547 return NT_STATUS_NO_MEMORY;
550 user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
551 lm_sess_key.data,
552 lm_sess_key.length);
553 if (lm_sess_key.data) {
554 if (user_info_dc->lm_session_key.data == NULL) {
555 TALLOC_FREE(user_info_dc);
556 return NT_STATUS_NO_MEMORY;
560 if (info->acct_flags & ACB_SVRTRUST) {
561 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
562 PAC */
563 user_info_dc->sids = talloc_realloc(user_info_dc,
564 user_info_dc->sids,
565 struct dom_sid,
566 user_info_dc->num_sids+1);
567 if (user_info_dc->sids == NULL) {
568 TALLOC_FREE(user_info_dc);
569 return NT_STATUS_NO_MEMORY;
571 user_info_dc->sids[user_info_dc->num_sids] = global_sid_Enterprise_DCs;
572 user_info_dc->num_sids++;
575 if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
576 (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
577 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
578 user_info_dc->sids = talloc_realloc(user_info_dc,
579 user_info_dc->sids,
580 struct dom_sid,
581 user_info_dc->num_sids+1);
582 if (user_info_dc->sids == NULL) {
583 TALLOC_FREE(user_info_dc);
584 return NT_STATUS_NO_MEMORY;
586 user_info_dc->sids[user_info_dc->num_sids] = *domain_sid;
587 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids],
588 DOMAIN_RID_ENTERPRISE_READONLY_DCS);
589 user_info_dc->num_sids++;
592 info->authenticated = true;
594 talloc_free(tmp_ctx);
595 *_user_info_dc = user_info_dc;
597 return NT_STATUS_OK;
600 _PUBLIC_ NTSTATUS authsam_update_user_info_dc(TALLOC_CTX *mem_ctx,
601 struct ldb_context *sam_ctx,
602 struct auth_user_info_dc *user_info_dc)
604 char *filter = NULL;
605 NTSTATUS status;
606 uint32_t i;
607 uint32_t n = 0;
610 * This function exists to expand group memberships
611 * in the local domain (forest), as the token
612 * may come from a different domain.
616 * Filter out builtin groups from this token. We will search
617 * for builtin groups later.
619 status = authsam_domain_group_filter(mem_ctx, &filter);
620 if (!NT_STATUS_IS_OK(status)) {
621 TALLOC_FREE(user_info_dc);
622 return status;
626 * We loop only over the existing number of
627 * sids.
629 n = user_info_dc->num_sids;
630 for (i = 0; i < n; i++) {
631 struct dom_sid *sid = &user_info_dc->sids[i];
632 struct dom_sid_buf sid_buf;
633 char dn_str[sizeof(sid_buf.buf)*2];
634 DATA_BLOB dn_blob = data_blob_null;
636 snprintf(dn_str,
637 sizeof(dn_str),
638 "<SID=%s>",
639 dom_sid_str_buf(sid, &sid_buf));
640 dn_blob = data_blob_string_const(dn_str);
643 * We already have the SID in the token, so set
644 * 'only childs' flag to true and add all
645 * groups which match the filter.
647 status = dsdb_expand_nested_groups(sam_ctx, &dn_blob,
648 true, filter,
649 user_info_dc,
650 &user_info_dc->sids,
651 &user_info_dc->num_sids);
652 if (!NT_STATUS_IS_OK(status)) {
653 return status;
657 return NT_STATUS_OK;
660 NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
661 TALLOC_CTX *mem_ctx, const char *principal,
662 const char **attrs,
663 struct ldb_dn **domain_dn,
664 struct ldb_message **msg)
666 struct ldb_dn *user_dn;
667 NTSTATUS nt_status;
668 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
669 int ret;
671 if (!tmp_ctx) {
672 return NT_STATUS_NO_MEMORY;
675 nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
676 &user_dn, domain_dn);
677 if (!NT_STATUS_IS_OK(nt_status)) {
678 talloc_free(tmp_ctx);
679 return nt_status;
682 /* pull the user attributes */
683 ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
684 LDB_SCOPE_BASE, attrs,
685 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
686 "(objectClass=*)");
687 if (ret != LDB_SUCCESS) {
688 talloc_free(tmp_ctx);
689 return NT_STATUS_INTERNAL_DB_CORRUPTION;
691 talloc_steal(mem_ctx, *msg);
692 talloc_steal(mem_ctx, *domain_dn);
693 talloc_free(tmp_ctx);
695 return NT_STATUS_OK;
698 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
700 Supply either a principal or a DN
702 NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
703 struct loadparm_context *lp_ctx,
704 struct ldb_context *sam_ctx,
705 const char *principal,
706 struct ldb_dn *user_dn,
707 struct auth_user_info_dc **user_info_dc)
709 NTSTATUS nt_status;
710 DATA_BLOB user_sess_key = data_blob(NULL, 0);
711 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
713 struct ldb_message *msg;
714 struct ldb_dn *domain_dn;
716 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
717 if (!tmp_ctx) {
718 return NT_STATUS_NO_MEMORY;
721 if (principal) {
722 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
723 user_attrs, &domain_dn, &msg);
724 if (!NT_STATUS_IS_OK(nt_status)) {
725 talloc_free(tmp_ctx);
726 return nt_status;
728 } else if (user_dn) {
729 struct dom_sid *user_sid, *domain_sid;
730 int ret;
731 /* pull the user attributes */
732 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
733 LDB_SCOPE_BASE, user_attrs,
734 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
735 "(objectClass=*)");
736 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
737 talloc_free(tmp_ctx);
738 return NT_STATUS_NO_SUCH_USER;
739 } else if (ret != LDB_SUCCESS) {
740 talloc_free(tmp_ctx);
741 return NT_STATUS_INTERNAL_DB_CORRUPTION;
744 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
746 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
747 if (!NT_STATUS_IS_OK(nt_status)) {
748 return nt_status;
751 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
752 "(&(objectSid=%s)(objectClass=domain))",
753 ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
754 if (!domain_dn) {
755 struct dom_sid_buf buf;
756 DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
757 dom_sid_str_buf(domain_sid, &buf)));
758 return NT_STATUS_NO_SUCH_USER;
761 } else {
762 return NT_STATUS_INVALID_PARAMETER;
765 nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
766 lpcfg_netbios_name(lp_ctx),
767 lpcfg_sam_name(lp_ctx),
768 lpcfg_sam_dnsname(lp_ctx),
769 domain_dn,
770 msg,
771 user_sess_key, lm_sess_key,
772 user_info_dc);
773 if (!NT_STATUS_IS_OK(nt_status)) {
774 talloc_free(tmp_ctx);
775 return nt_status;
778 talloc_steal(mem_ctx, *user_info_dc);
779 talloc_free(tmp_ctx);
781 return NT_STATUS_OK;
785 * Returns the details for the Password Settings Object (PSO), if one applies
786 * the user.
788 static int authsam_get_user_pso(struct ldb_context *sam_ctx,
789 TALLOC_CTX *mem_ctx,
790 struct ldb_message *user_msg,
791 struct ldb_message **pso_msg)
793 const char *attrs[] = { "msDS-LockoutThreshold",
794 "msDS-LockoutObservationWindow",
795 NULL };
796 struct ldb_dn *pso_dn = NULL;
797 struct ldb_result *res = NULL;
798 int ret;
800 /* check if the user has a PSO that applies to it */
801 pso_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, user_msg,
802 "msDS-ResultantPSO");
804 if (pso_dn != NULL) {
805 ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, pso_dn, attrs, 0);
806 if (ret != LDB_SUCCESS) {
807 return ret;
810 *pso_msg = res->msgs[0];
813 return LDB_SUCCESS;
816 NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
817 struct ldb_message *msg,
818 struct ldb_dn *domain_dn)
820 const char *attrs[] = { "lockoutThreshold",
821 "lockOutObservationWindow",
822 "lockoutDuration",
823 "pwdProperties",
824 NULL };
825 int ret;
826 NTSTATUS status;
827 struct ldb_result *domain_res;
828 struct ldb_message *msg_mod = NULL;
829 struct ldb_message *pso_msg = NULL;
830 TALLOC_CTX *mem_ctx;
832 mem_ctx = talloc_new(msg);
833 if (mem_ctx == NULL) {
834 return NT_STATUS_NO_MEMORY;
837 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
838 if (ret != LDB_SUCCESS) {
839 TALLOC_FREE(mem_ctx);
840 return NT_STATUS_INTERNAL_DB_CORRUPTION;
843 ret = authsam_get_user_pso(sam_ctx, mem_ctx, msg, &pso_msg);
844 if (ret != LDB_SUCCESS) {
847 * fallback to using the domain defaults so that we still
848 * record the bad password attempt
850 DBG_ERR("Error (%d) checking PSO for %s",
851 ret, ldb_dn_get_linearized(msg->dn));
854 status = dsdb_update_bad_pwd_count(mem_ctx, sam_ctx,
855 msg, domain_res->msgs[0], pso_msg,
856 &msg_mod);
857 if (!NT_STATUS_IS_OK(status)) {
858 TALLOC_FREE(mem_ctx);
859 return status;
862 if (msg_mod != NULL) {
863 struct ldb_request *req;
865 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
866 msg_mod,
867 NULL,
868 NULL,
869 ldb_op_default_callback,
870 NULL);
871 if (ret != LDB_SUCCESS) {
872 goto done;
875 ret = ldb_request_add_control(req,
876 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
877 false, NULL);
878 if (ret != LDB_SUCCESS) {
879 talloc_free(req);
880 goto done;
883 ret = dsdb_autotransaction_request(sam_ctx, req);
884 talloc_free(req);
887 done:
888 if (ret != LDB_SUCCESS) {
889 DEBUG(0, ("Failed to update badPwdCount, badPasswordTime or set lockoutTime on %s: %s\n",
890 ldb_dn_get_linearized(msg_mod->dn), ldb_errstring(sam_ctx)));
891 TALLOC_FREE(mem_ctx);
892 return NT_STATUS_INTERNAL_ERROR;
895 TALLOC_FREE(mem_ctx);
896 return NT_STATUS_OK;
900 static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
901 struct ldb_message *msg_mod,
902 struct ldb_dn *domain_dn,
903 NTTIME old_timestamp,
904 NTTIME now)
907 * We only set lastLogonTimestamp if the current value is older than
908 * now - msDS-LogonTimeSyncInterval days.
910 * msDS-LogonTimeSyncInterval is an int32_t number of days, while
911 * lastLogonTimestamp is in the 64 bit 100ns NTTIME format.
913 * The docs say: "the initial update, after the domain functional
914 * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
915 * 14 days minus a random percentage of 5 days", but we aren't doing
916 * that. The blogosphere seems to think that this randomised update
917 * happens everytime, but [MS-ADA1] doesn't agree.
919 * Dochelp referred us to the following blog post:
920 * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
922 * en msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
923 * not changed.
925 static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
926 NULL };
927 int ret;
928 struct ldb_result *domain_res = NULL;
929 TALLOC_CTX *mem_ctx = NULL;
930 int32_t sync_interval;
931 NTTIME sync_interval_nt;
933 mem_ctx = talloc_new(msg_mod);
934 if (mem_ctx == NULL) {
935 return NT_STATUS_NO_MEMORY;
938 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
940 if (ret != LDB_SUCCESS || domain_res->count != 1) {
941 TALLOC_FREE(mem_ctx);
942 return NT_STATUS_INTERNAL_DB_CORRUPTION;
945 sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
946 "msDS-LogonTimeSyncInterval",
947 14);
948 DEBUG(5, ("sync interval is %d\n", sync_interval));
949 if (sync_interval == 0){
951 * Setting msDS-LogonTimeSyncInterval to zero is how you ask
952 * that nothing happens here.
954 TALLOC_FREE(mem_ctx);
955 return NT_STATUS_OK;
957 else if (sync_interval >= 5){
959 * Subtract "a random percentage of 5" days. Presumably this
960 * percentage is between 0 and 100, and modulus is accurate
961 * enough.
963 uint32_t r = generate_random() % 6;
964 sync_interval -= r;
965 DEBUG(5, ("randomised sync interval is %d (-%d)\n", sync_interval, r));
967 /* In the case where sync_interval < 5 there is no randomisation */
969 sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
971 DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
972 (long long int)old_timestamp,
973 (long long int)(now - sync_interval_nt),
974 (long long int)(old_timestamp - now + sync_interval_nt)));
976 if (old_timestamp > now){
977 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
978 (long long int)old_timestamp, (long long int)now));
979 /* then what? */
981 } else if (old_timestamp < now - sync_interval_nt){
982 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
983 (long long int)now));
985 /* The time has come to update lastLogonTimestamp */
986 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
987 "lastLogonTimestamp", now);
989 if (ret != LDB_SUCCESS) {
990 TALLOC_FREE(mem_ctx);
991 return NT_STATUS_NO_MEMORY;
994 TALLOC_FREE(mem_ctx);
995 return NT_STATUS_OK;
998 /****************************************************************************
999 Look for the specified user in the sam, return ldb result structures
1000 ****************************************************************************/
1002 NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
1003 const char *account_name,
1004 struct ldb_dn *domain_dn,
1005 struct ldb_message **ret_msg)
1007 int ret;
1009 /* pull the user attributes */
1010 ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
1011 user_attrs,
1012 DSDB_SEARCH_SHOW_EXTENDED_DN,
1013 "(&(sAMAccountName=%s)(objectclass=user))",
1014 ldb_binary_encode_string(mem_ctx, account_name));
1015 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1016 DEBUG(3,("sam_search_user: Couldn't find user [%s] in samdb, under %s\n",
1017 account_name, ldb_dn_get_linearized(domain_dn)));
1018 return NT_STATUS_NO_SUCH_USER;
1020 if (ret != LDB_SUCCESS) {
1021 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1024 return NT_STATUS_OK;
1028 /* Reset the badPwdCount to zero and update the lastLogon time. */
1029 NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
1030 const struct ldb_message *msg,
1031 struct ldb_dn *domain_dn,
1032 bool interactive_or_kerberos,
1033 struct netr_SendToSamBase **send_to_sam)
1035 int ret;
1036 NTSTATUS status;
1037 int badPwdCount;
1038 int dbBadPwdCount;
1039 int64_t lockoutTime;
1040 struct ldb_message *msg_mod;
1041 TALLOC_CTX *mem_ctx;
1042 struct timeval tv_now;
1043 NTTIME now;
1044 NTTIME lastLogonTimestamp;
1045 bool am_rodc = false;
1047 mem_ctx = talloc_new(msg);
1048 if (mem_ctx == NULL) {
1049 return NT_STATUS_NO_MEMORY;
1052 lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
1053 dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
1054 if (interactive_or_kerberos) {
1055 badPwdCount = dbBadPwdCount;
1056 } else {
1057 badPwdCount = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx,
1058 domain_dn, msg);
1060 lastLogonTimestamp =
1061 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
1063 DEBUG(5, ("lastLogonTimestamp is %lld\n",
1064 (long long int)lastLogonTimestamp));
1066 msg_mod = ldb_msg_new(mem_ctx);
1067 if (msg_mod == NULL) {
1068 TALLOC_FREE(mem_ctx);
1069 return NT_STATUS_NO_MEMORY;
1071 msg_mod->dn = msg->dn;
1073 if (lockoutTime != 0) {
1075 * This implies "badPwdCount" = 0, see samldb_lockout_time()
1077 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
1078 if (ret != LDB_SUCCESS) {
1079 TALLOC_FREE(mem_ctx);
1080 return NT_STATUS_NO_MEMORY;
1082 } else if (badPwdCount != 0) {
1083 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
1084 if (ret != LDB_SUCCESS) {
1085 TALLOC_FREE(mem_ctx);
1086 return NT_STATUS_NO_MEMORY;
1090 tv_now = timeval_current();
1091 now = timeval_to_nttime(&tv_now);
1093 if (interactive_or_kerberos ||
1094 (badPwdCount != 0 && lockoutTime == 0)) {
1095 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1096 "lastLogon", now);
1097 if (ret != LDB_SUCCESS) {
1098 TALLOC_FREE(mem_ctx);
1099 return NT_STATUS_NO_MEMORY;
1103 if (interactive_or_kerberos) {
1104 int logonCount;
1106 logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
1108 logonCount += 1;
1110 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1111 "logonCount", logonCount);
1112 if (ret != LDB_SUCCESS) {
1113 TALLOC_FREE(mem_ctx);
1114 return NT_STATUS_NO_MEMORY;
1116 } else {
1117 /* Set an unset logonCount to 0 on first successful login */
1118 if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
1119 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1120 "logonCount", 0);
1121 if (ret != LDB_SUCCESS) {
1122 TALLOC_FREE(mem_ctx);
1123 return NT_STATUS_NO_MEMORY;
1128 ret = samdb_rodc(sam_ctx, &am_rodc);
1129 if (ret != LDB_SUCCESS) {
1130 TALLOC_FREE(mem_ctx);
1131 return NT_STATUS_INTERNAL_ERROR;
1134 if (!am_rodc) {
1135 status = authsam_update_lastlogon_timestamp(sam_ctx, msg_mod, domain_dn,
1136 lastLogonTimestamp, now);
1137 if (!NT_STATUS_IS_OK(status)) {
1138 TALLOC_FREE(mem_ctx);
1139 return NT_STATUS_NO_MEMORY;
1141 } else {
1142 /* Perform the (async) SendToSAM calls for MS-SAMS */
1143 if (dbBadPwdCount != 0 && send_to_sam != NULL) {
1144 struct netr_SendToSamBase *base_msg;
1145 struct GUID guid = samdb_result_guid(msg, "objectGUID");
1146 base_msg = talloc_zero(msg, struct netr_SendToSamBase);
1148 base_msg->message_type = SendToSamResetBadPasswordCount;
1149 base_msg->message_size = 16;
1150 base_msg->message.reset_bad_password.guid = guid;
1151 *send_to_sam = base_msg;
1155 if (msg_mod->num_elements > 0) {
1156 unsigned int i;
1157 struct ldb_request *req;
1159 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
1160 for (i=0;i<msg_mod->num_elements;i++) {
1161 msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1164 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1165 msg_mod,
1166 NULL,
1167 NULL,
1168 ldb_op_default_callback,
1169 NULL);
1170 if (ret != LDB_SUCCESS) {
1171 goto done;
1174 ret = ldb_request_add_control(req,
1175 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1176 false, NULL);
1177 if (ret != LDB_SUCCESS) {
1178 talloc_free(req);
1179 goto done;
1182 ret = dsdb_autotransaction_request(sam_ctx, req);
1183 talloc_free(req);
1186 done:
1187 if (ret != LDB_SUCCESS) {
1188 DEBUG(0, ("Failed to set badPwdCount and lockoutTime "
1189 "to 0 and/or lastlogon to now (%lld) "
1190 "%s: %s\n", (long long int)now,
1191 ldb_dn_get_linearized(msg_mod->dn),
1192 ldb_errstring(sam_ctx)));
1193 TALLOC_FREE(mem_ctx);
1194 return NT_STATUS_INTERNAL_ERROR;
1197 TALLOC_FREE(mem_ctx);
1198 return NT_STATUS_OK;