From 08a7bb4abc6e59bc109ac38c164ad3f0b63b2439 Mon Sep 17 00:00:00 2001 From: Wilco Baan Hofman Date: Fri, 23 Apr 2010 09:32:01 +0200 Subject: Add gpo list function for listing applicable GPO's for users/machines. Implements: * Hierarchical listing through all parent containers * security ACL checking on the GPO * User/computer disabled flags on the GPO * gPLink disabled * gPLink enforced * Block inheritance on the container Signed-off-by: Jelmer Vernooij --- source4/libgpo/gpo.h | 13 ++- source4/libgpo/gpo_ldap.c | 249 +++++++++++++++++++++++++++++++++++++++----- source4/utils/net/net_gpo.c | 98 ++++++++++++++++- 3 files changed, 322 insertions(+), 38 deletions(-) diff --git a/source4/libgpo/gpo.h b/source4/libgpo/gpo.h index 1e51e5250a..2e58094608 100644 --- a/source4/libgpo/gpo.h +++ b/source4/libgpo/gpo.h @@ -29,6 +29,10 @@ #define GPO_FLAG_USER_DISABLE (1 << 0) #define GPO_FLAG_MACHINE_DISABLE (1 << 1) +enum gpo_inheritance { + GPO_INHERIT = 0, + GPO_BLOCK_INHERITANCE = 1, +}; struct gp_context { struct ldb_context *ldb_ctx; @@ -47,13 +51,6 @@ struct gp_object { struct security_descriptor *security_descriptor; }; -struct gp_hierarchy_object { - enum { - GPO_INHERIT = 0, - GPO_BLOCK_INHERITANCE = 1, - } inheritance; - struct gp_link **gplinks; -}; struct gp_link { uint32_t options; @@ -72,6 +69,8 @@ NTSTATUS gp_init(TALLOC_CTX *mem_ctx, NTSTATUS gp_list_all_gpos(struct gp_context *gp_ctx, struct gp_object ***ret); NTSTATUS gp_get_gpo_info(struct gp_context *gp_ctx, const char *name, struct gp_object **ret); NTSTATUS gp_get_gplinks(struct gp_context *gp_ctx, const char *req_dn, struct gp_link ***ret); +NTSTATUS gp_list_gpos(struct gp_context *gp_ctx, struct security_token *token, const char ***ret); + NTSTATUS gp_get_gplink_options(TALLOC_CTX *mem_ctx, uint32_t flags, const char ***ret); NTSTATUS gp_get_gpo_flags(TALLOC_CTX *mem_ctx, uint32_t flags, const char ***ret); diff --git a/source4/libgpo/gpo_ldap.c b/source4/libgpo/gpo_ldap.c index 96a746c72b..4c44e83cb9 100644 --- a/source4/libgpo/gpo_ldap.c +++ b/source4/libgpo/gpo_ldap.c @@ -25,6 +25,10 @@ #include "../librpc/gen_ndr/nbt.h" #include "libcli/libcli.h" #include "libnet/libnet.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "../libcli/security/dom_sid.h" +#include "libcli/security/security.h" +#include "../lib/talloc/talloc.h" #include "gpo.h" struct gpo_stringmap { @@ -51,7 +55,9 @@ static NTSTATUS parse_gpo(TALLOC_CTX *mem_ctx, struct ldb_message *msg, struct g { unsigned int i; struct gp_object *gpo = talloc(mem_ctx, struct gp_object); - gpo->dn = ldb_dn_get_linearized(msg->dn); + enum ndr_err_code ndr_err; + + gpo->dn = talloc_steal(mem_ctx, ldb_dn_get_linearized(msg->dn)); DEBUG(9, ("Parsing GPO LDAP data for %s\n", gpo->dn)); for (i = 0; i < msg->num_elements; i++) { @@ -86,6 +92,18 @@ static NTSTATUS parse_gpo(TALLOC_CTX *mem_ctx, struct ldb_message *msg, struct g gpo->file_sys_path = talloc_strdup(gpo, (char *)element->values[0].data); DEBUG(10, ("Found file system path: %s\n", gpo->file_sys_path)); } + if (strcmp(element->name, "nTSecurityDescriptor") == 0) { + gpo->security_descriptor = talloc(mem_ctx, struct security_descriptor); + ndr_err = ndr_pull_struct_blob(&element->values[0], + mem_ctx, + NULL, + gpo->security_descriptor, + (ndr_pull_flags_fn_t)ndr_pull_security_descriptor); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return ndr_map_error2ntstatus(ndr_err); + } + DEBUG(10, ("Found security descriptor.\n")); + } } *ret = gpo; @@ -162,8 +180,9 @@ NTSTATUS gp_init(TALLOC_CTX *mem_ctx, /* Connect to ldap://DC_NAME with all relevant contexts*/ url = talloc_asprintf(mem_ctx, "ldap://%s", io->out.dcs[0].name); ldb_ctx = ldb_wrap_connect(mem_ctx, net_ctx->event_ctx, lp_ctx, - url, NULL, net_ctx->cred, 0); + url, NULL, net_ctx->cred, 0); if (ldb_ctx == NULL) { + DEBUG(0, ("Can't connect to DC's LDAP with url %s\n", url)); return NT_STATUS_UNSUCCESSFUL; } @@ -187,6 +206,7 @@ NTSTATUS gp_list_all_gpos(struct gp_context *gp_ctx, struct gp_object ***ret) struct ldb_dn *dn; struct gp_object **gpo; unsigned int i; /* same as in struct ldb_result */ + const char **attrs; /* Create a forked memory context, as a base for everything here */ mem_ctx = talloc_new(gp_ctx); @@ -202,7 +222,16 @@ NTSTATUS gp_list_all_gpos(struct gp_context *gp_ctx, struct gp_object ***ret) DEBUG(10, ("Searching for policies in DN: %s\n", ldb_dn_get_linearized(dn))); - rv = ldb_search(gp_ctx->ldb_ctx, mem_ctx, &result, dn, LDB_SCOPE_ONELEVEL, NULL, "(objectClass=groupPolicyContainer)"); + attrs = talloc_array(mem_ctx, const char *, 7); + attrs[0] = "nTSecurityDescriptor"; + attrs[1] = "versionNumber"; + attrs[2] = "flags"; + attrs[3] = "name"; + attrs[4] = "displayName"; + attrs[5] = "gPCFileSysPath"; + attrs[6] = NULL; + + rv = ldb_search(gp_ctx->ldb_ctx, mem_ctx, &result, dn, LDB_SCOPE_ONELEVEL, attrs, "(objectClass=groupPolicyContainer)"); if (rv != LDB_SUCCESS) { DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv),ldb_errstring(gp_ctx->ldb_ctx))); talloc_free(mem_ctx); @@ -227,7 +256,7 @@ NTSTATUS gp_list_all_gpos(struct gp_context *gp_ctx, struct gp_object ***ret) return NT_STATUS_OK; } -NTSTATUS gp_get_gpo_info(struct gp_context *gp_ctx, const char *name, struct gp_object **ret) +NTSTATUS gp_get_gpo_info(struct gp_context *gp_ctx, const char *dn_str, struct gp_object **ret) { struct ldb_result *result; struct ldb_dn *dn; @@ -235,27 +264,30 @@ NTSTATUS gp_get_gpo_info(struct gp_context *gp_ctx, const char *name, struct gp_ int rv; NTSTATUS status; TALLOC_CTX *mem_ctx; + const char **attrs; /* Create a forked memory context, as a base for everything here */ mem_ctx = talloc_new(gp_ctx); - /* Create full ldb dn of the policies base object */ - dn = ldb_get_default_basedn(gp_ctx->ldb_ctx); - rv = ldb_dn_add_child(dn, ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, "CN=Policies,CN=System")); - if (!rv) { - DEBUG(0, ("Can't append subtree to DN\n")); - talloc_free(mem_ctx); - return NT_STATUS_UNSUCCESSFUL; - } + /* Create an ldb dn struct for the dn string */ + dn = ldb_dn_new(mem_ctx, gp_ctx->ldb_ctx, dn_str); + + attrs = talloc_array(mem_ctx, const char *, 7); + attrs[0] = "nTSecurityDescriptor"; + attrs[1] = "versionNumber"; + attrs[2] = "flags"; + attrs[3] = "name"; + attrs[4] = "displayName"; + attrs[5] = "gPCFileSysPath"; + attrs[6] = NULL; rv = ldb_search(gp_ctx->ldb_ctx, - mem_ctx, - &result, - dn, - LDB_SCOPE_ONELEVEL, - NULL, - "(&(objectClass=groupPolicyContainer)(name=%s))", - name); + mem_ctx, + &result, + dn, + LDB_SCOPE_BASE, + attrs, + "objectClass=groupPolicyContainer"); if (rv != LDB_SUCCESS) { DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv),ldb_errstring(gp_ctx->ldb_ctx))); talloc_free(mem_ctx); @@ -264,7 +296,7 @@ NTSTATUS gp_get_gpo_info(struct gp_context *gp_ctx, const char *name, struct gp_ /* We expect exactly one record */ if (result->count != 1) { - DEBUG(0, ("Could not find GPC with name %s\n", name)); + DEBUG(0, ("Could not find GPC with dn %s\n", dn_str)); talloc_free(mem_ctx); return NT_STATUS_NOT_FOUND; } @@ -290,6 +322,7 @@ static NTSTATUS parse_gplink (TALLOC_CTX *mem_ctx, const char *gplink_str, struc char *buf, *end; gplinks = talloc_array(mem_ctx, struct gp_link *, 1); + gplinks[0] = NULL; /* Assuming every gPLink starts with "[LDAP://" */ start = 8; @@ -299,19 +332,20 @@ static NTSTATUS parse_gplink (TALLOC_CTX *mem_ctx, const char *gplink_str, struc gplinks = talloc_realloc(mem_ctx, gplinks, struct gp_link *, idx+2); gplinks[idx] = talloc(mem_ctx, struct gp_link); gplinks[idx]->dn = talloc_strndup(mem_ctx, - gplink_str + start, - pos - start); + gplink_str + start, + pos - start); for (start = pos + 1; gplink_str[pos] != ']'; pos++); - buf = talloc_strndup(mem_ctx, gplink_str + start, pos - start); - + buf = talloc_strndup(gplinks, gplink_str + start, pos - start); gplinks[idx]->options = (uint32_t) strtoll(buf, &end, 0); + talloc_free(buf); /* Set the last entry in the array to be NULL */ gplinks[idx + 1] = NULL; - /* Increment the array index, the string position, and the start reference */ + /* Increment the array index, the string position past + the next "[LDAP://", and set the start reference */ idx++; pos += 9; start = pos; @@ -341,7 +375,7 @@ NTSTATUS gp_get_gplinks(struct gp_context *gp_ctx, const char *req_dn, struct gp rv = ldb_search(gp_ctx->ldb_ctx, mem_ctx, &result, dn, LDB_SCOPE_BASE, NULL, "(objectclass=*)"); if (rv != LDB_SUCCESS) { - DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv),ldb_errstring(gp_ctx->ldb_ctx))); + DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv), ldb_errstring(gp_ctx->ldb_ctx))); talloc_free(mem_ctx); return NT_STATUS_UNSUCCESSFUL; } @@ -364,10 +398,171 @@ NTSTATUS gp_get_gplinks(struct gp_context *gp_ctx, const char *req_dn, struct gp status = parse_gplink(gp_ctx, gplink_str, &gplinks); if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("Failed to parse gPLinks\n")); + DEBUG(0, ("Failed to parse gPLink\n")); return status; } + talloc_free(mem_ctx); + *ret = gplinks; return NT_STATUS_OK; } + +NTSTATUS gp_list_gpos(struct gp_context *gp_ctx, struct security_token *token, const char ***ret) +{ + TALLOC_CTX *mem_ctx; + const char **gpos; + struct ldb_result *result; + const char *sid; + struct ldb_dn *dn; + struct ldb_message_element *element; + int inherit; + const char *attrs[] = { "objectClass", NULL }; + int rv; + NTSTATUS status; + unsigned int count = 0; + unsigned int i; + enum { + ACCOUNT_TYPE_USER = 0, + ACCOUNT_TYPE_MACHINE = 1 + } account_type; + + /* Create a forked memory context, as a base for everything here */ + mem_ctx = talloc_new(gp_ctx); + + sid = dom_sid_string(mem_ctx, token->user_sid); + + /* Find the user DN and objectclass via the sid from the security token */ + rv = ldb_search(gp_ctx->ldb_ctx, + mem_ctx, + &result, + ldb_get_default_basedn(gp_ctx->ldb_ctx), + LDB_SCOPE_SUBTREE, + attrs, + "(&(objectclass=user)(objectSid=%s))", sid); + if (rv != LDB_SUCCESS) { + DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv), + ldb_errstring(gp_ctx->ldb_ctx))); + talloc_free(mem_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + if (result->count != 1) { + DEBUG(0, ("Could not find user with sid %s.\n", sid)); + talloc_free(mem_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + DEBUG(10,("Found DN for this user: %s\n", ldb_dn_get_linearized(result->msgs[0]->dn))); + + element = ldb_msg_find_element(result->msgs[0], "objectClass"); + + /* We need to know if this account is a user or machine. */ + account_type = ACCOUNT_TYPE_USER; + for (i = 0; i < element->num_values; i++) { + if (strcmp((char *)element->values[i].data, "computer") == 0) { + account_type = ACCOUNT_TYPE_MACHINE; + DEBUG(10, ("This user is a machine\n")); + } + } + + gpos = talloc_array(gp_ctx, const char *, 1); + gpos[0] = NULL; + + /* Walk through the containers until we hit the root */ + inherit = 1; + dn = ldb_dn_get_parent(mem_ctx, result->msgs[0]->dn); + while (ldb_dn_compare_base(ldb_get_default_basedn(gp_ctx->ldb_ctx), dn) == 0) { + const char *gpo_attrs[] = { "gPLink", "gPOptions", NULL }; + struct gp_link **gplinks; + enum gpo_inheritance gpoptions; + + DEBUG(10, ("Getting gPLinks for DN: %s\n", ldb_dn_get_linearized(dn))); + + /* Get the gPLink and gPOptions attributes from the container */ + rv = ldb_search(gp_ctx->ldb_ctx, + mem_ctx, + &result, + dn, + LDB_SCOPE_BASE, + gpo_attrs, + "objectclass=*"); + if (rv != LDB_SUCCESS) { + DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv), + ldb_errstring(gp_ctx->ldb_ctx))); + talloc_free(mem_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + + /* Parse the gPLink attribute, put it into a nice struct array */ + status = parse_gplink(mem_ctx, ldb_msg_find_attr_as_string(result->msgs[0], "gPLink", ""), &gplinks); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to parse gPLink\n")); + talloc_free(mem_ctx); + return status; + } + + /* Check all group policy links on this container */ + for (i = 0; gplinks[i] != NULL; i++) { + struct gp_object *gpo; + uint32_t access_granted; + + /* If inheritance was blocked at a higher level and this + * gplink is not enforced, it should not be applied */ + if (!inherit && !(gplinks[i]->options & GPLINK_OPT_ENFORCE)) + continue; + + /* Don't apply disabled links */ + if (gplinks[i]->options & GPLINK_OPT_DISABLE) + continue; + + /* Get GPO information */ + status = gp_get_gpo_info(gp_ctx, gplinks[i]->dn, &gpo); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to get gpo information for %s\n", gplinks[i]->dn)); + talloc_free(mem_ctx); + return status; + } + + /* If the account does not have read access, this GPO does not apply + * to this account */ + status = sec_access_check(gpo->security_descriptor, + token, + (SEC_STD_READ_CONTROL | SEC_ADS_LIST | SEC_ADS_READ_PROP), + &access_granted); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + + /* If the account is a user and the GPO has user disabled flag, or + * a machine and the GPO has machine disabled flag, this GPO does + * not apply to this account */ + if ((account_type == ACCOUNT_TYPE_USER && + (gpo->flags & GPO_FLAG_USER_DISABLE)) || + (account_type == ACCOUNT_TYPE_MACHINE && + (gpo->flags & GPO_FLAG_MACHINE_DISABLE))) { + continue; + } + + /* Add the GPO to the list */ + gpos = talloc_realloc(gp_ctx, gpos, const char *, count+2); + gpos[count] = talloc_strdup(gp_ctx, gplinks[i]->dn); + gpos[count+1] = NULL; + count++; + + /* Clean up */ + talloc_free(gpo); + } + + /* If inheritance is blocked, then we should only add enforced gPLinks + * higher up */ + gpoptions = ldb_msg_find_attr_as_uint(result->msgs[0], "gPOptions", 0); + if (gpoptions == GPO_BLOCK_INHERITANCE) { + inherit = 0; + } + dn = ldb_dn_get_parent(mem_ctx, dn); + } + + talloc_free(mem_ctx); + + *ret = gpos; + return NT_STATUS_OK; +} diff --git a/source4/utils/net/net_gpo.c b/source4/utils/net/net_gpo.c index fc25c30fe6..4f9414bdd8 100644 --- a/source4/utils/net/net_gpo.c +++ b/source4/utils/net/net_gpo.c @@ -23,6 +23,9 @@ #include "includes.h" #include "utils/net/net.h" +#include "lib/ldb/include/ldb.h" +#include "auth/auth.h" +#include "param/param.h" #include "libgpo/gpo.h" static int net_gpo_list_all_usage(struct net_context *ctx, int argc, const char **argv) @@ -78,7 +81,7 @@ static int net_gpo_list_all(struct net_context *ctx, int argc, const char **argv static int net_gpo_get_gpo_usage(struct net_context *ctx, int argc, const char **argv) { - d_printf("Syntax: net gpo getgpo [options]\n"); + d_printf("Syntax: net gpo getgpo [options]\n"); d_printf("For a list of available options, please type net gpo getgpo --help\n"); return 0; } @@ -131,8 +134,8 @@ static int net_gpo_get_gpo(struct net_context *ctx, int argc, const char **argv) static int net_gpo_link_get_usage(struct net_context *ctx, int argc, const char **argv) { - d_printf("Syntax: net gpo linkget [options]\n"); - d_printf("For a list of available options, please type net gpo get --help\n"); + d_printf("Syntax: net gpo linkget [options]\n"); + d_printf("For a list of available options, please type net gpo linkget --help\n"); return 0; } @@ -182,6 +185,93 @@ static int net_gpo_link_get(struct net_context *ctx, int argc, const char **argv return 0; } +static int net_gpo_list_usage(struct net_context *ctx, int argc, const char **argv) +{ + d_printf("Syntax: net gpo list [options]\n"); + d_printf("For a list of available options, please type net gpo list --help\n"); + return 0; +} + +static int net_gpo_list(struct net_context *ctx, int argc, const char **argv) +{ + struct gp_context *gp_ctx; + struct ldb_result *result; + struct auth_serversupplied_info *server_info; + struct auth_session_info *session_info; + DATA_BLOB dummy = { NULL, 0 }; + const char **gpos; + NTSTATUS status; + int rv; + unsigned int i; + + if (argc != 1) { + return net_gpo_list_usage(ctx, argc, argv); + } + status = gp_init(ctx, ctx->lp_ctx, ctx->credentials, ctx->event_ctx, &gp_ctx); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to connect to DC's LDAP: %s\n", get_friendly_nt_error_msg(status))); + return 1; + } + + rv = ldb_search(gp_ctx->ldb_ctx, + gp_ctx, + &result, + ldb_get_default_basedn(gp_ctx->ldb_ctx), + LDB_SCOPE_SUBTREE, + NULL, + "(&(objectClass=user)(sAMAccountName=%s))", argv[0]); + if (rv != LDB_SUCCESS) { + DEBUG(0, ("LDB search failed: %s\n%s\n", ldb_strerror(rv),ldb_errstring(gp_ctx->ldb_ctx))); + talloc_free(gp_ctx); + return 1; + } + + /* We expect exactly one record */ + if (result->count != 1) { + DEBUG(0, ("Could not find SAM account with name %s\n", argv[0])); + talloc_free(gp_ctx); + return 1; + } + + status = authsam_make_server_info(gp_ctx, + gp_ctx->ldb_ctx, + lp_netbios_name(gp_ctx->lp_ctx), + lp_sam_name(gp_ctx->lp_ctx), + ldb_get_default_basedn(gp_ctx->ldb_ctx), + result->msgs[0], + dummy, + dummy, + &server_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to make server information: %s\n", get_friendly_nt_error_msg(status))); + talloc_free(gp_ctx); + return 1; + } + + status = auth_generate_session_info(gp_ctx, gp_ctx->ev_ctx, gp_ctx->lp_ctx, server_info, &session_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to generate session information: %s\n", get_friendly_nt_error_msg(status))); + talloc_free(gp_ctx); + return 1; + } + + status = gp_list_gpos(gp_ctx, session_info->security_token, &gpos); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to list gpos for user %s: %s\n", argv[0], + get_friendly_nt_error_msg(status))); + talloc_free(gp_ctx); + return 1; + } + + d_printf("GPO's for user %s:\n", argv[0]); + for (i = 0; gpos[i] != NULL; i++) { + d_printf("\t%s\n", gpos[i]); + } + + talloc_free(gp_ctx); + return 0; +} + static const struct net_functable net_gpo_functable[] = { { "listall", "List all GPO's on a DC\n", net_gpo_list_all, net_gpo_list_all_usage }, { "getgpo", "List specificied GPO\n", net_gpo_get_gpo, net_gpo_get_gpo_usage }, @@ -189,7 +279,7 @@ static const struct net_functable net_gpo_functable[] = { /* { "apply", "Apply GPO to container\n", net_gpo_apply, net_gpo_usage }, */ // { "linkadd", "Link a GPO to a container\n", net_gpo_link_add, net_gpo_usage }, /* { "linkdelete", "Delete GPO link from a container\n", net_gpo_link_delete, net_gpo_usage }, */ -// { "list", "List all GPO's for machine/user\n", net_gpo_list, net_gpo_list_usage }, + { "list", "List all GPO's for a machine/user\n", net_gpo_list, net_gpo_list_usage }, // { "refresh", "List all GPO's for machine/user and download them\n", net_gpo_refresh, net_gpo_refresh_usage }, { NULL, NULL } }; -- cgit