X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Fauth%2Fsam.c;h=07cfbd06b3363659504580ce7518062e63f7e383;hb=2fa2f132ae3de9a403b2d93d586570f59250de23;hp=56b64e50093b7d85ad1f4b14ba7f16e93d948591;hpb=432e83bf5bebd9f4fadb98fcadb82a32eb1b88ed;p=samba.git diff --git a/source4/auth/sam.c b/source4/auth/sam.c index 56b64e50093..07cfbd06b33 100644 --- a/source4/auth/sam.c +++ b/source4/auth/sam.c @@ -30,8 +30,12 @@ #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", \ @@ -64,6 +68,14 @@ const char *server_attrs[] = { }; 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", @@ -74,9 +86,14 @@ const char *user_attrs[] = { */ "lockoutTime", + /* + * Needed for SendToSAM requests + */ + "objectGUID", + /* check 'allowed workstations' */ "userWorkstations", - + /* required for user_info_dc, not access control: */ "displayName", "scriptPath", @@ -280,10 +297,46 @@ _PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx, 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, const char *domain_name, + const char *dns_domain_name, struct ldb_dn *domain_dn, struct ldb_message *msg, DATA_BLOB user_sess_key, @@ -293,7 +346,8 @@ _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx, 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; @@ -339,13 +393,15 @@ _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx, 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]); @@ -401,12 +457,33 @@ _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx, 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) { @@ -523,6 +600,68 @@ _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx, 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_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, @@ -630,6 +769,7 @@ NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx, nt_status = authsam_make_user_info_dc(tmp_ctx, sam_ctx, lpcfg_netbios_name(lp_ctx), lpcfg_sam_name(lp_ctx), + lpcfg_sam_dnsname(lp_ctx), domain_dn, msg, user_sess_key, lm_sess_key, @@ -645,6 +785,38 @@ NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx, 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) @@ -658,6 +830,7 @@ NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, 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); @@ -671,21 +844,56 @@ NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, 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); @@ -791,23 +999,54 @@ static NTSTATUS authsam_update_lastlogon_timestamp(struct ldb_context *sam_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; struct timeval tv_now; NTTIME now; NTTIME lastLogonTimestamp; + bool am_rodc = false; mem_ctx = talloc_new(msg); if (mem_ctx == NULL) { @@ -815,8 +1054,9 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_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); @@ -877,27 +1117,87 @@ NTSTATUS authsam_logon_success_accounting(struct ldb_context *sam_ctx, 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; + } + } } - status = authsam_update_lastlogon_timestamp(sam_ctx, msg_mod, domain_dn, - lastLogonTimestamp, now); - if (!NT_STATUS_IS_OK(status)) { + ret = samdb_rodc(sam_ctx, &am_rodc); + if (ret != LDB_SUCCESS) { TALLOC_FREE(mem_ctx); - return NT_STATUS_NO_MEMORY; + return NT_STATUS_INTERNAL_ERROR; + } + + if (!am_rodc) { + 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;inum_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; }