#include "dsdb/common/util.h"
#include "libcli/ldap/ldap_ndr.h"
#include "param/param.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
-#define KRBTGT_ATTRS \
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+#define KRBTGT_ATTRS \
/* required for the krb5 kdc */ \
"objectClass", \
"sAMAccountName", \
};
const char *user_attrs[] = {
+ /*
+ * This ordering (having msDS-ResultantPSO first) is
+ * important. By processing this attribute first it is
+ * available in the operational module for the other PSO
+ * attribute calcuations to use.
+ */
+ "msDS-ResultantPSO",
+
KRBTGT_ATTRS,
"logonHours",
*/
"lockoutTime",
+ /*
+ * Needed for SendToSAM requests
+ */
+ "objectGUID",
+
/* check 'allowed workstations' */
"userWorkstations",
-
+
/* required for user_info_dc, not access control: */
"displayName",
"scriptPath",
return NT_STATUS_OK;
}
+static NTSTATUS authsam_domain_group_filter(TALLOC_CTX *mem_ctx,
+ char **_filter)
+{
+ char *filter = NULL;
+
+ *_filter = NULL;
+
+ filter = talloc_strdup(mem_ctx, "(&(objectClass=group)");
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * Skip all builtin groups, they're added later.
+ */
+ filter = talloc_asprintf_append_buffer(filter,
+ "(!(groupType:1.2.840.113556.1.4.803:=%u))",
+ GROUP_TYPE_BUILTIN_LOCAL_GROUP);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ /*
+ * Only include security groups.
+ */
+ filter = talloc_asprintf_append_buffer(filter,
+ "(groupType:1.2.840.113556.1.4.803:=%u))",
+ GROUP_TYPE_SECURITY_ENABLED);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *_filter = filter;
+ return NT_STATUS_OK;
+}
+
_PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
struct ldb_context *sam_ctx,
const char *netbios_name,
NTSTATUS status;
struct auth_user_info_dc *user_info_dc;
struct auth_user_info *info;
- const char *str, *filter;
+ const char *str = NULL;
+ char *filter = NULL;
/* SIDs for the account and his primary group */
struct dom_sid *account_sid;
const char *primary_group_string;
sids[PRIMARY_GROUP_SID_INDEX] = *domain_sid;
sid_append_rid(&sids[PRIMARY_GROUP_SID_INDEX], ldb_msg_find_attr_as_uint(msg, "primaryGroupID", ~0));
- /* Filter out builtin groups from this token. We will search
+ /*
+ * Filter out builtin groups from this token. We will search
* for builtin groups later, and not include them in the PAC
- * on SamLogon validation info */
- 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);
- if (filter == NULL) {
+ * or SamLogon validation info.
+ */
+ status = authsam_domain_group_filter(tmp_ctx, &filter);
+ if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(user_info_dc);
- return NT_STATUS_NO_MEMORY;
+ return status;
}
primary_group_string = dom_sid_string(tmp_ctx, &sids[PRIMARY_GROUP_SID_INDEX]);
return NT_STATUS_OK;
}
+_PUBLIC_ NTSTATUS authsam_update_user_info_dc(TALLOC_CTX *mem_ctx,
+ struct ldb_context *sam_ctx,
+ struct auth_user_info_dc *user_info_dc)
+{
+ char *filter = NULL;
+ NTSTATUS status;
+ uint32_t i;
+ uint32_t n = 0;
+
+ /*
+ * This function exists to expand group memberships
+ * in the local domain (forest), as the token
+ * may come from a different domain.
+ */
+
+ /*
+ * Filter out builtin groups from this token. We will search
+ * for builtin groups later.
+ */
+ status = authsam_domain_group_filter(mem_ctx, &filter);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(user_info_dc);
+ return status;
+ }
+
+ /*
+ * We loop only over the existing number of
+ * sids.
+ */
+ n = user_info_dc->num_sids;
+ for (i = 0; i < n; i++) {
+ struct dom_sid *sid = &user_info_dc->sids[i];
+ char sid_buf[DOM_SID_STR_BUFLEN] = {0,};
+ char dn_str[DOM_SID_STR_BUFLEN*2] = {0,};
+ DATA_BLOB dn_blob = data_blob_null;
+ int len;
+
+ len = dom_sid_string_buf(sid, sid_buf, sizeof(sid_buf));
+ if (len+1 > sizeof(sid_buf)) {
+ return NT_STATUS_INVALID_SID;
+ }
+ snprintf(dn_str, sizeof(dn_str), "<SID=%s>", sid_buf);
+ dn_blob = data_blob_string_const(dn_str);
+
+ /*
+ * We already have the SID in the token, so set
+ * 'only childs' flag to true and add all
+ * groups which match the filter.
+ */
+ status = dsdb_expand_nested_groups(sam_ctx, &dn_blob,
+ true, filter,
+ user_info_dc,
+ &user_info_dc->sids,
+ &user_info_dc->num_sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
TALLOC_CTX *mem_ctx, const char *principal,
const char **attrs,
return NT_STATUS_OK;
}
+/*
+ * Returns the details for the Password Settings Object (PSO), if one applies
+ * the user.
+ */
+static int authsam_get_user_pso(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message *user_msg,
+ struct ldb_message **pso_msg)
+{
+ const char *attrs[] = { "msDS-LockoutThreshold",
+ "msDS-LockoutObservationWindow",
+ NULL };
+ struct ldb_dn *pso_dn = NULL;
+ struct ldb_result *res = NULL;
+ int ret;
+
+ /* check if the user has a PSO that applies to it */
+ pso_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, user_msg,
+ "msDS-ResultantPSO");
+
+ if (pso_dn != NULL) {
+ ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, pso_dn, attrs, 0);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ *pso_msg = res->msgs[0];
+ }
+
+ return LDB_SUCCESS;
+}
+
NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
struct ldb_message *msg,
struct ldb_dn *domain_dn)
NTSTATUS status;
struct ldb_result *domain_res;
struct ldb_message *msg_mod = NULL;
+ struct ldb_message *pso_msg = NULL;
TALLOC_CTX *mem_ctx;
mem_ctx = talloc_new(msg);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
+ ret = authsam_get_user_pso(sam_ctx, mem_ctx, msg, &pso_msg);
+ if (ret != LDB_SUCCESS) {
+
+ /*
+ * fallback to using the domain defaults so that we still
+ * record the bad password attempt
+ */
+ DBG_ERR("Error (%d) checking PSO for %s",
+ ret, ldb_dn_get_linearized(msg->dn));
+ }
+
status = dsdb_update_bad_pwd_count(mem_ctx, sam_ctx,
- msg, domain_res->msgs[0], &msg_mod);
+ msg, domain_res->msgs[0], pso_msg,
+ &msg_mod);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(mem_ctx);
return status;
}
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;
}
+/****************************************************************************
+ 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)
+ bool interactive_or_kerberos,
+ struct netr_SendToSamBase **send_to_sam)
{
int ret;
NTSTATUS status;
int badPwdCount;
+ int dbBadPwdCount;
int64_t lockoutTime;
struct ldb_message *msg_mod;
TALLOC_CTX *mem_ctx;
}
lockoutTime = ldb_msg_find_attr_as_int64(msg, "lockoutTime", 0);
+ dbBadPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
if (interactive_or_kerberos) {
- badPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
+ badPwdCount = dbBadPwdCount;
} else {
badPwdCount = samdb_result_effective_badPwdCount(sam_ctx, mem_ctx,
domain_dn, msg);
}
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;
}
+ } else {
+ /* Perform the (async) SendToSAM calls for MS-SAMS */
+ if (dbBadPwdCount != 0 && send_to_sam != NULL) {
+ struct netr_SendToSamBase *base_msg;
+ struct GUID guid = samdb_result_guid(msg, "objectGUID");
+ base_msg = talloc_zero(msg, struct netr_SendToSamBase);
+
+ base_msg->message_type = SendToSamResetBadPasswordCount;
+ base_msg->message_size = 16;
+ base_msg->message.reset_bad_password.guid = guid;
+ *send_to_sam = base_msg;
+ }
}
if (msg_mod->num_elements > 0) {
- ret = dsdb_replace(sam_ctx, msg_mod, 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) {
- 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;
+ 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;
+ }
+
TALLOC_FREE(mem_ctx);
return NT_STATUS_OK;
}