lib: Update nss_wrapper to version 1.1.3
[Samba.git] / source4 / auth / sam.c
blobcdfe8dd2fda4f997c740473b89b40ea4b8664e7d
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"
34 #define KRBTGT_ATTRS \
35 /* required for the krb5 kdc */ \
36 "objectClass", \
37 "sAMAccountName", \
38 "userPrincipalName", \
39 "servicePrincipalName", \
40 "msDS-KeyVersionNumber", \
41 "msDS-SecondaryKrbTgtNumber", \
42 "msDS-SupportedEncryptionTypes", \
43 "supplementalCredentials", \
44 "msDS-AllowedToDelegateTo", \
46 /* passwords */ \
47 "dBCSPwd", \
48 "unicodePwd", \
50 "userAccountControl", \
51 "msDS-User-Account-Control-Computed", \
52 "objectSid", \
54 "pwdLastSet", \
55 "accountExpires"
57 const char *krbtgt_attrs[] = {
58 KRBTGT_ATTRS, NULL
61 const char *server_attrs[] = {
62 KRBTGT_ATTRS, NULL
65 const char *user_attrs[] = {
66 KRBTGT_ATTRS,
68 "logonHours",
71 * To allow us to zero the badPwdCount and lockoutTime on
72 * successful logon, without database churn
74 "lockoutTime",
76 /* check 'allowed workstations' */
77 "userWorkstations",
79 /* required for user_info_dc, not access control: */
80 "displayName",
81 "scriptPath",
82 "profilePath",
83 "homeDirectory",
84 "homeDrive",
85 "lastLogon",
86 "lastLogonTimestamp",
87 "lastLogoff",
88 "accountExpires",
89 "badPwdCount",
90 "logonCount",
91 "primaryGroupID",
92 "memberOf",
93 "badPasswordTime",
94 "lmPwdHistory",
95 "ntPwdHistory",
96 NULL,
99 /****************************************************************************
100 Check if a user is allowed to logon at this time. Note this is the
101 servers local time, as logon hours are just specified as a weekly
102 bitmask.
103 ****************************************************************************/
105 static bool logon_hours_ok(struct ldb_message *msg, const char *name_for_logs)
107 /* In logon hours first bit is Sunday from 12AM to 1AM */
108 const struct ldb_val *hours;
109 struct tm *utctime;
110 time_t lasttime;
111 const char *asct;
112 uint8_t bitmask, bitpos;
114 hours = ldb_msg_find_ldb_val(msg, "logonHours");
115 if (!hours) {
116 DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n", name_for_logs));
117 return true;
120 if (hours->length != 168/8) {
121 DEBUG(5,("logon_hours_ok: malformed logon hours restrictions for user %s\n", name_for_logs));
122 return true;
125 lasttime = time(NULL);
126 utctime = gmtime(&lasttime);
127 if (!utctime) {
128 DEBUG(1, ("logon_hours_ok: failed to get gmtime. Failing logon for user %s\n",
129 name_for_logs));
130 return false;
133 /* find the corresponding byte and bit */
134 bitpos = (utctime->tm_wday * 24 + utctime->tm_hour) % 168;
135 bitmask = 1 << (bitpos % 8);
137 if (! (hours->data[bitpos/8] & bitmask)) {
138 struct tm *t = localtime(&lasttime);
139 if (!t) {
140 asct = "INVALID TIME";
141 } else {
142 asct = asctime(t);
143 if (!asct) {
144 asct = "INVALID TIME";
148 DEBUG(1, ("logon_hours_ok: Account for user %s not allowed to "
149 "logon at this time (%s).\n",
150 name_for_logs, asct ));
151 return false;
154 asct = asctime(utctime);
155 DEBUG(5,("logon_hours_ok: user %s allowed to logon at this time (%s)\n",
156 name_for_logs, asct ? asct : "UNKNOWN TIME" ));
158 return true;
161 /****************************************************************************
162 Do a specific test for a SAM_ACCOUNT being valid for this connection
163 (ie not disabled, expired and the like).
164 ****************************************************************************/
165 _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
166 struct ldb_context *sam_ctx,
167 uint32_t logon_parameters,
168 struct ldb_dn *domain_dn,
169 struct ldb_message *msg,
170 const char *logon_workstation,
171 const char *name_for_logs,
172 bool allow_domain_trust,
173 bool password_change)
175 uint16_t acct_flags;
176 const char *workstation_list;
177 NTTIME acct_expiry;
178 NTTIME must_change_time;
179 struct timeval tv_now = timeval_current();
180 NTTIME now = timeval_to_nttime(&tv_now);
182 DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
184 acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
186 acct_expiry = samdb_result_account_expires(msg);
188 /* Check for when we must change this password, taking the
189 * userAccountControl flags into account */
190 must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx,
191 domain_dn, msg);
193 workstation_list = ldb_msg_find_attr_as_string(msg, "userWorkstations", NULL);
195 /* Quit if the account was disabled. */
196 if (acct_flags & ACB_DISABLED) {
197 DEBUG(2,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
198 return NT_STATUS_ACCOUNT_DISABLED;
201 /* Quit if the account was locked out. */
202 if (acct_flags & ACB_AUTOLOCK) {
203 DEBUG(2,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
204 return NT_STATUS_ACCOUNT_LOCKED_OUT;
207 /* Test account expire time */
208 if (now > acct_expiry) {
209 DEBUG(2,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
210 DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
211 nt_time_string(mem_ctx, acct_expiry)));
212 return NT_STATUS_ACCOUNT_EXPIRED;
215 /* check for immediate expiry "must change at next logon" (but not if this is a password change request) */
216 if ((must_change_time == 0) && !password_change) {
217 DEBUG(2,("sam_account_ok: Account for user '%s' password must change!.\n",
218 name_for_logs));
219 return NT_STATUS_PASSWORD_MUST_CHANGE;
222 /* check for expired password (but not if this is a password change request) */
223 if ((must_change_time < now) && !password_change) {
224 DEBUG(2,("sam_account_ok: Account for user '%s' password expired!.\n",
225 name_for_logs));
226 DEBUG(2,("sam_account_ok: Password expired at '%s' unix time.\n",
227 nt_time_string(mem_ctx, must_change_time)));
228 return NT_STATUS_PASSWORD_EXPIRED;
231 /* Test workstation. Workstation list is comma separated. */
232 if (logon_workstation && workstation_list && *workstation_list) {
233 bool invalid_ws = true;
234 int i;
235 char **workstations = str_list_make(mem_ctx, workstation_list, ",");
237 for (i = 0; workstations && workstations[i]; i++) {
238 DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
239 workstations[i], logon_workstation));
241 if (strequal(workstations[i], logon_workstation)) {
242 invalid_ws = false;
243 break;
247 talloc_free(workstations);
249 if (invalid_ws) {
250 return NT_STATUS_INVALID_WORKSTATION;
254 if (!logon_hours_ok(msg, name_for_logs)) {
255 return NT_STATUS_INVALID_LOGON_HOURS;
258 if (!allow_domain_trust) {
259 if (acct_flags & ACB_DOMTRUST) {
260 DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
261 return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
264 if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
265 if (acct_flags & ACB_SVRTRUST) {
266 DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
267 return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
270 if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
271 /* TODO: this fails with current solaris client. We
272 need to work with Gordon to work out why */
273 if (acct_flags & ACB_WSTRUST) {
274 DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
275 return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
279 return NT_STATUS_OK;
282 _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
283 struct ldb_context *sam_ctx,
284 const char *netbios_name,
285 const char *domain_name,
286 struct ldb_dn *domain_dn,
287 struct ldb_message *msg,
288 DATA_BLOB user_sess_key,
289 DATA_BLOB lm_sess_key,
290 struct auth_user_info_dc **_user_info_dc)
292 NTSTATUS status;
293 struct auth_user_info_dc *user_info_dc;
294 struct auth_user_info *info;
295 const char *str, *filter;
296 /* SIDs for the account and his primary group */
297 struct dom_sid *account_sid;
298 const char *primary_group_string;
299 const char *primary_group_dn;
300 DATA_BLOB primary_group_blob;
301 /* SID structures for the expanded group memberships */
302 struct dom_sid *sids = NULL;
303 unsigned int num_sids = 0, i;
304 struct dom_sid *domain_sid;
305 TALLOC_CTX *tmp_ctx;
306 struct ldb_message_element *el;
308 user_info_dc = talloc(mem_ctx, struct auth_user_info_dc);
309 NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
311 tmp_ctx = talloc_new(user_info_dc);
312 if (user_info_dc == NULL) {
313 TALLOC_FREE(user_info_dc);
314 return NT_STATUS_NO_MEMORY;
317 sids = talloc_array(user_info_dc, struct dom_sid, 2);
318 if (sids == NULL) {
319 TALLOC_FREE(user_info_dc);
320 return NT_STATUS_NO_MEMORY;
323 num_sids = 2;
325 account_sid = samdb_result_dom_sid(user_info_dc, msg, "objectSid");
326 if (account_sid == NULL) {
327 TALLOC_FREE(user_info_dc);
328 return NT_STATUS_NO_MEMORY;
331 status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL);
332 if (!NT_STATUS_IS_OK(status)) {
333 talloc_free(user_info_dc);
334 return status;
337 sids[PRIMARY_USER_SID_INDEX] = *account_sid;
338 sids[PRIMARY_GROUP_SID_INDEX] = *domain_sid;
339 sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX], ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
341 /* Filter out builtin groups from this token. We will search
342 * for builtin groups later, and not include them in the PAC
343 * on SamLogon validation info */
344 filter = talloc_asprintf(tmp_ctx, "(&(objectClass=group)(!(groupType:1.2.840.113556.1.4.803:=%u))(groupType:1.2.840.113556.1.4.803:=%u))", GROUP_TYPE_BUILTIN_LOCAL_GROUP, GROUP_TYPE_SECURITY_ENABLED);
345 if (filter == NULL) {
346 TALLOC_FREE(user_info_dc);
347 return NT_STATUS_NO_MEMORY;
350 primary_group_string = dom_sid_string(tmp_ctx, &sids[PRIMARY_GROUP_SID_INDEX]);
351 if (primary_group_string == NULL) {
352 TALLOC_FREE(user_info_dc);
353 return NT_STATUS_NO_MEMORY;
356 primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string);
357 if (primary_group_dn == NULL) {
358 TALLOC_FREE(user_info_dc);
359 return NT_STATUS_NO_MEMORY;
362 primary_group_blob = data_blob_string_const(primary_group_dn);
364 /* Expands the primary group - this function takes in
365 * memberOf-like values, so we fake one up with the
366 * <SID=S-...> format of DN and then let it expand
367 * them, as long as they meet the filter - so only
368 * domain groups, not builtin groups
370 * The primary group is still treated specially, so we set the
371 * 'only childs' flag to true
373 status = dsdb_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter,
374 user_info_dc, &sids, &num_sids);
375 if (!NT_STATUS_IS_OK(status)) {
376 talloc_free(user_info_dc);
377 return status;
380 /* Expands the additional groups */
381 el = ldb_msg_find_element(msg, "memberOf");
382 for (i = 0; el && i < el->num_values; i++) {
383 /* This function takes in memberOf values and expands
384 * them, as long as they meet the filter - so only
385 * domain groups, not builtin groups */
386 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i], false, filter,
387 user_info_dc, &sids, &num_sids);
388 if (!NT_STATUS_IS_OK(status)) {
389 talloc_free(user_info_dc);
390 return status;
394 user_info_dc->sids = sids;
395 user_info_dc->num_sids = num_sids;
397 user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
398 NT_STATUS_HAVE_NO_MEMORY(user_info_dc->info);
400 info->account_name = talloc_steal(info,
401 ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL));
403 info->domain_name = talloc_strdup(info, domain_name);
404 if (info->domain_name == NULL) {
405 TALLOC_FREE(user_info_dc);
406 return NT_STATUS_NO_MEMORY;
409 str = ldb_msg_find_attr_as_string(msg, "displayName", "");
410 info->full_name = talloc_strdup(info, str);
411 if (info->full_name == NULL) {
412 TALLOC_FREE(user_info_dc);
413 return NT_STATUS_NO_MEMORY;
416 str = ldb_msg_find_attr_as_string(msg, "scriptPath", "");
417 info->logon_script = talloc_strdup(info, str);
418 if (info->logon_script == NULL) {
419 TALLOC_FREE(user_info_dc);
420 return NT_STATUS_NO_MEMORY;
423 str = ldb_msg_find_attr_as_string(msg, "profilePath", "");
424 info->profile_path = talloc_strdup(info, str);
425 if (info->profile_path == NULL) {
426 TALLOC_FREE(user_info_dc);
427 return NT_STATUS_NO_MEMORY;
430 str = ldb_msg_find_attr_as_string(msg, "homeDirectory", "");
431 info->home_directory = talloc_strdup(info, str);
432 if (info->home_directory == NULL) {
433 TALLOC_FREE(user_info_dc);
434 return NT_STATUS_NO_MEMORY;
437 str = ldb_msg_find_attr_as_string(msg, "homeDrive", "");
438 info->home_drive = talloc_strdup(info, str);
439 if (info->home_drive == NULL) {
440 TALLOC_FREE(user_info_dc);
441 return NT_STATUS_NO_MEMORY;
444 info->logon_server = talloc_strdup(info, netbios_name);
445 if (info->logon_server == NULL) {
446 TALLOC_FREE(user_info_dc);
447 return NT_STATUS_NO_MEMORY;
450 info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
451 info->last_logoff = samdb_result_last_logoff(msg);
452 info->acct_expiry = samdb_result_account_expires(msg);
453 info->last_password_change = samdb_result_nttime(msg,
454 "pwdLastSet", 0);
455 info->allow_password_change
456 = samdb_result_allow_password_change(sam_ctx, mem_ctx,
457 domain_dn, msg, "pwdLastSet");
458 info->force_password_change
459 = samdb_result_force_password_change(sam_ctx, mem_ctx,
460 domain_dn, msg);
461 info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
462 info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
465 info->acct_flags = samdb_result_acct_flags(msg, "msDS-User-Account-Control-Computed");
467 user_info_dc->user_session_key = data_blob_talloc(user_info_dc,
468 user_sess_key.data,
469 user_sess_key.length);
470 if (user_sess_key.data) {
471 if (user_info_dc->user_session_key.data == NULL) {
472 TALLOC_FREE(user_info_dc);
473 return NT_STATUS_NO_MEMORY;
476 user_info_dc->lm_session_key = data_blob_talloc(user_info_dc,
477 lm_sess_key.data,
478 lm_sess_key.length);
479 if (lm_sess_key.data) {
480 if (user_info_dc->lm_session_key.data == NULL) {
481 TALLOC_FREE(user_info_dc);
482 return NT_STATUS_NO_MEMORY;
486 if (info->acct_flags & ACB_SVRTRUST) {
487 /* the SID_NT_ENTERPRISE_DCS SID gets added into the
488 PAC */
489 user_info_dc->sids = talloc_realloc(user_info_dc,
490 user_info_dc->sids,
491 struct dom_sid,
492 user_info_dc->num_sids+1);
493 if (user_info_dc->sids == NULL) {
494 TALLOC_FREE(user_info_dc);
495 return NT_STATUS_NO_MEMORY;
497 user_info_dc->sids[user_info_dc->num_sids] = global_sid_Enterprise_DCs;
498 user_info_dc->num_sids++;
501 if ((info->acct_flags & (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) ==
502 (ACB_PARTIAL_SECRETS_ACCOUNT | ACB_WSTRUST)) {
503 /* the DOMAIN_RID_ENTERPRISE_READONLY_DCS PAC */
504 user_info_dc->sids = talloc_realloc(user_info_dc,
505 user_info_dc->sids,
506 struct dom_sid,
507 user_info_dc->num_sids+1);
508 if (user_info_dc->sids == NULL) {
509 TALLOC_FREE(user_info_dc);
510 return NT_STATUS_NO_MEMORY;
512 user_info_dc->sids[user_info_dc->num_sids] = *domain_sid;
513 sid_append_rid(&user_info_dc->sids[user_info_dc->num_sids],
514 DOMAIN_RID_ENTERPRISE_READONLY_DCS);
515 user_info_dc->num_sids++;
518 info->authenticated = true;
520 talloc_free(tmp_ctx);
521 *_user_info_dc = user_info_dc;
523 return NT_STATUS_OK;
526 NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
527 TALLOC_CTX *mem_ctx, const char *principal,
528 const char **attrs,
529 struct ldb_dn **domain_dn,
530 struct ldb_message **msg)
532 struct ldb_dn *user_dn;
533 NTSTATUS nt_status;
534 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
535 int ret;
537 if (!tmp_ctx) {
538 return NT_STATUS_NO_MEMORY;
541 nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal,
542 &user_dn, domain_dn);
543 if (!NT_STATUS_IS_OK(nt_status)) {
544 talloc_free(tmp_ctx);
545 return nt_status;
548 /* pull the user attributes */
549 ret = dsdb_search_one(sam_ctx, tmp_ctx, msg, user_dn,
550 LDB_SCOPE_BASE, attrs,
551 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
552 "(objectClass=*)");
553 if (ret != LDB_SUCCESS) {
554 talloc_free(tmp_ctx);
555 return NT_STATUS_INTERNAL_DB_CORRUPTION;
557 talloc_steal(mem_ctx, *msg);
558 talloc_steal(mem_ctx, *domain_dn);
559 talloc_free(tmp_ctx);
561 return NT_STATUS_OK;
564 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
566 Supply either a principal or a DN
568 NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
569 struct loadparm_context *lp_ctx,
570 struct ldb_context *sam_ctx,
571 const char *principal,
572 struct ldb_dn *user_dn,
573 struct auth_user_info_dc **user_info_dc)
575 NTSTATUS nt_status;
576 DATA_BLOB user_sess_key = data_blob(NULL, 0);
577 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
579 struct ldb_message *msg;
580 struct ldb_dn *domain_dn;
582 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
583 if (!tmp_ctx) {
584 return NT_STATUS_NO_MEMORY;
587 if (principal) {
588 nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
589 user_attrs, &domain_dn, &msg);
590 if (!NT_STATUS_IS_OK(nt_status)) {
591 talloc_free(tmp_ctx);
592 return nt_status;
594 } else if (user_dn) {
595 struct dom_sid *user_sid, *domain_sid;
596 int ret;
597 /* pull the user attributes */
598 ret = dsdb_search_one(sam_ctx, tmp_ctx, &msg, user_dn,
599 LDB_SCOPE_BASE, user_attrs,
600 DSDB_SEARCH_SHOW_EXTENDED_DN | DSDB_SEARCH_NO_GLOBAL_CATALOG,
601 "(objectClass=*)");
602 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
603 talloc_free(tmp_ctx);
604 return NT_STATUS_NO_SUCH_USER;
605 } else if (ret != LDB_SUCCESS) {
606 talloc_free(tmp_ctx);
607 return NT_STATUS_INTERNAL_DB_CORRUPTION;
610 user_sid = samdb_result_dom_sid(msg, msg, "objectSid");
612 nt_status = dom_sid_split_rid(tmp_ctx, user_sid, &domain_sid, NULL);
613 if (!NT_STATUS_IS_OK(nt_status)) {
614 return nt_status;
617 domain_dn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
618 "(&(objectSid=%s)(objectClass=domain))",
619 ldap_encode_ndr_dom_sid(tmp_ctx, domain_sid));
620 if (!domain_dn) {
621 DEBUG(3, ("authsam_get_user_info_dc_principal: Failed to find domain with: SID %s\n",
622 dom_sid_string(tmp_ctx, domain_sid)));
623 return NT_STATUS_NO_SUCH_USER;
626 } else {
627 return NT_STATUS_INVALID_PARAMETER;
630 nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
631 lpcfg_netbios_name(lp_ctx),
632 lpcfg_workgroup(lp_ctx),
633 domain_dn,
634 msg,
635 user_sess_key, lm_sess_key,
636 user_info_dc);
637 if (!NT_STATUS_IS_OK(nt_status)) {
638 talloc_free(tmp_ctx);
639 return nt_status;
642 talloc_steal(mem_ctx, *user_info_dc);
643 talloc_free(tmp_ctx);
645 return NT_STATUS_OK;
648 NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
649 struct ldb_message *msg,
650 struct ldb_dn *domain_dn)
652 const char *attrs[] = { "lockoutThreshold",
653 "lockOutObservationWindow",
654 "lockoutDuration",
655 "pwdProperties",
656 NULL };
657 int ret;
658 NTSTATUS status;
659 struct ldb_result *domain_res;
660 struct ldb_message *msg_mod = NULL;
661 TALLOC_CTX *mem_ctx;
663 mem_ctx = talloc_new(msg);
664 if (mem_ctx == NULL) {
665 return NT_STATUS_NO_MEMORY;
668 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
669 if (ret != LDB_SUCCESS) {
670 TALLOC_FREE(mem_ctx);
671 return NT_STATUS_INTERNAL_DB_CORRUPTION;
674 status = dsdb_update_bad_pwd_count(mem_ctx, sam_ctx,
675 msg, domain_res->msgs[0], &msg_mod);
676 if (!NT_STATUS_IS_OK(status)) {
677 TALLOC_FREE(mem_ctx);
678 return status;
681 if (msg_mod != NULL) {
682 ret = dsdb_modify(sam_ctx, msg_mod, 0);
683 if (ret != LDB_SUCCESS) {
684 DEBUG(0, ("Failed to update badPwdCount, badPasswordTime or set lockoutTime on %s: %s\n",
685 ldb_dn_get_linearized(msg_mod->dn), ldb_errstring(sam_ctx)));
686 TALLOC_FREE(mem_ctx);
687 return NT_STATUS_INTERNAL_ERROR;
691 TALLOC_FREE(mem_ctx);
692 return NT_STATUS_OK;
696 static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
697 struct ldb_message *msg_mod,
698 struct ldb_dn *domain_dn,
699 NTTIME old_timestamp,
700 NTTIME now)
703 * We only set lastLogonTimestamp if the current value is older than
704 * now - msDS-LogonTimeSyncInterval days.
706 * msDS-LogonTimeSyncInterval is an int32_t number of days, while
707 * lastLogonTimestamp is in the 64 bit 100ns NTTIME format.
709 * The docs say: "the initial update, after the domain functional
710 * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
711 * 14 days minus a random percentage of 5 days", but we aren't doing
712 * that. The blogosphere seems to think that this randomised update
713 * happens everytime, but [MS-ADA1] doesn't agree.
715 * Dochelp referred us to the following blog post:
716 * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
718 * en msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
719 * not changed.
721 static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
722 NULL };
723 int ret;
724 struct ldb_result *domain_res = NULL;
725 TALLOC_CTX *mem_ctx = NULL;
726 int32_t sync_interval;
727 NTTIME sync_interval_nt;
729 mem_ctx = talloc_new(msg_mod);
730 if (mem_ctx == NULL) {
731 return NT_STATUS_NO_MEMORY;
734 ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
736 if (ret != LDB_SUCCESS || domain_res->count != 1) {
737 TALLOC_FREE(mem_ctx);
738 return NT_STATUS_INTERNAL_DB_CORRUPTION;
741 sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
742 "msDS-LogonTimeSyncInterval",
743 14);
744 DEBUG(5, ("sync interval is %d\n", sync_interval));
745 if (sync_interval == 0){
747 * Setting msDS-LogonTimeSyncInterval to zero is how you ask
748 * that nothing happens here.
750 TALLOC_FREE(mem_ctx);
751 return NT_STATUS_OK;
753 else if (sync_interval >= 5){
755 * Subtract "a random percentage of 5" days. Presumably this
756 * percentage is between 0 and 100, and modulus is accurate
757 * enough.
759 uint32_t r = generate_random() % 6;
760 sync_interval -= r;
761 DEBUG(5, ("randomised sync interval is %d (-%d)\n", sync_interval, r));
763 /* In the case where sync_interval < 5 there is no randomisation */
765 sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
767 DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
768 (long long int)old_timestamp,
769 (long long int)(now - sync_interval_nt),
770 (long long int)(old_timestamp - now + sync_interval_nt)));
772 if (old_timestamp > now){
773 DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
774 (long long int)old_timestamp, (long long int)now));
775 /* then what? */
777 } else if (old_timestamp < now - sync_interval_nt){
778 DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
779 (long long int)now));
781 /* The time has come to update lastLogonTimestamp */
782 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
783 "lastLogonTimestamp", now);
785 if (ret != LDB_SUCCESS) {
786 TALLOC_FREE(mem_ctx);
787 return NT_STATUS_NO_MEMORY;
790 TALLOC_FREE(mem_ctx);
791 return NT_STATUS_OK;
796 /* Reset the badPwdCount to zero and update the lastLogon time. */
797 NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
798 const struct ldb_message *msg,
799 struct ldb_dn *domain_dn,
800 bool interactive_or_kerberos)
802 int ret;
803 NTSTATUS status;
804 int badPwdCount;
805 int64_t lockoutTime;
806 struct ldb_message *msg_mod;
807 TALLOC_CTX *mem_ctx;
808 struct timeval tv_now;
809 NTTIME now;
810 NTTIME lastLogonTimestamp;
811 NTTIME lastLogon;
813 lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
814 badPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
815 lastLogonTimestamp = \
816 ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
817 lastLogon = ldb_msg_find_attr_as_int64(msg, "lastLogon", 0);
819 DEBUG(5, ("lastLogonTimestamp is %lld\n",
820 (long long int)lastLogonTimestamp));
822 mem_ctx = talloc_new(msg);
823 if (mem_ctx == NULL) {
824 return NT_STATUS_NO_MEMORY;
826 msg_mod = ldb_msg_new(mem_ctx);
827 if (msg_mod == NULL) {
828 TALLOC_FREE(mem_ctx);
829 return NT_STATUS_NO_MEMORY;
831 msg_mod->dn = msg->dn;
833 if (lockoutTime != 0) {
835 * This implies "badPwdCount" = 0, see samldb_lockout_time()
837 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "lockoutTime", 0);
838 if (ret != LDB_SUCCESS) {
839 TALLOC_FREE(mem_ctx);
840 return NT_STATUS_NO_MEMORY;
842 } else if (badPwdCount != 0) {
843 ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
844 if (ret != LDB_SUCCESS) {
845 TALLOC_FREE(mem_ctx);
846 return NT_STATUS_NO_MEMORY;
850 tv_now = timeval_current();
851 now = timeval_to_nttime(&tv_now);
853 if (interactive_or_kerberos || lastLogon == 0 ||
854 (badPwdCount != 0 && lockoutTime == 0)) {
855 ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
856 "lastLogon", now);
857 if (ret != LDB_SUCCESS) {
858 TALLOC_FREE(mem_ctx);
859 return NT_STATUS_NO_MEMORY;
862 status = authsam_update_lastlogon_timestamp(sam_ctx, msg_mod, domain_dn,
863 lastLogonTimestamp, now);
864 if (!NT_STATUS_IS_OK(status)) {
865 TALLOC_FREE(mem_ctx);
866 return NT_STATUS_NO_MEMORY;
869 if (msg_mod->num_elements > 0) {
870 ret = dsdb_replace(sam_ctx, msg_mod, 0);
871 if (ret != LDB_SUCCESS) {
872 DEBUG(0, ("Failed to set badPwdCount and lockoutTime "
873 "to 0 and/or lastlogon to now (%lld) "
874 "%s: %s\n", (long long int)now,
875 ldb_dn_get_linearized(msg_mod->dn),
876 ldb_errstring(sam_ctx)));
877 TALLOC_FREE(mem_ctx);
878 return NT_STATUS_INTERNAL_ERROR;
881 TALLOC_FREE(mem_ctx);
882 return NT_STATUS_OK;