mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
69410c0a02
GP links with the GPOPTIONS_BLOCK_INHERITANCE option set were blocking GPOs from the same link (i.e. an OU with the flag set would block its own GPOs). This patch makes sure the GPOs from the link are added to the list. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13046 Signed-off-by: Lutz Justen <ljusten@google.com> Reviewed-by: Jeremy Allison <jra@samba.org> Reviewed-by: Guenther Deschner <gd@samba.org>
903 lines
22 KiB
C
903 lines
22 KiB
C
/*
|
|
* Unix SMB/CIFS implementation.
|
|
* Group Policy Object Support
|
|
* Copyright (C) Guenther Deschner 2005,2007
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "libgpo/gpo.h"
|
|
#include "auth.h"
|
|
#include "../libcli/security/security.h"
|
|
|
|
/****************************************************************
|
|
parse the raw extension string into a GP_EXT structure
|
|
****************************************************************/
|
|
|
|
bool ads_parse_gp_ext(TALLOC_CTX *mem_ctx,
|
|
const char *extension_raw,
|
|
struct GP_EXT **gp_ext)
|
|
{
|
|
bool ret = false;
|
|
struct GP_EXT *ext = NULL;
|
|
char **ext_list = NULL;
|
|
char **ext_strings = NULL;
|
|
int i;
|
|
|
|
if (!extension_raw) {
|
|
goto parse_error;
|
|
}
|
|
|
|
DEBUG(20,("ads_parse_gp_ext: %s\n", extension_raw));
|
|
|
|
ext = talloc_zero(mem_ctx, struct GP_EXT);
|
|
if (!ext) {
|
|
goto parse_error;
|
|
}
|
|
|
|
ext_list = str_list_make(mem_ctx, extension_raw, "]");
|
|
if (!ext_list) {
|
|
goto parse_error;
|
|
}
|
|
|
|
for (i = 0; ext_list[i] != NULL; i++) {
|
|
/* no op */
|
|
}
|
|
|
|
ext->num_exts = i;
|
|
|
|
if (ext->num_exts) {
|
|
ext->extensions = talloc_zero_array(mem_ctx, char *,
|
|
ext->num_exts);
|
|
ext->extensions_guid = talloc_zero_array(mem_ctx, char *,
|
|
ext->num_exts);
|
|
ext->snapins = talloc_zero_array(mem_ctx, char *,
|
|
ext->num_exts);
|
|
ext->snapins_guid = talloc_zero_array(mem_ctx, char *,
|
|
ext->num_exts);
|
|
}
|
|
|
|
ext->gp_extension = talloc_strdup(mem_ctx, extension_raw);
|
|
|
|
if (!ext->extensions || !ext->extensions_guid ||
|
|
!ext->snapins || !ext->snapins_guid ||
|
|
!ext->gp_extension) {
|
|
goto parse_error;
|
|
}
|
|
|
|
for (i = 0; ext_list[i] != NULL; i++) {
|
|
|
|
int k;
|
|
char *p, *q;
|
|
|
|
DEBUGADD(10,("extension #%d\n", i));
|
|
|
|
p = ext_list[i];
|
|
|
|
if (p[0] == '[') {
|
|
p++;
|
|
}
|
|
|
|
ext_strings = str_list_make(mem_ctx, p, "}");
|
|
if (ext_strings == NULL) {
|
|
goto parse_error;
|
|
}
|
|
|
|
for (k = 0; ext_strings[k] != NULL; k++) {
|
|
/* no op */
|
|
}
|
|
|
|
q = ext_strings[0];
|
|
|
|
if (q[0] == '{') {
|
|
q++;
|
|
}
|
|
|
|
ext->extensions[i] = talloc_strdup(mem_ctx,
|
|
cse_gpo_guid_string_to_name(q));
|
|
ext->extensions_guid[i] = talloc_strdup(mem_ctx, q);
|
|
|
|
/* we might have no name for the guid */
|
|
if (ext->extensions_guid[i] == NULL) {
|
|
goto parse_error;
|
|
}
|
|
|
|
for (k = 1; ext_strings[k] != NULL; k++) {
|
|
|
|
char *m = ext_strings[k];
|
|
|
|
if (m[0] == '{') {
|
|
m++;
|
|
}
|
|
|
|
/* FIXME: theoretically there could be more than one
|
|
* snapin per extension */
|
|
ext->snapins[i] = talloc_strdup(mem_ctx,
|
|
cse_snapin_gpo_guid_string_to_name(m));
|
|
ext->snapins_guid[i] = talloc_strdup(mem_ctx, m);
|
|
|
|
/* we might have no name for the guid */
|
|
if (ext->snapins_guid[i] == NULL) {
|
|
goto parse_error;
|
|
}
|
|
}
|
|
}
|
|
|
|
*gp_ext = ext;
|
|
|
|
ret = true;
|
|
|
|
parse_error:
|
|
talloc_free(ext_list);
|
|
talloc_free(ext_strings);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef HAVE_LDAP
|
|
|
|
/****************************************************************
|
|
parse the raw link string into a GP_LINK structure
|
|
****************************************************************/
|
|
|
|
static ADS_STATUS gpo_parse_gplink(TALLOC_CTX *mem_ctx,
|
|
const char *gp_link_raw,
|
|
uint32_t options,
|
|
struct GP_LINK *gp_link)
|
|
{
|
|
ADS_STATUS status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
|
|
char **link_list;
|
|
int i;
|
|
|
|
ZERO_STRUCTP(gp_link);
|
|
|
|
DEBUG(10,("gpo_parse_gplink: gPLink: %s\n", gp_link_raw));
|
|
|
|
link_list = str_list_make_v3(mem_ctx, gp_link_raw, "]");
|
|
if (!link_list) {
|
|
goto parse_error;
|
|
}
|
|
|
|
for (i = 0; link_list[i] != NULL; i++) {
|
|
/* no op */
|
|
}
|
|
|
|
gp_link->gp_opts = options;
|
|
gp_link->num_links = i;
|
|
|
|
if (gp_link->num_links) {
|
|
gp_link->link_names = talloc_zero_array(mem_ctx, char *,
|
|
gp_link->num_links);
|
|
gp_link->link_opts = talloc_zero_array(mem_ctx, uint32_t,
|
|
gp_link->num_links);
|
|
}
|
|
|
|
gp_link->gp_link = talloc_strdup(mem_ctx, gp_link_raw);
|
|
|
|
if (!gp_link->link_names || !gp_link->link_opts || !gp_link->gp_link) {
|
|
goto parse_error;
|
|
}
|
|
|
|
for (i = 0; link_list[i] != NULL; i++) {
|
|
|
|
char *p, *q;
|
|
|
|
DEBUGADD(10,("gpo_parse_gplink: processing link #%d\n", i));
|
|
|
|
q = link_list[i];
|
|
if (q[0] == '[') {
|
|
q++;
|
|
};
|
|
|
|
p = strchr(q, ';');
|
|
|
|
if (p == NULL) {
|
|
goto parse_error;
|
|
}
|
|
|
|
gp_link->link_names[i] = talloc_strdup(mem_ctx, q);
|
|
if (gp_link->link_names[i] == NULL) {
|
|
goto parse_error;
|
|
}
|
|
gp_link->link_names[i][PTR_DIFF(p, q)] = 0;
|
|
|
|
gp_link->link_opts[i] = atoi(p + 1);
|
|
|
|
DEBUGADD(10,("gpo_parse_gplink: link: %s\n",
|
|
gp_link->link_names[i]));
|
|
DEBUGADD(10,("gpo_parse_gplink: opt: %d\n",
|
|
gp_link->link_opts[i]));
|
|
|
|
}
|
|
|
|
status = ADS_SUCCESS;
|
|
|
|
parse_error:
|
|
talloc_free(link_list);
|
|
|
|
return status;
|
|
}
|
|
|
|
/****************************************************************
|
|
helper call to get a GP_LINK structure from a linkdn
|
|
****************************************************************/
|
|
|
|
ADS_STATUS ads_get_gpo_link(ADS_STRUCT *ads,
|
|
TALLOC_CTX *mem_ctx,
|
|
const char *link_dn,
|
|
struct GP_LINK *gp_link_struct)
|
|
{
|
|
ADS_STATUS status;
|
|
const char *attrs[] = {"gPLink", "gPOptions", NULL};
|
|
LDAPMessage *res = NULL;
|
|
const char *gp_link;
|
|
uint32_t gp_options;
|
|
|
|
ZERO_STRUCTP(gp_link_struct);
|
|
|
|
status = ads_search_dn(ads, &res, link_dn, attrs);
|
|
if (!ADS_ERR_OK(status)) {
|
|
DEBUG(10,("ads_get_gpo_link: search failed with %s\n",
|
|
ads_errstr(status)));
|
|
return status;
|
|
}
|
|
|
|
if (ads_count_replies(ads, res) != 1) {
|
|
DEBUG(10,("ads_get_gpo_link: no result\n"));
|
|
ads_msgfree(ads, res);
|
|
return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
|
|
}
|
|
|
|
gp_link = ads_pull_string(ads, mem_ctx, res, "gPLink");
|
|
if (gp_link == NULL) {
|
|
DEBUG(10,("ads_get_gpo_link: no 'gPLink' attribute found\n"));
|
|
ads_msgfree(ads, res);
|
|
return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
|
|
}
|
|
|
|
/* perfectly legal to have no options */
|
|
if (!ads_pull_uint32(ads, res, "gPOptions", &gp_options)) {
|
|
DEBUG(10,("ads_get_gpo_link: "
|
|
"no 'gPOptions' attribute found\n"));
|
|
gp_options = 0;
|
|
}
|
|
|
|
ads_msgfree(ads, res);
|
|
|
|
return gpo_parse_gplink(mem_ctx, gp_link, gp_options, gp_link_struct);
|
|
}
|
|
|
|
/****************************************************************
|
|
helper call to add a gp link
|
|
****************************************************************/
|
|
|
|
ADS_STATUS ads_add_gpo_link(ADS_STRUCT *ads,
|
|
TALLOC_CTX *mem_ctx,
|
|
const char *link_dn,
|
|
const char *gpo_dn,
|
|
uint32_t gpo_opt)
|
|
{
|
|
ADS_STATUS status;
|
|
const char *attrs[] = {"gPLink", NULL};
|
|
LDAPMessage *res = NULL;
|
|
const char *gp_link, *gp_link_new;
|
|
ADS_MODLIST mods;
|
|
|
|
/* although ADS allows one to set anything here, we better check here if
|
|
* the gpo_dn is sane */
|
|
|
|
if (!strnequal(gpo_dn, "LDAP://CN={", strlen("LDAP://CN={")) != 0) {
|
|
return ADS_ERROR(LDAP_INVALID_DN_SYNTAX);
|
|
}
|
|
|
|
status = ads_search_dn(ads, &res, link_dn, attrs);
|
|
if (!ADS_ERR_OK(status)) {
|
|
DEBUG(10,("ads_add_gpo_link: search failed with %s\n",
|
|
ads_errstr(status)));
|
|
return status;
|
|
}
|
|
|
|
if (ads_count_replies(ads, res) != 1) {
|
|
DEBUG(10,("ads_add_gpo_link: no result\n"));
|
|
ads_msgfree(ads, res);
|
|
return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
|
|
}
|
|
|
|
gp_link = ads_pull_string(ads, mem_ctx, res, "gPLink");
|
|
if (gp_link == NULL) {
|
|
gp_link_new = talloc_asprintf(mem_ctx, "[%s;%d]",
|
|
gpo_dn, gpo_opt);
|
|
} else {
|
|
gp_link_new = talloc_asprintf(mem_ctx, "%s[%s;%d]",
|
|
gp_link, gpo_dn, gpo_opt);
|
|
}
|
|
|
|
ads_msgfree(ads, res);
|
|
ADS_ERROR_HAVE_NO_MEMORY(gp_link_new);
|
|
|
|
mods = ads_init_mods(mem_ctx);
|
|
ADS_ERROR_HAVE_NO_MEMORY(mods);
|
|
|
|
status = ads_mod_str(mem_ctx, &mods, "gPLink", gp_link_new);
|
|
if (!ADS_ERR_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
return ads_gen_mod(ads, link_dn, mods);
|
|
}
|
|
|
|
/****************************************************************
|
|
helper call to delete add a gp link
|
|
****************************************************************/
|
|
|
|
/* untested & broken */
|
|
ADS_STATUS ads_delete_gpo_link(ADS_STRUCT *ads,
|
|
TALLOC_CTX *mem_ctx,
|
|
const char *link_dn,
|
|
const char *gpo_dn)
|
|
{
|
|
ADS_STATUS status;
|
|
const char *attrs[] = {"gPLink", NULL};
|
|
LDAPMessage *res = NULL;
|
|
const char *gp_link, *gp_link_new = NULL;
|
|
ADS_MODLIST mods;
|
|
|
|
/* check for a sane gpo_dn */
|
|
if (gpo_dn[0] != '[') {
|
|
DEBUG(10,("ads_delete_gpo_link: first char not: [\n"));
|
|
return ADS_ERROR(LDAP_INVALID_DN_SYNTAX);
|
|
}
|
|
|
|
if (gpo_dn[strlen(gpo_dn)] != ']') {
|
|
DEBUG(10,("ads_delete_gpo_link: last char not: ]\n"));
|
|
return ADS_ERROR(LDAP_INVALID_DN_SYNTAX);
|
|
}
|
|
|
|
status = ads_search_dn(ads, &res, link_dn, attrs);
|
|
if (!ADS_ERR_OK(status)) {
|
|
DEBUG(10,("ads_delete_gpo_link: search failed with %s\n",
|
|
ads_errstr(status)));
|
|
return status;
|
|
}
|
|
|
|
if (ads_count_replies(ads, res) != 1) {
|
|
DEBUG(10,("ads_delete_gpo_link: no result\n"));
|
|
ads_msgfree(ads, res);
|
|
return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
|
|
}
|
|
|
|
gp_link = ads_pull_string(ads, mem_ctx, res, "gPLink");
|
|
if (gp_link == NULL) {
|
|
return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
|
|
}
|
|
|
|
/* find link to delete */
|
|
/* gp_link_new = talloc_asprintf(mem_ctx, "%s[%s;%d]", gp_link,
|
|
gpo_dn, gpo_opt); */
|
|
|
|
ads_msgfree(ads, res);
|
|
ADS_ERROR_HAVE_NO_MEMORY(gp_link_new);
|
|
|
|
mods = ads_init_mods(mem_ctx);
|
|
ADS_ERROR_HAVE_NO_MEMORY(mods);
|
|
|
|
status = ads_mod_str(mem_ctx, &mods, "gPLink", gp_link_new);
|
|
if (!ADS_ERR_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
return ads_gen_mod(ads, link_dn, mods);
|
|
}
|
|
|
|
/****************************************************************
|
|
parse a GROUP_POLICY_OBJECT structure from an LDAPMessage result
|
|
****************************************************************/
|
|
|
|
ADS_STATUS ads_parse_gpo(ADS_STRUCT *ads,
|
|
TALLOC_CTX *mem_ctx,
|
|
LDAPMessage *res,
|
|
const char *gpo_dn,
|
|
struct GROUP_POLICY_OBJECT *gpo)
|
|
{
|
|
ZERO_STRUCTP(gpo);
|
|
|
|
ADS_ERROR_HAVE_NO_MEMORY(res);
|
|
|
|
if (gpo_dn) {
|
|
gpo->ds_path = talloc_strdup(mem_ctx, gpo_dn);
|
|
} else {
|
|
gpo->ds_path = ads_get_dn(ads, mem_ctx, res);
|
|
}
|
|
|
|
ADS_ERROR_HAVE_NO_MEMORY(gpo->ds_path);
|
|
|
|
if (!ads_pull_uint32(ads, res, "versionNumber", &gpo->version)) {
|
|
return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
|
|
}
|
|
|
|
if (!ads_pull_uint32(ads, res, "flags", &gpo->options)) {
|
|
return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
|
|
}
|
|
|
|
gpo->file_sys_path = ads_pull_string(ads, mem_ctx, res,
|
|
"gPCFileSysPath");
|
|
if (gpo->file_sys_path == NULL) {
|
|
return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
|
|
}
|
|
|
|
gpo->display_name = ads_pull_string(ads, mem_ctx, res,
|
|
"displayName");
|
|
if (gpo->display_name == NULL) {
|
|
return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
|
|
}
|
|
|
|
gpo->name = ads_pull_string(ads, mem_ctx, res,
|
|
"name");
|
|
if (gpo->name == NULL) {
|
|
return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
|
|
}
|
|
|
|
gpo->machine_extensions = ads_pull_string(ads, mem_ctx, res,
|
|
"gPCMachineExtensionNames");
|
|
gpo->user_extensions = ads_pull_string(ads, mem_ctx, res,
|
|
"gPCUserExtensionNames");
|
|
|
|
ads_pull_sd(ads, mem_ctx, res, "ntSecurityDescriptor",
|
|
&gpo->security_descriptor);
|
|
if (gpo->security_descriptor == NULL) {
|
|
return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
|
|
}
|
|
|
|
return ADS_ERROR(LDAP_SUCCESS);
|
|
}
|
|
|
|
/****************************************************************
|
|
get a GROUP_POLICY_OBJECT structure based on different input parameters
|
|
****************************************************************/
|
|
|
|
ADS_STATUS ads_get_gpo(ADS_STRUCT *ads,
|
|
TALLOC_CTX *mem_ctx,
|
|
const char *gpo_dn,
|
|
const char *display_name,
|
|
const char *guid_name,
|
|
struct GROUP_POLICY_OBJECT *gpo)
|
|
{
|
|
ADS_STATUS status;
|
|
LDAPMessage *res = NULL;
|
|
char *dn;
|
|
const char *filter;
|
|
const char *attrs[] = {
|
|
"cn",
|
|
"displayName",
|
|
"flags",
|
|
"gPCFileSysPath",
|
|
"gPCFunctionalityVersion",
|
|
"gPCMachineExtensionNames",
|
|
"gPCUserExtensionNames",
|
|
"gPCWQLFilter",
|
|
"name",
|
|
"ntSecurityDescriptor",
|
|
"versionNumber",
|
|
NULL};
|
|
uint32_t sd_flags = SECINFO_DACL;
|
|
|
|
ZERO_STRUCTP(gpo);
|
|
|
|
if (!gpo_dn && !display_name && !guid_name) {
|
|
return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
|
|
}
|
|
|
|
if (gpo_dn) {
|
|
|
|
if (strnequal(gpo_dn, "LDAP://", strlen("LDAP://")) != 0) {
|
|
gpo_dn = gpo_dn + strlen("LDAP://");
|
|
}
|
|
|
|
status = ads_search_retry_dn_sd_flags(ads, &res,
|
|
sd_flags,
|
|
gpo_dn, attrs);
|
|
|
|
} else if (display_name || guid_name) {
|
|
|
|
filter = talloc_asprintf(mem_ctx,
|
|
"(&(objectclass=groupPolicyContainer)(%s=%s))",
|
|
display_name ? "displayName" : "name",
|
|
display_name ? display_name : guid_name);
|
|
ADS_ERROR_HAVE_NO_MEMORY(filter);
|
|
|
|
status = ads_do_search_all_sd_flags(ads, ads->config.bind_path,
|
|
LDAP_SCOPE_SUBTREE, filter,
|
|
attrs, sd_flags, &res);
|
|
}
|
|
|
|
if (!ADS_ERR_OK(status)) {
|
|
DEBUG(10,("ads_get_gpo: search failed with %s\n",
|
|
ads_errstr(status)));
|
|
return status;
|
|
}
|
|
|
|
if (ads_count_replies(ads, res) != 1) {
|
|
DEBUG(10,("ads_get_gpo: no result\n"));
|
|
ads_msgfree(ads, res);
|
|
return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
|
|
}
|
|
|
|
dn = ads_get_dn(ads, mem_ctx, res);
|
|
if (dn == NULL) {
|
|
ads_msgfree(ads, res);
|
|
return ADS_ERROR(LDAP_NO_MEMORY);
|
|
}
|
|
|
|
status = ads_parse_gpo(ads, mem_ctx, res, dn, gpo);
|
|
ads_msgfree(ads, res);
|
|
TALLOC_FREE(dn);
|
|
|
|
return status;
|
|
}
|
|
|
|
/****************************************************************
|
|
add a gplink to the GROUP_POLICY_OBJECT linked list
|
|
****************************************************************/
|
|
|
|
static ADS_STATUS add_gplink_to_gpo_list(ADS_STRUCT *ads,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct GROUP_POLICY_OBJECT **gpo_list,
|
|
const char *link_dn,
|
|
struct GP_LINK *gp_link,
|
|
enum GPO_LINK_TYPE link_type,
|
|
bool only_add_forced_gpos,
|
|
const struct security_token *token)
|
|
{
|
|
ADS_STATUS status;
|
|
uint32_t count;
|
|
|
|
/*
|
|
* Note: DLIST_ADD pushes to the front,
|
|
* so loop from last to first to get the
|
|
* order right.
|
|
*/
|
|
for (count = gp_link->num_links; count > 0; count--) {
|
|
/* NB. Index into arrays is one less than counter. */
|
|
uint32_t i = count - 1;
|
|
struct GROUP_POLICY_OBJECT *new_gpo = NULL;
|
|
|
|
if (gp_link->link_opts[i] & GPO_LINK_OPT_DISABLED) {
|
|
DEBUG(10,("skipping disabled GPO\n"));
|
|
continue;
|
|
}
|
|
|
|
if (only_add_forced_gpos) {
|
|
|
|
if (!(gp_link->link_opts[i] & GPO_LINK_OPT_ENFORCED)) {
|
|
DEBUG(10,("skipping nonenforced GPO link "
|
|
"because GPOPTIONS_BLOCK_INHERITANCE "
|
|
"has been set\n"));
|
|
continue;
|
|
} else {
|
|
DEBUG(10,("adding enforced GPO link although "
|
|
"the GPOPTIONS_BLOCK_INHERITANCE "
|
|
"has been set\n"));
|
|
}
|
|
}
|
|
|
|
new_gpo = talloc_zero(mem_ctx, struct GROUP_POLICY_OBJECT);
|
|
ADS_ERROR_HAVE_NO_MEMORY(new_gpo);
|
|
|
|
status = ads_get_gpo(ads, mem_ctx, gp_link->link_names[i],
|
|
NULL, NULL, new_gpo);
|
|
if (!ADS_ERR_OK(status)) {
|
|
DEBUG(10,("failed to get gpo: %s\n",
|
|
gp_link->link_names[i]));
|
|
if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
|
|
(status.err.rc == LDAP_NO_SUCH_ATTRIBUTE)) {
|
|
DEBUG(10,("skipping empty gpo: %s\n",
|
|
gp_link->link_names[i]));
|
|
talloc_free(new_gpo);
|
|
continue;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
status = ADS_ERROR_NT(gpo_apply_security_filtering(new_gpo,
|
|
token));
|
|
if (!ADS_ERR_OK(status)) {
|
|
DEBUG(10,("skipping GPO \"%s\" as object "
|
|
"has no access to it\n",
|
|
new_gpo->display_name));
|
|
talloc_free(new_gpo);
|
|
continue;
|
|
}
|
|
|
|
new_gpo->link = link_dn;
|
|
new_gpo->link_type = link_type;
|
|
|
|
DLIST_ADD(*gpo_list, new_gpo);
|
|
|
|
DEBUG(10,("add_gplink_to_gplist: added GPLINK #%d %s "
|
|
"to GPO list\n", i, gp_link->link_names[i]));
|
|
}
|
|
|
|
return ADS_ERROR(LDAP_SUCCESS);
|
|
}
|
|
|
|
/****************************************************************
|
|
****************************************************************/
|
|
|
|
ADS_STATUS ads_get_sid_token(ADS_STRUCT *ads,
|
|
TALLOC_CTX *mem_ctx,
|
|
const char *dn,
|
|
struct security_token **token)
|
|
{
|
|
ADS_STATUS status;
|
|
struct dom_sid object_sid;
|
|
struct dom_sid primary_group_sid;
|
|
struct dom_sid *ad_token_sids;
|
|
size_t num_ad_token_sids = 0;
|
|
struct dom_sid *token_sids;
|
|
uint32_t num_token_sids = 0;
|
|
struct security_token *new_token = NULL;
|
|
int i;
|
|
|
|
status = ads_get_tokensids(ads, mem_ctx, dn,
|
|
&object_sid, &primary_group_sid,
|
|
&ad_token_sids, &num_ad_token_sids);
|
|
if (!ADS_ERR_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
token_sids = talloc_array(mem_ctx, struct dom_sid, 1);
|
|
ADS_ERROR_HAVE_NO_MEMORY(token_sids);
|
|
|
|
status = ADS_ERROR_NT(add_sid_to_array_unique(mem_ctx,
|
|
&primary_group_sid,
|
|
&token_sids,
|
|
&num_token_sids));
|
|
if (!ADS_ERR_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
for (i = 0; i < num_ad_token_sids; i++) {
|
|
|
|
if (sid_check_is_in_builtin(&ad_token_sids[i])) {
|
|
continue;
|
|
}
|
|
|
|
status = ADS_ERROR_NT(add_sid_to_array_unique(mem_ctx,
|
|
&ad_token_sids[i],
|
|
&token_sids,
|
|
&num_token_sids));
|
|
if (!ADS_ERR_OK(status)) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
new_token = create_local_nt_token(mem_ctx, &object_sid, false,
|
|
num_token_sids, token_sids);
|
|
ADS_ERROR_HAVE_NO_MEMORY(new_token);
|
|
|
|
*token = new_token;
|
|
|
|
security_token_debug(DBGC_CLASS, 5, *token);
|
|
|
|
return ADS_ERROR_LDAP(LDAP_SUCCESS);
|
|
}
|
|
|
|
/****************************************************************
|
|
****************************************************************/
|
|
|
|
static ADS_STATUS add_local_policy_to_gpo_list(TALLOC_CTX *mem_ctx,
|
|
struct GROUP_POLICY_OBJECT **gpo_list,
|
|
enum GPO_LINK_TYPE link_type)
|
|
{
|
|
struct GROUP_POLICY_OBJECT *gpo = NULL;
|
|
|
|
ADS_ERROR_HAVE_NO_MEMORY(gpo_list);
|
|
|
|
gpo = talloc_zero(mem_ctx, struct GROUP_POLICY_OBJECT);
|
|
ADS_ERROR_HAVE_NO_MEMORY(gpo);
|
|
|
|
gpo->name = talloc_strdup(mem_ctx, "Local Policy");
|
|
ADS_ERROR_HAVE_NO_MEMORY(gpo->name);
|
|
|
|
gpo->display_name = talloc_strdup(mem_ctx, "Local Policy");
|
|
ADS_ERROR_HAVE_NO_MEMORY(gpo->display_name);
|
|
|
|
gpo->link_type = link_type;
|
|
|
|
DLIST_ADD(*gpo_list, gpo);
|
|
|
|
return ADS_ERROR_NT(NT_STATUS_OK);
|
|
}
|
|
|
|
/****************************************************************
|
|
get the full list of GROUP_POLICY_OBJECTs for a given dn
|
|
****************************************************************/
|
|
|
|
ADS_STATUS ads_get_gpo_list(ADS_STRUCT *ads,
|
|
TALLOC_CTX *mem_ctx,
|
|
const char *dn,
|
|
uint32_t flags,
|
|
const struct security_token *token,
|
|
struct GROUP_POLICY_OBJECT **gpo_list)
|
|
{
|
|
/*
|
|
* Push GPOs to gpo_list so that the traversal order of the list matches
|
|
* the order of application:
|
|
* (L)ocal (S)ite (D)omain (O)rganizational(U)nit
|
|
* Within domains and OUs: parent-to-child.
|
|
* Since GPOs are pushed to the front of gpo_list, GPOs have to be
|
|
* pushed in the opposite order of application (OUs first, local last,
|
|
* child-to-parent).
|
|
*/
|
|
|
|
ADS_STATUS status;
|
|
struct GP_LINK gp_link;
|
|
const char *parent_dn, *site_dn, *tmp_dn;
|
|
bool add_only_forced_gpos = false;
|
|
|
|
ZERO_STRUCTP(gpo_list);
|
|
|
|
if (!dn) {
|
|
return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (!ads_set_sasl_wrap_flags(ads, ADS_AUTH_SASL_SIGN)) {
|
|
return ADS_ERROR(LDAP_INVALID_CREDENTIALS);
|
|
}
|
|
|
|
DEBUG(10,("ads_get_gpo_list: getting GPO list for [%s]\n", dn));
|
|
|
|
tmp_dn = dn;
|
|
|
|
while ((parent_dn = ads_parent_dn(tmp_dn)) &&
|
|
(!strequal(parent_dn, ads_parent_dn(ads->config.bind_path)))) {
|
|
|
|
|
|
/* (O)rganizational(U)nit */
|
|
|
|
/* An account can be a member of more OUs */
|
|
if (strncmp(parent_dn, "OU=", strlen("OU=")) == 0) {
|
|
|
|
DEBUG(10,("ads_get_gpo_list: query OU: [%s] for GPOs\n",
|
|
parent_dn));
|
|
|
|
status = ads_get_gpo_link(ads, mem_ctx, parent_dn,
|
|
&gp_link);
|
|
if (ADS_ERR_OK(status)) {
|
|
|
|
if (DEBUGLEVEL >= 100) {
|
|
dump_gplink(&gp_link);
|
|
}
|
|
|
|
status = add_gplink_to_gpo_list(ads,
|
|
mem_ctx,
|
|
gpo_list,
|
|
parent_dn,
|
|
&gp_link,
|
|
GP_LINK_OU,
|
|
add_only_forced_gpos,
|
|
token);
|
|
if (!ADS_ERR_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
/* block inheritance from now on */
|
|
if (gp_link.gp_opts &
|
|
GPOPTIONS_BLOCK_INHERITANCE) {
|
|
add_only_forced_gpos = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
tmp_dn = parent_dn;
|
|
|
|
}
|
|
|
|
/* reset dn again */
|
|
tmp_dn = dn;
|
|
|
|
while ((parent_dn = ads_parent_dn(tmp_dn)) &&
|
|
(!strequal(parent_dn, ads_parent_dn(ads->config.bind_path)))) {
|
|
|
|
/* (D)omain */
|
|
|
|
/* An account can just be a member of one domain */
|
|
if (strncmp(parent_dn, "DC=", strlen("DC=")) == 0) {
|
|
|
|
DEBUG(10,("ads_get_gpo_list: query DC: [%s] for GPOs\n",
|
|
parent_dn));
|
|
|
|
status = ads_get_gpo_link(ads, mem_ctx, parent_dn,
|
|
&gp_link);
|
|
if (ADS_ERR_OK(status)) {
|
|
|
|
if (DEBUGLEVEL >= 100) {
|
|
dump_gplink(&gp_link);
|
|
}
|
|
|
|
status = add_gplink_to_gpo_list(ads,
|
|
mem_ctx,
|
|
gpo_list,
|
|
parent_dn,
|
|
&gp_link,
|
|
GP_LINK_DOMAIN,
|
|
add_only_forced_gpos,
|
|
token);
|
|
if (!ADS_ERR_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
/* block inheritance from now on */
|
|
if (gp_link.gp_opts &
|
|
GPOPTIONS_BLOCK_INHERITANCE) {
|
|
add_only_forced_gpos = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
tmp_dn = parent_dn;
|
|
}
|
|
|
|
/* (S)ite */
|
|
|
|
/* are site GPOs valid for users as well ??? */
|
|
if (flags & GPO_LIST_FLAG_MACHINE) {
|
|
|
|
status = ads_site_dn_for_machine(ads, mem_ctx,
|
|
ads->config.ldap_server_name,
|
|
&site_dn);
|
|
if (!ADS_ERR_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
DEBUG(10,("ads_get_gpo_list: query SITE: [%s] for GPOs\n",
|
|
site_dn));
|
|
|
|
status = ads_get_gpo_link(ads, mem_ctx, site_dn, &gp_link);
|
|
if (ADS_ERR_OK(status)) {
|
|
|
|
if (DEBUGLEVEL >= 100) {
|
|
dump_gplink(&gp_link);
|
|
}
|
|
|
|
status = add_gplink_to_gpo_list(ads, mem_ctx, gpo_list,
|
|
site_dn, &gp_link,
|
|
GP_LINK_SITE,
|
|
add_only_forced_gpos,
|
|
token);
|
|
if (!ADS_ERR_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (flags & GPO_LIST_FLAG_SITEONLY) {
|
|
return ADS_ERROR(LDAP_SUCCESS);
|
|
}
|
|
|
|
/* inheritance can't be blocked at the site level */
|
|
}
|
|
}
|
|
|
|
/* (L)ocal */
|
|
status = add_local_policy_to_gpo_list(mem_ctx, gpo_list,
|
|
GP_LINK_LOCAL);
|
|
if (!ADS_ERR_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
return ADS_ERROR(LDAP_SUCCESS);
|
|
}
|
|
|
|
#endif /* HAVE_LDAP */
|