vfs_ceph: add fake async pwrite/pread send/recv hooks
[Samba.git] / source4 / auth / sam.c
blobfb309f5100e1e80ef6652eb883d52986dcdb205b
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 #define KRBTGT_ATTRS \
36 /* required for the krb5 kdc */ \
37 "objectClass", \
38 "sAMAccountName", \
39 "userPrincipalName", \
40 "servicePrincipalName", \
41 "msDS-KeyVersionNumber", \
42 "msDS-SecondaryKrbTgtNumber", \
43 "msDS-SupportedEncryptionTypes", \
44 "supplementalCredentials", \
45 "msDS-AllowedToDelegateTo", \
47 /* passwords */ \
48 "dBCSPwd", \
49 "unicodePwd", \
51 "userAccountControl", \
52 "msDS-User-Account-Control-Computed", \
53 "objectSid", \
55 "pwdLastSet", \
56 "msDS-UserPasswordExpiryTimeComputed", \
57 "accountExpires"
59 const char *krbtgt_attrs[] = {
60 KRBTGT_ATTRS, NULL
63 const char *server_attrs[] = {
64 KRBTGT_ATTRS, NULL
67 const char *user_attrs[] = {
68 KRBTGT_ATTRS,
70 "logonHours",
73 * To allow us to zero the badPwdCount and lockoutTime on
74 * successful logon, without database churn
76 "lockoutTime",
79 * Needed for SendToSAM requests
81 "objectGUID",
83 /* check 'allowed workstations' */
84 "userWorkstations",
86 /* required for user_info_dc, not access control: */
87 "displayName",
88 "scriptPath",
89 "profilePath",
90 "homeDirectory",
91 "homeDrive",
92 "lastLogon",
93 "lastLogonTimestamp",
94 "lastLogoff",
95 "accountExpires",
96 "badPwdCount",
97 "logonCount",
98 "primaryGroupID",
99 "memberOf",
100 "badPasswordTime",
101 "lmPwdHistory",
102 "ntPwdHistory",
103 NULL,
106 /****************************************************************************
107 Check if a user is allowed to logon at this time. Note this is the
108 servers local time, as logon hours are just specified as a weekly
109 bitmask.
110 ****************************************************************************/
112 static bool logon_hours_ok(struct ldb_message *msg, const char *name_for_logs)
114 /* In logon hours first bit is Sunday from 12AM to 1AM */
115 const struct ldb_val *hours;
116 struct tm *utctime;
117 time_t lasttime;
118 const char *asct;
119 uint8_t bitmask, bitpos;
121 hours = ldb_msg_find_ldb_val(msg, "logonHours");
122 if (!hours) {
123 DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n", name_for_logs));
124 return true;
127 if (hours->length != 168/8) {
128 DEBUG(5,("logon_hours_ok: malformed logon hours restrictions for user %s\n", name_for_logs));
129 return true;
132 lasttime = time(NULL);
133 utctime = gmtime(&lasttime);
134 if (!utctime) {
135 DEBUG(1, ("logon_hours_ok: failed to get gmtime. Failing logon for user %s\n",
136 name_for_logs));
137 return false;
140 /* find the corresponding byte and bit */
141 bitpos = (utctime->tm_wday * 24 + utctime->tm_hour) % 168;
142 bitmask = 1 << (bitpos % 8);
144 if (! (hours->data[bitpos/8] & bitmask)) {
145 struct tm *t = localtime(&lasttime);
146 if (!t) {
147 asct = "INVALID TIME";
148 } else {
149 asct = asctime(t);
150 if (!asct) {
151 asct = "INVALID TIME";
155 DEBUG(1, ("logon_hours_ok: Account for user %s not allowed to "
156 "logon at this time (%s).\n",
157 name_for_logs, asct ));
158 return false;
161 asct = asctime(utctime);
162 DEBUG(5,("logon_hours_ok: user %s allowed to logon at this time (%s)\n",
163 name_for_logs, asct ? asct : "UNKNOWN TIME" ));
165 return true;
168 /****************************************************************************
169 Do a specific test for a SAM_ACCOUNT being valid for this connection
170 (ie not disabled, expired and the like).
171 ****************************************************************************/
172 _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
173 struct ldb_context *sam_ctx,
174 uint32_t logon_parameters,
175 struct ldb_dn *domain_dn,
176 struct ldb_message *msg,
177 const char *logon_workstation,
178 const char *name_for_logs,
179 bool allow_domain_trust,
180 bool password_change)
182 uint16_t acct_flags;
183 const char *workstation_list;
184 NTTIME acct_expiry;
185 NTTIME must_change_time;
186 struct timeval tv_now = timeval_current();
187 NTTIME now = timeval_to_nttime(&tv_now);
189 DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
191 acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
193 acct_expiry = samdb_result_account_expires(msg);
195 /* Check for when we must change this password, taking the
196 * userAccountControl flags into account */
197 must_change_time = samdb_result_nttime(msg,
198 "msDS-UserPasswordExpiryTimeComputed", 0);
200 workstation_list = ldb_msg_find_attr_as_string(msg, "userWorkstations", NULL);
202 /* Quit if the account was disabled. */
203 if (acct_flags & ACB_DISABLED) {
204 DEBUG(2,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
205 return NT_STATUS_ACCOUNT_DISABLED;
208 /* Quit if the account was locked out. */
209 if (acct_flags & ACB_AUTOLOCK) {
210 DEBUG(2,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
211 return NT_STATUS_ACCOUNT_LOCKED_OUT;
214 /* Test account expire time */
215 if (now > acct_expiry) {
216 DEBUG(2,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
217 DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
218 nt_time_string(mem_ctx, acct_expiry)));
219 return NT_STATUS_ACCOUNT_EXPIRED;
222 /* check for immediate expiry "must change at next logon" (but not if this is a password change request) */
223 if ((must_change_time == 0) && !password_change) {
224 DEBUG(2,("sam_account_ok: Account for user '%s' password must change!.\n",
225 name_for_logs));
226 return NT_STATUS_PASSWORD_MUST_CHANGE;
229 /* check for expired password (but not if this is a password change request) */
230 if ((must_change_time < now) && !password_change) {
231 DEBUG(2,("sam_account_ok: Account for user '%s' password expired!.\n",
232 name_for_logs));
233 DEBUG(2,("sam_account_ok: Password expired at '%s' unix time.\n",
234 nt_time_string(mem_ctx, must_change_time)));
235 return NT_STATUS_PASSWORD_EXPIRED;
238 /* Test workstation. Workstation list is comma separated. */
239 if (logon_workstation && workstation_list && *workstation_list) {
240 bool invalid_ws = true;
241 int i;
242 char **workstations = str_list_make(mem_ctx, workstation_list, ",");
244 for (i = 0; workstations && workstations[i]; i++) {
245 DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
246 workstations[i], logon_workstation));
248 if (strequal(workstations[i], logon_workstation)) {
249 invalid_ws = false;
250 break;
254 talloc_free(workstations);
256 if (invalid_ws) {
257 return NT_STATUS_INVALID_WORKSTATION;
261 if (!logon_hours_ok(msg, name_for_logs)) {
262 return NT_STATUS_INVALID_LOGON_HOURS;
265 if (!allow_domain_trust) {
266 if (acct_flags & ACB_DOMTRUST) {
267 DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
268 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
271 if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
272 if (acct_flags & ACB_SVRTRUST) {
273 DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
274 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
277 if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
278 /* TODO: this fails with current solaris client. We
279 need to work with Gordon to work out why */
280 if (acct_flags & ACB_WSTRUST) {
281 DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
282 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
286 return NT_STATUS_OK;
289 static NTSTATUS authsam_domain_group_filter(TALLOC_CTX *mem_ctx,
290 char **_filter)
292 char *filter = NULL;
294 *_filter = NULL;
296 filter = talloc_strdup(mem_ctx, "(&(objectClass=group)");
297 if (filter == NULL) {
298 return NT_STATUS_NO_MEMORY;
302 * Skip all builtin groups, they're added later.
304 filter = talloc_asprintf_append_buffer(filter,
305 "(!(groupType:1.2.840.113556.1.4.803:=%u))",
306 GROUP_TYPE_BUILTIN_LOCAL_GROUP);
307 if (filter == NULL) {
308 return NT_STATUS_NO_MEMORY;
311 * Only include security groups.
313 filter = talloc_asprintf_append_buffer(filter,
314 "(groupType:1.2.840.113556.1.4.803:=%u))",
315 GROUP_TYPE_SECURITY_ENABLED);
316 if (filter == NULL) {
317 return NT_STATUS_NO_MEMORY;
320 *_filter = filter;
321 return NT_STATUS_OK;
324 _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
325 struct ldb_context *sam_ctx,
326 const char *netbios_name,
327 const char *domain_name,
328 const char *dns_domain_name,
329 struct ldb_dn *domain_dn,
330 struct ldb_message *msg,
331 DATA_BLOB user_sess_key,
332 DATA_BLOB lm_sess_key,
333 struct auth_user_info_dc **_user_info_dc)
335 NTSTATUS status;
336 struct auth_user_info_dc *user_info_dc;
337 struct auth_user_info *info;
338 const char *str = NULL;
339 char *filter = NULL;
340 /* SIDs for the account and his primary group */
341 struct dom_sid *account_sid;
342 const char *primary_group_string;
343 const char *primary_group_dn;
344 DATA_BLOB primary_group_blob;
345 /* SID structures for the expanded group memberships */
346 struct dom_sid *sids = NULL;
347 unsigned int num_sids = 0, i;
348 struct dom_sid *domain_sid;
349 TALLOC_CTX *tmp_ctx;
350 struct ldb_message_element *el;
352 user_info_dc = talloc(mem_ctx, struct auth_user_info_dc);
353 NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
355 tmp_ctx = talloc_new(user_info_dc);
356 if (user_info_dc == NULL) {
357 TALLOC_FREE(user_info_dc);
358 return NT_STATUS_NO_MEMORY;
361 sids = talloc_array(user_info_dc, struct dom_sid, 2);
362 if (sids == NULL) {
363 TALLOC_FREE(user_info_dc);
364 return NT_STATUS_NO_MEMORY;
367 num_sids = 2;
369 account_sid = samdb_result_dom_sid(user_info_dc, msg, "objectSid");
370 if (account_sid == NULL) {
371 TALLOC_FREE(user_info_dc);
372 return NT_STATUS_NO_MEMORY;
375 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
376 if (!NT_STATUS_IS_OK(status)) {
377 talloc_free(user_info_dc);
378 return status;
381 sids[PRIMARY_USER_SID_INDEX] = *account_sid;
382 sids[PRIMARY_GROUP_SID_INDEX] = *domain_sid;
383 sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX], ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
386 * Filter out builtin groups from this token. We will search
387 * for builtin groups later, and not include them in the PAC
388 * or SamLogon validation info.
390 status = authsam_domain_group_filter(tmp_ctx, &filter);
391 if (!NT_STATUS_IS_OK(status)) {
392 TALLOC_FREE(user_info_dc);
393 return status;
396 primary_group_string = dom_sid_string(tmp_ctx, &sids[PRIMARY_GROUP_SID_INDEX]);
397 if (primary_group_string == NULL) {
398 TALLOC_FREE(user_info_dc);
399 return NT_STATUS_NO_MEMORY;
402 primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string);
403 if (primary_group_dn == NULL) {
404 TALLOC_FREE(user_info_dc);
405 return NT_STATUS_NO_MEMORY;
408 primary_group_blob = data_blob_string_const(primary_group_dn);
410 /* Expands the primary group - this function takes in
411 * memberOf-like values, so we fake one up with the
412 * <SID=S-...> format of DN and then let it expand
413 * them, as long as they meet the filter - so only
414 * domain groups, not builtin groups
416 * The primary group is still treated specially, so we set the
417 * 'only childs' flag to true
419 status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
420 user_info_dc, &sids, &num_sids);
421 if (!NT_STATUS_IS_OK(status)) {
422 talloc_free(user_info_dc);
423 return status;
426 /* Expands the additional groups */
427 el = ldb_msg_find_element(msg, "memberOf");
428 for (i = 0; el && i < el->num_values; i++) {
429 /* This function takes in memberOf values and expands
430 * them, as long as they meet the filter - so only
431 * domain groups, not builtin groups */
432 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
433 user_info_dc, &sids, &num_sids);
434 if (!NT_STATUS_IS_OK(status)) {
435 talloc_free(user_info_dc);
436 return status;
440 user_info_dc->sids = sids;
441 user_info_dc->num_sids = num_sids;
443 user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
444 NT_STATUS_HAVE_NO_MEMORY(user_info_dc->info);
446 info->account_name = talloc_steal(info,
447 ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL));
449 info->user_principal_name = talloc_steal(info,
450 ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL));
451 if (info->user_principal_name == NULL && dns_domain_name != NULL) {
452 info->user_principal_name = talloc_asprintf(info, "%s@%s",
453 info->account_name,
454 dns_domain_name);
455 if (info->user_principal_name == NULL) {
456 TALLOC_FREE(user_info_dc);
457 return NT_STATUS_NO_MEMORY;
459 info->user_principal_constructed = true;
462 info->domain_name = talloc_strdup(info, domain_name);
463 if (info->domain_name == NULL) {
464 TALLOC_FREE(user_info_dc);
465 return NT_STATUS_NO_MEMORY;
468 if (dns_domain_name != NULL) {
469 info->dns_domain_name = talloc_strdup(info, dns_domain_name);
470 if (info->dns_domain_name == NULL) {
471 TALLOC_FREE(user_info_dc);
472 return NT_STATUS_NO_MEMORY;
476 str = ldb_msg_find_attr_as_string(msg, "displayName", "");
477 info->full_name = talloc_strdup(info, str);
478 if (info->full_name == NULL) {
479 TALLOC_FREE(user_info_dc);
480 return NT_STATUS_NO_MEMORY;
483 str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
484 info->logon_script = talloc_strdup(info, str);
485 if (info->logon_script == NULL) {
486 TALLOC_FREE(user_info_dc);
487 return NT_STATUS_NO_MEMORY;
490 str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
491 info->profile_path = talloc_strdup(info, str);
492 if (info->profile_path == NULL) {
493 TALLOC_FREE(user_info_dc);
494 return NT_STATUS_NO_MEMORY;
497 str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
498 info->home_directory = talloc_strdup(info, str);
499 if (info->home_directory == NULL) {
500 TALLOC_FREE(user_info_dc);
501 return NT_STATUS_NO_MEMORY;
504 str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
505 info->home_drive = talloc_strdup(info, str);
506 if (info->home_drive == NULL) {
507 TALLOC_FREE(user_info_dc);
508 return NT_STATUS_NO_MEMORY;
511 info->logon_server = talloc_strdup(info, netbios_name);
512 if (info->logon_server == NULL) {
513 TALLOC_FREE(user_info_dc);
514 return NT_STATUS_NO_MEMORY;
517 info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
518 info->last_logoff = samdb_result_last_logoff(msg);
519 info->acct_expiry = samdb_result_account_expires(msg);
520 info->last_password_change = samdb_result_nttime(msg,
521 "pwdLastSet", 0);
522 info->allow_password_change
523 = samdb_result_allow_password_change(sam_ctx, mem_ctx,
524 domain_dn, msg, "pwdLastSet");
525 info->force_password_change = samdb_result_nttime(msg,
526 "msDS-UserPasswordExpiryTimeComputed", 0);
527 info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
528 info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
531 info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
533 user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
534 user_sess_key.data,
535 user_sess_key.length);
536 if (user_sess_key.data) {
537 if (user_info_dc->user_session_key.data == NULL) {
538 TALLOC_FREE(user_info_dc);
539 return NT_STATUS_NO_MEMORY;
542 user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
543 lm_sess_key.data,
544 lm_sess_key.length);
545 if (lm_sess_key.data) {
546 if (user_info_dc->lm_session_key.data == NULL) {
547 TALLOC_FREE(user_info_dc);
548 return NT_STATUS_NO_MEMORY;
552 if (info->acct_flags & ACB_SVRTRUST) {
553 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
554 PAC */
555 user_info_dc->sids = talloc_realloc(user_info_dc,
556 user_info_dc->sids,
557 struct dom_sid,
558 user_info_dc->num_sids+1);
559 if (user_info_dc->sids == NULL) {
560 TALLOC_FREE(user_info_dc);
561 return NT_STATUS_NO_MEMORY;
563 user_info_dc->sids[user_info_dc->num_sids] = global_sid_Enterprise_DCs;
564 user_info_dc->num_sids++;
567 if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
568 (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
569 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
570 user_info_dc->sids = talloc_realloc(user_info_dc,
571 user_info_dc->sids,
572 struct dom_sid,
573 user_info_dc->num_sids+1);
574 if (user_info_dc->sids == NULL) {
575 TALLOC_FREE(user_info_dc);
576 return NT_STATUS_NO_MEMORY;
578 user_info_dc->sids[user_info_dc->num_sids] = *domain_sid;
579 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids],
580 DOMAIN_RID_ENTERPRISE_READONLY_DCS);
581 user_info_dc->num_sids++;
584 info->authenticated = true;
586 talloc_free(tmp_ctx);
587 *_user_info_dc = user_info_dc;
589 return NT_STATUS_OK;
592 _PUBLIC_ NTSTATUS authsam_update_user_info_dc(TALLOC_CTX *mem_ctx,
593 struct ldb_context *sam_ctx,
594 struct auth_user_info_dc *user_info_dc)
596 char *filter = NULL;
597 NTSTATUS status;
598 uint32_t i;
599 uint32_t n = 0;
602 * This function exists to expand group memberships
603 * in the local domain (forest), as the token
604 * may come from a different domain.
608 * Filter out builtin groups from this token. We will search
609 * for builtin groups later.
611 status = authsam_domain_group_filter(mem_ctx, &filter);
612 if (!NT_STATUS_IS_OK(status)) {
613 TALLOC_FREE(user_info_dc);
614 return status;
618 * We loop only over the existing number of
619 * sids.
621 n = user_info_dc->num_sids;
622 for (i = 0; i < n; i++) {
623 struct dom_sid *sid = &user_info_dc->sids[i];
624 char sid_buf[DOM_SID_STR_BUFLEN] = {0,};
625 char dn_str[DOM_SID_STR_BUFLEN*2] = {0,};
626 DATA_BLOB dn_blob = data_blob_null;
627 int len;
629 len = dom_sid_string_buf(sid, sid_buf, sizeof(sid_buf));
630 if (len+1 > sizeof(sid_buf)) {
631 return NT_STATUS_INVALID_SID;
633 snprintf(dn_str, sizeof(dn_str), "<SID=%s>", sid_buf);
634 dn_blob = data_blob_string_const(dn_str);
637 * We already have the SID in the token, so set
638 * 'only childs' flag to true and add all
639 * groups which match the filter.
641 status = dsdb_expand_nested_groups(sam_ctx, &dn_blob,
642 true, filter,
643 user_info_dc,
644 &user_info_dc->sids,
645 &user_info_dc->num_sids);
646 if (!NT_STATUS_IS_OK(status)) {
647 return status;
651 return NT_STATUS_OK;
654 NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
655 TALLOC_CTX *mem_ctx, const char *principal,
656 const char **attrs,
657 struct ldb_dn **domain_dn,
658 struct ldb_message **msg)
660 struct ldb_dn *user_dn;
661 NTSTATUS nt_status;
662 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
663 int ret;
665 if (!tmp_ctx) {
666 return NT_STATUS_NO_MEMORY;
669 nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
670 &user_dn, domain_dn);
671 if (!NT_STATUS_IS_OK(nt_status)) {
672 talloc_free(tmp_ctx);
673 return nt_status;
676 /* pull the user attributes */
677 ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
678 LDB_SCOPE_BASE, attrs,
679 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
680 "(objectClass=*)");
681 if (ret != LDB_SUCCESS) {
682 talloc_free(tmp_ctx);
683 return NT_STATUS_INTERNAL_DB_CORRUPTION;
685 talloc_steal(mem_ctx, *msg);
686 talloc_steal(mem_ctx, *domain_dn);
687 talloc_free(tmp_ctx);
689 return NT_STATUS_OK;
692 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
694 Supply either a principal or a DN
696 NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
697 struct loadparm_context *lp_ctx,
698 struct ldb_context *sam_ctx,
699 const char *principal,
700 struct ldb_dn *user_dn,
701 struct auth_user_info_dc **user_info_dc)
703 NTSTATUS nt_status;
704 DATA_BLOB user_sess_key = data_blob(NULL, 0);
705 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
707 struct ldb_message *msg;
708 struct ldb_dn *domain_dn;
710 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
711 if (!tmp_ctx) {
712 return NT_STATUS_NO_MEMORY;
715 if (principal) {
716 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
717 user_attrs, &domain_dn, &msg);
718 if (!NT_STATUS_IS_OK(nt_status)) {
719 talloc_free(tmp_ctx);
720 return nt_status;
722 } else if (user_dn) {
723 struct dom_sid *user_sid, *domain_sid;
724 int ret;
725 /* pull the user attributes */
726 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
727 LDB_SCOPE_BASE, user_attrs,
728 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
729 "(objectClass=*)");
730 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
731 talloc_free(tmp_ctx);
732 return NT_STATUS_NO_SUCH_USER;
733 } else if (ret != LDB_SUCCESS) {
734 talloc_free(tmp_ctx);
735 return NT_STATUS_INTERNAL_DB_CORRUPTION;
738 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
740 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
741 if (!NT_STATUS_IS_OK(nt_status)) {
742 return nt_status;
745 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
746 "(&(objectSid=%s)(objectClass=domain))",
747 ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
748 if (!domain_dn) {
749 DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
750 dom_sid_string(tmp_ctx, domain_sid)));
751 return NT_STATUS_NO_SUCH_USER;
754 } else {
755 return NT_STATUS_INVALID_PARAMETER;
758 nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
759 lpcfg_netbios_name(lp_ctx),
760 lpcfg_sam_name(lp_ctx),
761 lpcfg_sam_dnsname(lp_ctx),
762 domain_dn,
763 msg,
764 user_sess_key, lm_sess_key,
765 user_info_dc);
766 if (!NT_STATUS_IS_OK(nt_status)) {
767 talloc_free(tmp_ctx);
768 return nt_status;
771 talloc_steal(mem_ctx, *user_info_dc);
772 talloc_free(tmp_ctx);
774 return NT_STATUS_OK;
777 NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
778 struct ldb_message *msg,
779 struct ldb_dn *domain_dn)
781 const char *attrs[] = { "lockoutThreshold",
782 "lockOutObservationWindow",
783 "lockoutDuration",
784 "pwdProperties",
785 NULL };
786 int ret;
787 NTSTATUS status;
788 struct ldb_result *domain_res;
789 struct ldb_message *msg_mod = NULL;
790 TALLOC_CTX *mem_ctx;
792 mem_ctx = talloc_new(msg);
793 if (mem_ctx == NULL) {
794 return NT_STATUS_NO_MEMORY;
797 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
798 if (ret != LDB_SUCCESS) {
799 TALLOC_FREE(mem_ctx);
800 return NT_STATUS_INTERNAL_DB_CORRUPTION;
803 status = dsdb_update_bad_pwd_count(mem_ctx, sam_ctx,
804 msg, domain_res->msgs[0], &msg_mod);
805 if (!NT_STATUS_IS_OK(status)) {
806 TALLOC_FREE(mem_ctx);
807 return status;
810 if (msg_mod != NULL) {
811 struct ldb_request *req;
813 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
814 msg_mod,
815 NULL,
816 NULL,
817 ldb_op_default_callback,
818 NULL);
819 if (ret != LDB_SUCCESS) {
820 goto done;
823 ret = ldb_request_add_control(req,
824 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
825 false, NULL);
826 if (ret != LDB_SUCCESS) {
827 talloc_free(req);
828 goto done;
831 ret = dsdb_autotransaction_request(sam_ctx, req);
832 talloc_free(req);
835 done:
836 if (ret != LDB_SUCCESS) {
837 DEBUG(0, ("Failed to update badPwdCount, badPasswordTime or set lockoutTime on %s: %s\n",
838 ldb_dn_get_linearized(msg_mod->dn), ldb_errstring(sam_ctx)));
839 TALLOC_FREE(mem_ctx);
840 return NT_STATUS_INTERNAL_ERROR;
843 TALLOC_FREE(mem_ctx);
844 return NT_STATUS_OK;
848 static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
849 struct ldb_message *msg_mod,
850 struct ldb_dn *domain_dn,
851 NTTIME old_timestamp,
852 NTTIME now)
855 * We only set lastLogonTimestamp if the current value is older than
856 * now - msDS-LogonTimeSyncInterval days.
858 * msDS-LogonTimeSyncInterval is an int32_t number of days, while
859 * lastLogonTimestamp is in the 64 bit 100ns NTTIME format.
861 * The docs say: "the initial update, after the domain functional
862 * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
863 * 14 days minus a random percentage of 5 days", but we aren't doing
864 * that. The blogosphere seems to think that this randomised update
865 * happens everytime, but [MS-ADA1] doesn't agree.
867 * Dochelp referred us to the following blog post:
868 * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
870 * en msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
871 * not changed.
873 static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
874 NULL };
875 int ret;
876 struct ldb_result *domain_res = NULL;
877 TALLOC_CTX *mem_ctx = NULL;
878 int32_t sync_interval;
879 NTTIME sync_interval_nt;
881 mem_ctx = talloc_new(msg_mod);
882 if (mem_ctx == NULL) {
883 return NT_STATUS_NO_MEMORY;
886 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
888 if (ret != LDB_SUCCESS || domain_res->count != 1) {
889 TALLOC_FREE(mem_ctx);
890 return NT_STATUS_INTERNAL_DB_CORRUPTION;
893 sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
894 "msDS-LogonTimeSyncInterval",
895 14);
896 DEBUG(5, ("sync interval is %d\n", sync_interval));
897 if (sync_interval == 0){
899 * Setting msDS-LogonTimeSyncInterval to zero is how you ask
900 * that nothing happens here.
902 TALLOC_FREE(mem_ctx);
903 return NT_STATUS_OK;
905 else if (sync_interval >= 5){
907 * Subtract "a random percentage of 5" days. Presumably this
908 * percentage is between 0 and 100, and modulus is accurate
909 * enough.
911 uint32_t r = generate_random() % 6;
912 sync_interval -= r;
913 DEBUG(5, ("randomised sync interval is %d (-%d)\n", sync_interval, r));
915 /* In the case where sync_interval < 5 there is no randomisation */
917 sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
919 DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
920 (long long int)old_timestamp,
921 (long long int)(now - sync_interval_nt),
922 (long long int)(old_timestamp - now + sync_interval_nt)));
924 if (old_timestamp > now){
925 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
926 (long long int)old_timestamp, (long long int)now));
927 /* then what? */
929 } else if (old_timestamp < now - sync_interval_nt){
930 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
931 (long long int)now));
933 /* The time has come to update lastLogonTimestamp */
934 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
935 "lastLogonTimestamp", now);
937 if (ret != LDB_SUCCESS) {
938 TALLOC_FREE(mem_ctx);
939 return NT_STATUS_NO_MEMORY;
942 TALLOC_FREE(mem_ctx);
943 return NT_STATUS_OK;
946 /****************************************************************************
947 Look for the specified user in the sam, return ldb result structures
948 ****************************************************************************/
950 NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
951 const char *account_name,
952 struct ldb_dn *domain_dn,
953 struct ldb_message **ret_msg)
955 int ret;
957 /* pull the user attributes */
958 ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
959 user_attrs,
960 DSDB_SEARCH_SHOW_EXTENDED_DN,
961 "(&(sAMAccountName=%s)(objectclass=user))",
962 ldb_binary_encode_string(mem_ctx, account_name));
963 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
964 DEBUG(3,("sam_search_user: Couldn't find user [%s] in samdb, under %s\n",
965 account_name, ldb_dn_get_linearized(domain_dn)));
966 return NT_STATUS_NO_SUCH_USER;
968 if (ret != LDB_SUCCESS) {
969 return NT_STATUS_INTERNAL_DB_CORRUPTION;
972 return NT_STATUS_OK;
976 /* Reset the badPwdCount to zero and update the lastLogon time. */
977 NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
978 const struct ldb_message *msg,
979 struct ldb_dn *domain_dn,
980 bool interactive_or_kerberos,
981 struct netr_SendToSamBase **send_to_sam)
983 int ret;
984 NTSTATUS status;
985 int badPwdCount;
986 int dbBadPwdCount;
987 int64_t lockoutTime;
988 struct ldb_message *msg_mod;
989 TALLOC_CTX *mem_ctx;
990 struct timeval tv_now;
991 NTTIME now;
992 NTTIME lastLogonTimestamp;
993 bool am_rodc = false;
995 mem_ctx = talloc_new(msg);
996 if (mem_ctx == NULL) {
997 return NT_STATUS_NO_MEMORY;
1000 lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
1001 dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
1002 if (interactive_or_kerberos) {
1003 badPwdCount = dbBadPwdCount;
1004 } else {
1005 badPwdCount = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx,
1006 domain_dn, msg);
1008 lastLogonTimestamp =
1009 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
1011 DEBUG(5, ("lastLogonTimestamp is %lld\n",
1012 (long long int)lastLogonTimestamp));
1014 msg_mod = ldb_msg_new(mem_ctx);
1015 if (msg_mod == NULL) {
1016 TALLOC_FREE(mem_ctx);
1017 return NT_STATUS_NO_MEMORY;
1019 msg_mod->dn = msg->dn;
1021 if (lockoutTime != 0) {
1023 * This implies "badPwdCount" = 0, see samldb_lockout_time()
1025 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
1026 if (ret != LDB_SUCCESS) {
1027 TALLOC_FREE(mem_ctx);
1028 return NT_STATUS_NO_MEMORY;
1030 } else if (badPwdCount != 0) {
1031 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
1032 if (ret != LDB_SUCCESS) {
1033 TALLOC_FREE(mem_ctx);
1034 return NT_STATUS_NO_MEMORY;
1038 tv_now = timeval_current();
1039 now = timeval_to_nttime(&tv_now);
1041 if (interactive_or_kerberos ||
1042 (badPwdCount != 0 && lockoutTime == 0)) {
1043 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
1044 "lastLogon", now);
1045 if (ret != LDB_SUCCESS) {
1046 TALLOC_FREE(mem_ctx);
1047 return NT_STATUS_NO_MEMORY;
1051 if (interactive_or_kerberos) {
1052 int logonCount;
1054 logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
1056 logonCount += 1;
1058 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1059 "logonCount", logonCount);
1060 if (ret != LDB_SUCCESS) {
1061 TALLOC_FREE(mem_ctx);
1062 return NT_STATUS_NO_MEMORY;
1064 } else {
1065 /* Set an unset logonCount to 0 on first successful login */
1066 if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
1067 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
1068 "logonCount", 0);
1069 if (ret != LDB_SUCCESS) {
1070 TALLOC_FREE(mem_ctx);
1071 return NT_STATUS_NO_MEMORY;
1076 ret = samdb_rodc(sam_ctx, &am_rodc);
1077 if (ret != LDB_SUCCESS) {
1078 TALLOC_FREE(mem_ctx);
1079 return NT_STATUS_INTERNAL_ERROR;
1082 if (!am_rodc) {
1083 status = authsam_update_lastlogon_timestamp(sam_ctx, msg_mod, domain_dn,
1084 lastLogonTimestamp, now);
1085 if (!NT_STATUS_IS_OK(status)) {
1086 TALLOC_FREE(mem_ctx);
1087 return NT_STATUS_NO_MEMORY;
1089 } else {
1090 /* Perform the (async) SendToSAM calls for MS-SAMS */
1091 if (dbBadPwdCount != 0 && send_to_sam != NULL) {
1092 struct netr_SendToSamBase *base_msg;
1093 struct GUID guid = samdb_result_guid(msg, "objectGUID");
1094 base_msg = talloc_zero(msg, struct netr_SendToSamBase);
1096 base_msg->message_type = SendToSamResetBadPasswordCount;
1097 base_msg->message_size = 16;
1098 base_msg->message.reset_bad_password.guid = guid;
1099 *send_to_sam = base_msg;
1103 if (msg_mod->num_elements > 0) {
1104 unsigned int i;
1105 struct ldb_request *req;
1107 /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
1108 for (i=0;i<msg_mod->num_elements;i++) {
1109 msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1112 ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
1113 msg_mod,
1114 NULL,
1115 NULL,
1116 ldb_op_default_callback,
1117 NULL);
1118 if (ret != LDB_SUCCESS) {
1119 goto done;
1122 ret = ldb_request_add_control(req,
1123 DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
1124 false, NULL);
1125 if (ret != LDB_SUCCESS) {
1126 talloc_free(req);
1127 goto done;
1130 ret = dsdb_autotransaction_request(sam_ctx, req);
1131 talloc_free(req);
1134 done:
1135 if (ret != LDB_SUCCESS) {
1136 DEBUG(0, ("Failed to set badPwdCount and lockoutTime "
1137 "to 0 and/or lastlogon to now (%lld) "
1138 "%s: %s\n", (long long int)now,
1139 ldb_dn_get_linearized(msg_mod->dn),
1140 ldb_errstring(sam_ctx)));
1141 TALLOC_FREE(mem_ctx);
1142 return NT_STATUS_INTERNAL_ERROR;
1145 TALLOC_FREE(mem_ctx);
1146 return NT_STATUS_OK;