"objectSid", \
\
"pwdLastSet", \
+ "msDS-UserPasswordExpiryTimeComputed", \
"accountExpires"
const char *krbtgt_attrs[] = {
"homeDirectory",
"homeDrive",
"lastLogon",
+ "lastLogonTimestamp",
"lastLogoff",
"accountExpires",
"badPwdCount",
/* Check for when we must change this password, taking the
* userAccountControl flags into account */
- must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx,
- domain_dn, msg);
+ must_change_time = samdb_result_nttime(msg,
+ "msDS-UserPasswordExpiryTimeComputed", 0);
workstation_list = ldb_msg_find_attr_as_string(msg, "userWorkstations", NULL);
struct ldb_context *sam_ctx,
const char *netbios_name,
const char *domain_name,
+ const char *dns_domain_name,
struct ldb_dn *domain_dn,
struct ldb_message *msg,
DATA_BLOB user_sess_key,
info->account_name = talloc_steal(info,
ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL));
+ info->user_principal_name = talloc_steal(info,
+ ldb_msg_find_attr_as_string(msg, "userPrincipalName", NULL));
+ if (info->user_principal_name == NULL && dns_domain_name != NULL) {
+ info->user_principal_name = talloc_asprintf(info, "%s@%s",
+ info->account_name,
+ dns_domain_name);
+ if (info->user_principal_name == NULL) {
+ TALLOC_FREE(user_info_dc);
+ return NT_STATUS_NO_MEMORY;
+ }
+ info->user_principal_constructed = true;
+ }
+
info->domain_name = talloc_strdup(info, domain_name);
if (info->domain_name == NULL) {
TALLOC_FREE(user_info_dc);
return NT_STATUS_NO_MEMORY;
}
+ if (dns_domain_name != NULL) {
+ info->dns_domain_name = talloc_strdup(info, dns_domain_name);
+ if (info->dns_domain_name == NULL) {
+ TALLOC_FREE(user_info_dc);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
str = ldb_msg_find_attr_as_string(msg, "displayName", "");
info->full_name = talloc_strdup(info, str);
if (info->full_name == NULL) {
info->allow_password_change
= samdb_result_allow_password_change(sam_ctx, mem_ctx,
domain_dn, msg, "pwdLastSet");
- info->force_password_change
- = samdb_result_force_password_change(sam_ctx, mem_ctx,
- domain_dn, msg);
+ info->force_password_change = samdb_result_nttime(msg,
+ "msDS-UserPasswordExpiryTimeComputed", 0);
info->logon_count = ldb_msg_find_attr_as_uint(msg, "logonCount", 0);
info->bad_password_count = ldb_msg_find_attr_as_uint(msg, "badPwdCount",
0);
return NT_STATUS_OK;
}
-NTSTATUS sam_get_results_trust(struct ldb_context *sam_ctx,
- TALLOC_CTX *mem_ctx, const char *domain,
- const char *realm, const char * const *attrs,
- struct ldb_message **msg)
-{
- TALLOC_CTX *frame = talloc_stackframe();
- int lret;
- struct ldb_dn *system_dn;
- char *filter;
- struct ldb_result *res = NULL;
- char *domain_encoded;
-
- system_dn = ldb_dn_copy(frame, ldb_get_default_basedn(sam_ctx));
- if (system_dn == NULL) {
- TALLOC_FREE(frame);
- return NT_STATUS_NO_MEMORY;
- }
-
- if (!ldb_dn_add_child_fmt(system_dn, "CN=System")) {
- TALLOC_FREE(frame);
- return NT_STATUS_NO_MEMORY;
- }
-
- domain_encoded = ldb_binary_encode_string(mem_ctx, domain);
- if (!domain_encoded) {
- TALLOC_FREE(frame);
- return NT_STATUS_NO_MEMORY;
- }
- if (realm == NULL) {
- filter = talloc_asprintf(mem_ctx,
- "(&(objectClass=trustedDomain)(flatname=%s))",
- domain_encoded);
- if (!filter) {
- TALLOC_FREE(frame);
- return NT_STATUS_NO_MEMORY;
- }
- } else {
- char *realm_encoded = ldb_binary_encode_string(mem_ctx, realm);
- if (!realm_encoded) {
- TALLOC_FREE(frame);
- return NT_STATUS_NO_MEMORY;
- }
-
- filter = talloc_asprintf(mem_ctx,
- "(&(objectClass=trustedDomain)"
- "(|(trustPartner=%s)(flatname=%s))"
- ")",
- realm_encoded, domain_encoded);
- if (!filter) {
- TALLOC_FREE(frame);
- return NT_STATUS_NO_MEMORY;
- }
- }
-
- lret = dsdb_search(sam_ctx, frame, &res,
- system_dn,
- LDB_SCOPE_ONELEVEL, attrs,
- DSDB_SEARCH_NO_GLOBAL_CATALOG|DSDB_SEARCH_ONE_ONLY,
- "%s", filter);
- if (lret == LDB_ERR_NO_SUCH_OBJECT) {
- DEBUG(3, ("Failed to find result for %s: %s\n", filter, ldb_errstring(sam_ctx)));
- TALLOC_FREE(frame);
- return NT_STATUS_NOT_FOUND;
- } else if (lret != LDB_SUCCESS) {
- DEBUG(3, ("Failed to search for %s: %s\n", filter, ldb_errstring(sam_ctx)));
- TALLOC_FREE(frame);
- return NT_STATUS_INTERNAL_DB_CORRUPTION;
- }
- talloc_steal(mem_ctx, res->msgs);
- *msg = res->msgs[0];
- TALLOC_FREE(frame);
- return NT_STATUS_OK;
-}
-
/* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
Supply either a principal or a DN
nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx,
lpcfg_netbios_name(lp_ctx),
- lpcfg_workgroup(lp_ctx),
+ lpcfg_sam_name(lp_ctx),
+ lpcfg_sam_dnsname(lp_ctx),
domain_dn,
msg,
user_sess_key, lm_sess_key,
}
if (msg_mod != NULL) {
- ret = dsdb_modify(sam_ctx, msg_mod, 0);
+ struct ldb_request *req;
+
+ ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
+ msg_mod,
+ NULL,
+ NULL,
+ ldb_op_default_callback,
+ NULL);
if (ret != LDB_SUCCESS) {
- DEBUG(0, ("Failed to update badPwdCount, badPasswordTime or set lockoutTime on %s: %s\n",
- ldb_dn_get_linearized(msg_mod->dn), ldb_errstring(sam_ctx)));
- TALLOC_FREE(mem_ctx);
- return NT_STATUS_INTERNAL_ERROR;
+ goto done;
}
+
+ ret = ldb_request_add_control(req,
+ DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
+ false, NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(req);
+ goto done;
+ }
+
+ ret = dsdb_autotransaction_request(sam_ctx, req);
+ talloc_free(req);
}
+done:
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Failed to update badPwdCount, badPasswordTime or set lockoutTime on %s: %s\n",
+ ldb_dn_get_linearized(msg_mod->dn), ldb_errstring(sam_ctx)));
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_ctx,
+ struct ldb_message *msg_mod,
+ struct ldb_dn *domain_dn,
+ NTTIME old_timestamp,
+ NTTIME now)
+{
+ /*
+ * We only set lastLogonTimestamp if the current value is older than
+ * now - msDS-LogonTimeSyncInterval days.
+ *
+ * msDS-LogonTimeSyncInterval is an int32_t number of days, while
+ * lastLogonTimestamp is in the 64 bit 100ns NTTIME format.
+ *
+ * The docs say: "the initial update, after the domain functional
+ * level is raised to DS_BEHAVIOR_WIN2003 or higher, is calculated as
+ * 14 days minus a random percentage of 5 days", but we aren't doing
+ * that. The blogosphere seems to think that this randomised update
+ * happens everytime, but [MS-ADA1] doesn't agree.
+ *
+ * Dochelp referred us to the following blog post:
+ * http://blogs.technet.com/b/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx
+ *
+ * en msDS-LogonTimeSyncInterval is zero, the lastLogonTimestamp is
+ * not changed.
+ */
+ static const char *attrs[] = { "msDS-LogonTimeSyncInterval",
+ NULL };
+ int ret;
+ struct ldb_result *domain_res = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ int32_t sync_interval;
+ NTTIME sync_interval_nt;
+
+ mem_ctx = talloc_new(msg_mod);
+ if (mem_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs,
+ 0);
+ if (ret != LDB_SUCCESS || domain_res->count != 1) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ sync_interval = ldb_msg_find_attr_as_int(domain_res->msgs[0],
+ "msDS-LogonTimeSyncInterval",
+ 14);
+ DEBUG(5, ("sync interval is %d\n", sync_interval));
+ if (sync_interval == 0){
+ /*
+ * Setting msDS-LogonTimeSyncInterval to zero is how you ask
+ * that nothing happens here.
+ */
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_OK;
+ }
+ else if (sync_interval >= 5){
+ /*
+ * Subtract "a random percentage of 5" days. Presumably this
+ * percentage is between 0 and 100, and modulus is accurate
+ * enough.
+ */
+ uint32_t r = generate_random() % 6;
+ sync_interval -= r;
+ DEBUG(5, ("randomised sync interval is %d (-%d)\n", sync_interval, r));
+ }
+ /* In the case where sync_interval < 5 there is no randomisation */
+
+ sync_interval_nt = sync_interval * 24LL * 3600LL * 10000000LL;
+
+ DEBUG(5, ("old timestamp is %lld, threshold %lld, diff %lld\n",
+ (long long int)old_timestamp,
+ (long long int)(now - sync_interval_nt),
+ (long long int)(old_timestamp - now + sync_interval_nt)));
+
+ if (old_timestamp > now){
+ DEBUG(0, ("lastLogonTimestamp is in the future! (%lld > %lld)\n",
+ (long long int)old_timestamp, (long long int)now));
+ /* then what? */
+
+ } else if (old_timestamp < now - sync_interval_nt){
+ DEBUG(5, ("updating lastLogonTimestamp to %lld\n",
+ (long long int)now));
+
+ /* The time has come to update lastLogonTimestamp */
+ ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
+ "lastLogonTimestamp", now);
+
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
TALLOC_FREE(mem_ctx);
return NT_STATUS_OK;
}
-NTSTATUS authsam_zero_bad_pwd_count(struct ldb_context *sam_ctx,
- const struct ldb_message *msg)
+/****************************************************************************
+ Look for the specified user in the sam, return ldb result structures
+****************************************************************************/
+
+NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
+ const char *account_name,
+ struct ldb_dn *domain_dn,
+ struct ldb_message **ret_msg)
{
int ret;
+
+ /* pull the user attributes */
+ ret = dsdb_search_one(sam_ctx, mem_ctx, ret_msg, domain_dn, LDB_SCOPE_SUBTREE,
+ user_attrs,
+ DSDB_SEARCH_SHOW_EXTENDED_DN,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ ldb_binary_encode_string(mem_ctx, account_name));
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(3,("sam_search_user: Couldn't find user [%s] in samdb, under %s\n",
+ account_name, ldb_dn_get_linearized(domain_dn)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/* Reset the badPwdCount to zero and update the lastLogon time. */
+NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx,
+ const struct ldb_message *msg,
+ struct ldb_dn *domain_dn,
+ bool interactive_or_kerberos)
+{
+ int ret;
+ NTSTATUS status;
int badPwdCount;
int64_t lockoutTime;
struct ldb_message *msg_mod;
TALLOC_CTX *mem_ctx;
-
- lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
- badPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
- if (lockoutTime == 0 && badPwdCount == 0) {
- return NT_STATUS_OK;
- }
+ struct timeval tv_now;
+ NTTIME now;
+ NTTIME lastLogonTimestamp;
+ bool am_rodc = false;
mem_ctx = talloc_new(msg);
if (mem_ctx == NULL) {
return NT_STATUS_NO_MEMORY;
}
+
+ lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
+ if (interactive_or_kerberos) {
+ badPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
+ } else {
+ badPwdCount = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx,
+ domain_dn, msg);
+ }
+ lastLogonTimestamp =
+ ldb_msg_find_attr_as_int64(msg, "lastLogonTimestamp", 0);
+
+ DEBUG(5, ("lastLogonTimestamp is %lld\n",
+ (long long int)lastLogonTimestamp));
+
msg_mod = ldb_msg_new(mem_ctx);
if (msg_mod == NULL) {
TALLOC_FREE(mem_ctx);
TALLOC_FREE(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
- } else {
+ } else if (badPwdCount != 0) {
ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", 0);
if (ret != LDB_SUCCESS) {
TALLOC_FREE(mem_ctx);
}
}
- ret = dsdb_replace(sam_ctx, msg_mod, 0);
+ tv_now = timeval_current();
+ now = timeval_to_nttime(&tv_now);
+
+ if (interactive_or_kerberos ||
+ (badPwdCount != 0 && lockoutTime == 0)) {
+ ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod,
+ "lastLogon", now);
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (interactive_or_kerberos) {
+ int logonCount;
+
+ logonCount = ldb_msg_find_attr_as_int(msg, "logonCount", 0);
+
+ logonCount += 1;
+
+ ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
+ "logonCount", logonCount);
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ /* Set an unset logonCount to 0 on first successful login */
+ if (ldb_msg_find_ldb_val(msg, "logonCount") == NULL) {
+ ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod,
+ "logonCount", 0);
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+
+ ret = samdb_rodc(sam_ctx, &am_rodc);
if (ret != LDB_SUCCESS) {
- DEBUG(0, ("Failed to set badPwdCount and lockoutTime to 0 on %s: %s\n",
- ldb_dn_get_linearized(msg_mod->dn), ldb_errstring(sam_ctx)));
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!am_rodc) {
+ /* TODO Perform the (async) SendToSAM calls for MS-SAMS */
+ status = authsam_update_lastlogon_timestamp(sam_ctx, msg_mod, domain_dn,
+ lastLogonTimestamp, now);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (msg_mod->num_elements > 0) {
+ unsigned int i;
+ struct ldb_request *req;
+
+ /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
+ for (i=0;i<msg_mod->num_elements;i++) {
+ msg_mod->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = ldb_build_mod_req(&req, sam_ctx, sam_ctx,
+ msg_mod,
+ NULL,
+ NULL,
+ ldb_op_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ goto done;
+ }
+
+ ret = ldb_request_add_control(req,
+ DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE,
+ false, NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(req);
+ goto done;
+ }
+
+ ret = dsdb_autotransaction_request(sam_ctx, req);
+ talloc_free(req);
+ }
+
+done:
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Failed to set badPwdCount and lockoutTime "
+ "to 0 and/or lastlogon to now (%lld) "
+ "%s: %s\n", (long long int)now,
+ ldb_dn_get_linearized(msg_mod->dn),
+ ldb_errstring(sam_ctx)));
TALLOC_FREE(mem_ctx);
return NT_STATUS_INTERNAL_ERROR;
}