diff options
Diffstat (limited to 'source4')
-rw-r--r-- | source4/auth/auth.h | 8 | ||||
-rw-r--r-- | source4/auth/sam.c | 182 | ||||
-rw-r--r-- | source4/auth/session.c | 147 | ||||
-rw-r--r-- | source4/dsdb/samdb/samdb.c | 11 |
4 files changed, 264 insertions, 84 deletions
diff --git a/source4/auth/auth.h b/source4/auth/auth.h index 915d10397b..3984704928 100644 --- a/source4/auth/auth.h +++ b/source4/auth/auth.h @@ -244,13 +244,11 @@ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx, const char *name_for_logs, bool allow_domain_trust, bool password_change); -struct auth_session_info *system_session(struct loadparm_context *lp_ctx); NTSTATUS authsam_expand_nested_groups(struct ldb_context *sam_ctx, - const struct dom_sid *sid, - const bool only_childs, - TALLOC_CTX *res_sids_ctx, - struct dom_sid ***res_sids, + struct ldb_val *dn_val, const bool only_childs, const char *filter, + TALLOC_CTX *res_sids_ctx, struct dom_sid ***res_sids, unsigned int *num_res_sids); +struct auth_session_info *system_session(struct loadparm_context *lp_ctx); NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx, const char *netbios_name, const char *domain_name, diff --git a/source4/auth/sam.c b/source4/auth/sam.c index 201185cf4d..244ebc732e 100644 --- a/source4/auth/sam.c +++ b/source4/auth/sam.c @@ -294,72 +294,94 @@ static bool sids_contains_sid(const struct dom_sid **sids, * "false". * The "only_childs" flag is particularly useful if you have a user SID and * want to include all his groups (referenced with "memberOf") without his SID - * itself. + * itself, or considering if that SID matches the filter * * At the beginning "res_sids" should reference to a NULL pointer. */ -_PUBLIC_ NTSTATUS authsam_expand_nested_groups(struct ldb_context *sam_ctx, - const struct dom_sid *sid, const bool only_childs, - TALLOC_CTX *res_sids_ctx, struct dom_sid ***res_sids, - unsigned int *num_res_sids) +NTSTATUS authsam_expand_nested_groups(struct ldb_context *sam_ctx, + struct ldb_val *dn_val, const bool only_childs, const char *filter, + TALLOC_CTX *res_sids_ctx, struct dom_sid ***res_sids, + unsigned int *num_res_sids) { const char * const attrs[] = { "memberOf", NULL }; unsigned int i; int ret; bool already_there; - struct ldb_dn *tmp_dn; - struct dom_sid *tmp_sid; + struct ldb_dn *dn; + struct dom_sid *sid; TALLOC_CTX *tmp_ctx; - struct ldb_message **res; + struct ldb_result *res; NTSTATUS status; + const struct ldb_val *v; + const struct ldb_message_element *el; + enum ndr_err_code ndr_err; if (*res_sids == NULL) { *num_res_sids = 0; } - if (sid == NULL) { - return NT_STATUS_OK; + tmp_ctx = talloc_new(res_sids_ctx); + + sid = talloc(tmp_ctx, struct dom_sid); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sid, tmp_ctx); + + dn = ldb_dn_from_ldb_val(tmp_ctx, sam_ctx, dn_val); + if (dn == NULL) { + talloc_free(tmp_ctx); + return NT_STATUS_INTERNAL_DB_CORRUPTION; } + v = ldb_dn_get_extended_component(dn, "SID"); + ndr_err = ndr_pull_struct_blob(v, sid, NULL, sid, + (ndr_pull_flags_fn_t)ndr_pull_dom_sid); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(tmp_ctx); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + /* This is an O(n^2) linear search */ already_there = sids_contains_sid((const struct dom_sid**) *res_sids, - *num_res_sids, sid); + *num_res_sids, sid); if (already_there) { return NT_STATUS_OK; } - if (!only_childs) { - tmp_sid = dom_sid_dup(res_sids_ctx, sid); - NT_STATUS_HAVE_NO_MEMORY(tmp_sid); - *res_sids = talloc_realloc(res_sids_ctx, *res_sids, - struct dom_sid *, *num_res_sids + 1); - NT_STATUS_HAVE_NO_MEMORY(*res_sids); - (*res_sids)[*num_res_sids] = tmp_sid; - ++(*num_res_sids); + if (only_childs) { + ret = dsdb_search(sam_ctx, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, NULL); + } else { + ret = dsdb_search(sam_ctx, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, "%s", filter); } - tmp_ctx = talloc_new(res_sids_ctx); + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + talloc_free(tmp_ctx); + return NT_STATUS_OK; + } - ret = gendb_search(sam_ctx, tmp_ctx, NULL, &res, attrs, - "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid)); - if (ret != 1) { + if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } - if (res[0]->num_elements == 0) { - talloc_free(res); + /* We may get back 0 results, if the SID didn't match the filter - such as it wasn't a domain group, for example */ + if (res->count != 1) { talloc_free(tmp_ctx); return NT_STATUS_OK; } - for (i = 0; i < res[0]->elements[0].num_values; i++) { - tmp_dn = ldb_dn_from_ldb_val(tmp_ctx, sam_ctx, - &res[0]->elements[0].values[i]); - tmp_sid = samdb_search_dom_sid(sam_ctx, tmp_ctx, tmp_dn, - "objectSid", NULL); + /* We only apply this test once we know the SID matches the filter */ + if (!only_childs) { + *res_sids = talloc_realloc(res_sids_ctx, *res_sids, + struct dom_sid *, *num_res_sids + 1); + NT_STATUS_HAVE_NO_MEMORY(*res_sids); + (*res_sids)[*num_res_sids] = talloc_steal(*res_sids, sid); + ++(*num_res_sids); + } - status = authsam_expand_nested_groups(sam_ctx, tmp_sid, - false, res_sids_ctx, res_sids, num_res_sids); + el = ldb_msg_find_element(res->msgs[0], "memberOf"); + + for (i = 0; el && i < el->num_values; i++) { + status = authsam_expand_nested_groups(sam_ctx, &el->values[i], + false, filter, res_sids_ctx, res_sids, num_res_sids); if (!NT_STATUS_IS_OK(status)) { talloc_free(res); talloc_free(tmp_ctx); @@ -385,72 +407,87 @@ _PUBLIC_ NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, { NTSTATUS status; struct auth_serversupplied_info *server_info; - const char *str; - struct dom_sid *tmp_sid; + const char *str, *filter; /* SIDs for the account and his primary group */ struct dom_sid *account_sid; struct dom_sid *primary_group_sid; + const char *primary_group_string; + const char *primary_group_dn; + DATA_BLOB primary_group_blob; /* SID structures for the expanded group memberships */ - struct dom_sid **groupSIDs = NULL, **groupSIDs_2 = NULL; - unsigned int num_groupSIDs = 0, num_groupSIDs_2 = 0, i; - uint32_t userAccountControl; + struct dom_sid **groupSIDs = NULL; + unsigned int num_groupSIDs = 0, i; + struct dom_sid *domain_sid; + TALLOC_CTX *tmp_ctx; + struct ldb_message_element *el; server_info = talloc(mem_ctx, struct auth_serversupplied_info); NT_STATUS_HAVE_NO_MEMORY(server_info); + tmp_ctx = talloc_new(server_info); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(server_info, server_info); + account_sid = samdb_result_dom_sid(server_info, msg, "objectSid"); NT_STATUS_HAVE_NO_MEMORY_AND_FREE(account_sid, server_info); - primary_group_sid = dom_sid_add_rid(server_info, - samdb_domain_sid(sam_ctx), - samdb_result_uint(msg, "primaryGroupID", ~0)); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(primary_group_sid, server_info); - - /* Expands the primary group */ - status = authsam_expand_nested_groups(sam_ctx, primary_group_sid, false, - server_info, &groupSIDs, &num_groupSIDs); + status = dom_sid_split_rid(tmp_ctx, account_sid, &domain_sid, NULL); if (!NT_STATUS_IS_OK(status)) { talloc_free(server_info); return status; } - /* Expands the additional groups */ - status = authsam_expand_nested_groups(sam_ctx, account_sid, true, - server_info, &groupSIDs_2, &num_groupSIDs_2); + primary_group_sid = dom_sid_add_rid(server_info, + domain_sid, + samdb_result_uint(msg, "primaryGroupID", ~0)); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(primary_group_sid, server_info); + + /* 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); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(filter, server_info); + + primary_group_string = dom_sid_string(tmp_ctx, primary_group_sid); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(primary_group_string, server_info); + + primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(primary_group_dn, server_info); + + primary_group_blob = data_blob_string_const(primary_group_dn); + + /* Expands the primary group - this function takes in + * memberOf-like values, so we fake one up with the + * <SID=S-...> format of DN and then let it expand + * them, as long as they meet the filter - so only + * domain groups, not builtin groups + * + * The primary group is still treated specially, so we set the + * 'only childs' flag to true + */ + status = authsam_expand_nested_groups(sam_ctx, &primary_group_blob, true, filter, + server_info, &groupSIDs, &num_groupSIDs); if (!NT_STATUS_IS_OK(status)) { talloc_free(server_info); return status; } - /* Merge the two expanded structures (groupSIDs, groupSIDs_2) */ - for (i = 0; i < num_groupSIDs_2; i++) - if (!sids_contains_sid((const struct dom_sid **) groupSIDs, - num_groupSIDs, groupSIDs_2[i])) { - tmp_sid = dom_sid_dup(server_info, groupSIDs_2[i]); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(tmp_sid, server_info); - groupSIDs = talloc_realloc(server_info, groupSIDs, - struct dom_sid *, num_groupSIDs + 1); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(groupSIDs, - server_info); - groupSIDs[num_groupSIDs] = tmp_sid; - ++num_groupSIDs; + /* Expands the additional groups */ + el = ldb_msg_find_element(msg, "memberOf"); + for (i = 0; el && i < el->num_values; i++) { + /* This function takes in memberOf values and expands + * them, as long as they meet the filter - so only + * domain groups, not builtin groups */ + status = authsam_expand_nested_groups(sam_ctx, &el->values[i], false, filter, + server_info, &groupSIDs, &num_groupSIDs); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(server_info); + return status; } - talloc_free(groupSIDs_2); + } server_info->account_sid = account_sid; server_info->primary_group_sid = primary_group_sid; - /* DCs also get SID_NT_ENTERPRISE_DCS */ - userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0); - if (userAccountControl & UF_SERVER_TRUST_ACCOUNT) { - groupSIDs = talloc_realloc(server_info, groupSIDs, struct dom_sid *, - num_groupSIDs+1); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(groupSIDs, server_info); - groupSIDs[num_groupSIDs] = dom_sid_parse_talloc(groupSIDs, SID_NT_ENTERPRISE_DCS); - NT_STATUS_HAVE_NO_MEMORY_AND_FREE(groupSIDs[num_groupSIDs], server_info); - num_groupSIDs++; - } - server_info->domain_groups = groupSIDs; server_info->n_domain_groups = num_groupSIDs; @@ -523,6 +560,7 @@ _PUBLIC_ NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, server_info->authenticated = true; + talloc_free(tmp_ctx); *_server_info = server_info; return NT_STATUS_OK; diff --git a/source4/auth/session.c b/source4/auth/session.c index 03d1c91054..7817195727 100644 --- a/source4/auth/session.c +++ b/source4/auth/session.c @@ -49,9 +49,71 @@ _PUBLIC_ NTSTATUS auth_generate_session_info(TALLOC_CTX *mem_ctx, { struct auth_session_info *session_info; NTSTATUS nt_status; + unsigned int i, num_groupSIDs = 0; + const char *account_sid_string; + const char *account_sid_dn; + DATA_BLOB account_sid_blob; + const char *primary_group_string; + const char *primary_group_dn; + DATA_BLOB primary_group_blob; - session_info = talloc(mem_ctx, struct auth_session_info); - NT_STATUS_HAVE_NO_MEMORY(session_info); + const char *filter; + + struct dom_sid **groupSIDs = NULL; + const struct dom_sid *dom_sid; + bool is_enterprise_dc = false; + + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); + + if (!auth_context->sam_ctx) { + DEBUG(0, ("No SAM available, cannot determine local groups\n")); + return NT_STATUS_INVALID_SYSTEM_SERVICE; + } + + /* For now, we don't have trusted domains, so we do a very + * simple check to see that the user's SID is in *this* + * domain, and then trust the user account control. When we + * get trusted domains, we should check it's a trusted domain + * in this forest. This elaborate check is to try and avoid a + * nasty security bug if we forget about this later... */ + + if (server_info->acct_flags & ACB_SVRTRUST) { + dom_sid = samdb_domain_sid(auth_context->sam_ctx); + if (dom_sid) { + if (dom_sid_in_domain(dom_sid, server_info->account_sid)) { + is_enterprise_dc = true; + } else { + DEBUG(2, ("DC %s is not in our domain. " + "It will not have Enterprise Domain Controllers membership on this server", + server_info->account_name)); + } + } else { + DEBUG(2, ("Could not obtain local domain SID, " + "so can not determine if DC %s is a DC of this domain. " + "It will not have Enterprise Domain Controllers membership", + server_info->account_name)); + } + } + + groupSIDs = talloc_array(tmp_ctx, struct dom_sid *, server_info->n_domain_groups); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(groupSIDs, tmp_ctx); + if (!groupSIDs) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + num_groupSIDs = server_info->n_domain_groups; + + for (i=0; i < server_info->n_domain_groups; i++) { + groupSIDs[i] = server_info->domain_groups[i]; + } + + filter = talloc_asprintf(tmp_ctx, "(&(objectClass=group)(groupType:1.2.840.113556.1.4.803:=%u))", + GROUP_TYPE_BUILTIN_LOCAL_GROUP); + + session_info = talloc(tmp_ctx, struct auth_session_info); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(session_info, tmp_ctx); session_info->server_info = talloc_reference(session_info, server_info); @@ -59,19 +121,94 @@ _PUBLIC_ NTSTATUS auth_generate_session_info(TALLOC_CTX *mem_ctx, * key from the auth subsystem */ session_info->session_key = server_info->user_session_key; + /* Search for each group in the token */ + + /* Expands the account SID - this function takes in + * memberOf-like values, so we fake one up with the + * <SID=S-...> format of DN and then let it expand + * them, as long as they meet the filter - so only + * builtin groups + * + * We already have the primary group in the token, so set + * 'only childs' flag to true + */ + account_sid_string = dom_sid_string(tmp_ctx, server_info->account_sid); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(account_sid_string, server_info); + + account_sid_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", account_sid_string); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(account_sid_dn, server_info); + + account_sid_blob = data_blob_string_const(account_sid_dn); + + nt_status = authsam_expand_nested_groups(auth_context->sam_ctx, &account_sid_blob, true, filter, + tmp_ctx, &groupSIDs, &num_groupSIDs); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(tmp_ctx); + return nt_status; + } + + /* Expands the primary group - this function takes in + * memberOf-like values, so we fake one up with the + * <SID=S-...> format of DN and then let it expand + * them, as long as they meet the filter - so only + * builtin groups + * + * We already have the primary group in the token, so set + * 'only childs' flag to true + */ + primary_group_string = dom_sid_string(tmp_ctx, server_info->primary_group_sid); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(primary_group_string, server_info); + + primary_group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", primary_group_string); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(primary_group_dn, server_info); + + primary_group_blob = data_blob_string_const(primary_group_dn); + + nt_status = authsam_expand_nested_groups(auth_context->sam_ctx, &primary_group_blob, true, filter, + tmp_ctx, &groupSIDs, &num_groupSIDs); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(tmp_ctx); + return nt_status; + } + + for (i = 0; i < server_info->n_domain_groups; i++) { + const char *group_string; + const char *group_dn; + DATA_BLOB group_blob; + group_string = dom_sid_string(tmp_ctx, server_info->domain_groups[i]); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(group_string, server_info); + + group_dn = talloc_asprintf(tmp_ctx, "<SID=%s>", group_string); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(group_dn, server_info); + + group_blob = data_blob_string_const(group_dn); + + /* This function takes in memberOf values and expands + * them, as long as they meet the filter - so only + * builtin groups */ + nt_status = authsam_expand_nested_groups(auth_context->sam_ctx, &group_blob, true, filter, + tmp_ctx, &groupSIDs, &num_groupSIDs); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(tmp_ctx); + return nt_status; + } + } + nt_status = security_token_create(session_info, auth_context->event_ctx, auth_context->lp_ctx, server_info->account_sid, server_info->primary_group_sid, - server_info->n_domain_groups, - server_info->domain_groups, + num_groupSIDs, + groupSIDs, server_info->authenticated, + is_enterprise_dc, &session_info->security_token); - NT_STATUS_NOT_OK_RETURN(nt_status); + NT_STATUS_NOT_OK_RETURN_AND_FREE(nt_status, tmp_ctx); session_info->credentials = NULL; + talloc_steal(mem_ctx, session_info); *_session_info = session_info; return NT_STATUS_OK; } diff --git a/source4/dsdb/samdb/samdb.c b/source4/dsdb/samdb/samdb.c index 51e3f73a7e..9e4156407e 100644 --- a/source4/dsdb/samdb/samdb.c +++ b/source4/dsdb/samdb/samdb.c @@ -147,6 +147,7 @@ NTSTATUS security_token_create(TALLOC_CTX *mem_ctx, unsigned int n_groupSIDs, struct dom_sid **groupSIDs, bool is_authenticated, + bool is_dc, struct security_token **token) { struct security_token *ptoken; @@ -156,7 +157,7 @@ NTSTATUS security_token_create(TALLOC_CTX *mem_ctx, ptoken = security_token_initialise(mem_ctx); NT_STATUS_HAVE_NO_MEMORY(ptoken); - ptoken->sids = talloc_array(ptoken, struct dom_sid *, n_groupSIDs + 5); + ptoken->sids = talloc_array(ptoken, struct dom_sid *, n_groupSIDs + 6); NT_STATUS_HAVE_NO_MEMORY(ptoken->sids); ptoken->user_sid = talloc_reference(ptoken, user_sid); @@ -178,7 +179,13 @@ NTSTATUS security_token_create(TALLOC_CTX *mem_ctx, ptoken->num_sids = 4; if (is_authenticated) { - ptoken->sids[4] = dom_sid_parse_talloc(ptoken->sids, SID_NT_AUTHENTICATED_USERS); + ptoken->sids[ptoken->num_sids] = dom_sid_parse_talloc(ptoken->sids, SID_NT_AUTHENTICATED_USERS); + NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[4]); + ptoken->num_sids++; + } + + if (is_dc) { + ptoken->sids[ptoken->num_sids] = dom_sid_parse_talloc(ptoken->sids, SID_NT_ENTERPRISE_DCS); NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[4]); ptoken->num_sids++; } |