2 * Unix SMB/CIFS implementation.
3 * Group Policy Object Support
4 * Copyright (C) Guenther Deschner 2005,2007
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "libgpo/gpo.h"
23 #include "../libcli/security/security.h"
25 /****************************************************************
26 parse the raw extension string into a GP_EXT structure
27 ****************************************************************/
29 bool ads_parse_gp_ext(TALLOC_CTX *mem_ctx,
30 const char *extension_raw,
31 struct GP_EXT **gp_ext)
34 struct GP_EXT *ext = NULL;
35 char **ext_list = NULL;
36 char **ext_strings = NULL;
43 DEBUG(20,("ads_parse_gp_ext: %s\n", extension_raw));
45 ext = talloc_zero(mem_ctx, struct GP_EXT);
50 ext_list = str_list_make(mem_ctx, extension_raw, "]");
55 for (i = 0; ext_list[i] != NULL; i++) {
62 ext->extensions = talloc_zero_array(mem_ctx, char *,
64 ext->extensions_guid = talloc_zero_array(mem_ctx, char *,
66 ext->snapins = talloc_zero_array(mem_ctx, char *,
68 ext->snapins_guid = talloc_zero_array(mem_ctx, char *,
72 ext->gp_extension = talloc_strdup(mem_ctx, extension_raw);
74 if (!ext->extensions || !ext->extensions_guid ||
75 !ext->snapins || !ext->snapins_guid ||
80 for (i = 0; ext_list[i] != NULL; i++) {
85 DEBUGADD(10,("extension #%d\n", i));
93 ext_strings = str_list_make(mem_ctx, p, "}");
94 if (ext_strings == NULL) {
98 for (k = 0; ext_strings[k] != NULL; k++) {
110 ext->extensions[i] = talloc_strdup(mem_ctx,
111 cse_gpo_guid_string_to_name(q));
112 ext->extensions_guid[i] = talloc_strdup(mem_ctx, q);
114 /* we might have no name for the guid */
115 if (ext->extensions_guid[i] == NULL) {
119 for (k = 1; ext_strings[k] != NULL; k++) {
121 char *m = ext_strings[k];
127 /* FIXME: theoretically there could be more than one
128 * snapin per extension */
129 ext->snapins[i] = talloc_strdup(mem_ctx,
130 cse_snapin_gpo_guid_string_to_name(m));
131 ext->snapins_guid[i] = talloc_strdup(mem_ctx, m);
133 /* we might have no name for the guid */
134 if (ext->snapins_guid[i] == NULL) {
145 talloc_free(ext_list);
146 talloc_free(ext_strings);
153 /****************************************************************
154 parse the raw link string into a GP_LINK structure
155 ****************************************************************/
157 static ADS_STATUS gpo_parse_gplink(TALLOC_CTX *mem_ctx,
158 const char *gp_link_raw,
160 struct GP_LINK *gp_link)
162 ADS_STATUS status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
166 ZERO_STRUCTP(gp_link);
168 DEBUG(10,("gpo_parse_gplink: gPLink: %s\n", gp_link_raw));
170 link_list = str_list_make_v3(mem_ctx, gp_link_raw, "]");
175 for (i = 0; link_list[i] != NULL; i++) {
179 gp_link->gp_opts = options;
180 gp_link->num_links = i;
182 if (gp_link->num_links) {
183 gp_link->link_names = talloc_zero_array(mem_ctx, char *,
185 gp_link->link_opts = talloc_zero_array(mem_ctx, uint32_t,
189 gp_link->gp_link = talloc_strdup(mem_ctx, gp_link_raw);
191 if (!gp_link->link_names || !gp_link->link_opts || !gp_link->gp_link) {
195 for (i = 0; link_list[i] != NULL; i++) {
199 DEBUGADD(10,("gpo_parse_gplink: processing link #%d\n", i));
212 gp_link->link_names[i] = talloc_strdup(mem_ctx, q);
213 if (gp_link->link_names[i] == NULL) {
216 gp_link->link_names[i][PTR_DIFF(p, q)] = 0;
218 gp_link->link_opts[i] = atoi(p + 1);
220 DEBUGADD(10,("gpo_parse_gplink: link: %s\n",
221 gp_link->link_names[i]));
222 DEBUGADD(10,("gpo_parse_gplink: opt: %d\n",
223 gp_link->link_opts[i]));
227 status = ADS_SUCCESS;
230 talloc_free(link_list);
235 /****************************************************************
236 helper call to get a GP_LINK structure from a linkdn
237 ****************************************************************/
239 ADS_STATUS ads_get_gpo_link(ADS_STRUCT *ads,
242 struct GP_LINK *gp_link_struct)
245 const char *attrs[] = {"gPLink", "gPOptions", NULL};
246 LDAPMessage *res = NULL;
250 ZERO_STRUCTP(gp_link_struct);
252 status = ads_search_dn(ads, &res, link_dn, attrs);
253 if (!ADS_ERR_OK(status)) {
254 DEBUG(10,("ads_get_gpo_link: search failed with %s\n",
255 ads_errstr(status)));
259 if (ads_count_replies(ads, res) != 1) {
260 DEBUG(10,("ads_get_gpo_link: no result\n"));
261 ads_msgfree(ads, res);
262 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
265 gp_link = ads_pull_string(ads, mem_ctx, res, "gPLink");
266 if (gp_link == NULL) {
267 DEBUG(10,("ads_get_gpo_link: no 'gPLink' attribute found\n"));
268 ads_msgfree(ads, res);
269 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
272 /* perfectly legal to have no options */
273 if (!ads_pull_uint32(ads, res, "gPOptions", &gp_options)) {
274 DEBUG(10,("ads_get_gpo_link: "
275 "no 'gPOptions' attribute found\n"));
279 ads_msgfree(ads, res);
281 return gpo_parse_gplink(mem_ctx, gp_link, gp_options, gp_link_struct);
284 /****************************************************************
285 helper call to add a gp link
286 ****************************************************************/
288 ADS_STATUS ads_add_gpo_link(ADS_STRUCT *ads,
295 const char *attrs[] = {"gPLink", NULL};
296 LDAPMessage *res = NULL;
297 const char *gp_link, *gp_link_new;
300 /* although ADS allows one to set anything here, we better check here if
301 * the gpo_dn is sane */
303 if (!strnequal(gpo_dn, "LDAP://CN={", strlen("LDAP://CN={")) != 0) {
304 return ADS_ERROR(LDAP_INVALID_DN_SYNTAX);
307 status = ads_search_dn(ads, &res, link_dn, attrs);
308 if (!ADS_ERR_OK(status)) {
309 DEBUG(10,("ads_add_gpo_link: search failed with %s\n",
310 ads_errstr(status)));
314 if (ads_count_replies(ads, res) != 1) {
315 DEBUG(10,("ads_add_gpo_link: no result\n"));
316 ads_msgfree(ads, res);
317 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
320 gp_link = ads_pull_string(ads, mem_ctx, res, "gPLink");
321 if (gp_link == NULL) {
322 gp_link_new = talloc_asprintf(mem_ctx, "[%s;%d]",
325 gp_link_new = talloc_asprintf(mem_ctx, "%s[%s;%d]",
326 gp_link, gpo_dn, gpo_opt);
329 ads_msgfree(ads, res);
330 ADS_ERROR_HAVE_NO_MEMORY(gp_link_new);
332 mods = ads_init_mods(mem_ctx);
333 ADS_ERROR_HAVE_NO_MEMORY(mods);
335 status = ads_mod_str(mem_ctx, &mods, "gPLink", gp_link_new);
336 if (!ADS_ERR_OK(status)) {
340 return ads_gen_mod(ads, link_dn, mods);
343 /****************************************************************
344 helper call to delete add a gp link
345 ****************************************************************/
347 /* untested & broken */
348 ADS_STATUS ads_delete_gpo_link(ADS_STRUCT *ads,
354 const char *attrs[] = {"gPLink", NULL};
355 LDAPMessage *res = NULL;
356 const char *gp_link, *gp_link_new = NULL;
359 /* check for a sane gpo_dn */
360 if (gpo_dn[0] != '[') {
361 DEBUG(10,("ads_delete_gpo_link: first char not: [\n"));
362 return ADS_ERROR(LDAP_INVALID_DN_SYNTAX);
365 if (gpo_dn[strlen(gpo_dn)] != ']') {
366 DEBUG(10,("ads_delete_gpo_link: last char not: ]\n"));
367 return ADS_ERROR(LDAP_INVALID_DN_SYNTAX);
370 status = ads_search_dn(ads, &res, link_dn, attrs);
371 if (!ADS_ERR_OK(status)) {
372 DEBUG(10,("ads_delete_gpo_link: search failed with %s\n",
373 ads_errstr(status)));
377 if (ads_count_replies(ads, res) != 1) {
378 DEBUG(10,("ads_delete_gpo_link: no result\n"));
379 ads_msgfree(ads, res);
380 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
383 gp_link = ads_pull_string(ads, mem_ctx, res, "gPLink");
384 if (gp_link == NULL) {
385 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
388 /* find link to delete */
389 /* gp_link_new = talloc_asprintf(mem_ctx, "%s[%s;%d]", gp_link,
392 ads_msgfree(ads, res);
393 ADS_ERROR_HAVE_NO_MEMORY(gp_link_new);
395 mods = ads_init_mods(mem_ctx);
396 ADS_ERROR_HAVE_NO_MEMORY(mods);
398 status = ads_mod_str(mem_ctx, &mods, "gPLink", gp_link_new);
399 if (!ADS_ERR_OK(status)) {
403 return ads_gen_mod(ads, link_dn, mods);
406 /****************************************************************
407 parse a GROUP_POLICY_OBJECT structure from an LDAPMessage result
408 ****************************************************************/
410 ADS_STATUS ads_parse_gpo(ADS_STRUCT *ads,
414 struct GROUP_POLICY_OBJECT *gpo)
418 ADS_ERROR_HAVE_NO_MEMORY(res);
421 gpo->ds_path = talloc_strdup(mem_ctx, gpo_dn);
423 gpo->ds_path = ads_get_dn(ads, mem_ctx, res);
426 ADS_ERROR_HAVE_NO_MEMORY(gpo->ds_path);
428 if (!ads_pull_uint32(ads, res, "versionNumber", &gpo->version)) {
429 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
432 if (!ads_pull_uint32(ads, res, "flags", &gpo->options)) {
433 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
436 gpo->file_sys_path = ads_pull_string(ads, mem_ctx, res,
438 if (gpo->file_sys_path == NULL) {
439 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
442 gpo->display_name = ads_pull_string(ads, mem_ctx, res,
444 if (gpo->display_name == NULL) {
445 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
448 gpo->name = ads_pull_string(ads, mem_ctx, res,
450 if (gpo->name == NULL) {
451 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
454 gpo->machine_extensions = ads_pull_string(ads, mem_ctx, res,
455 "gPCMachineExtensionNames");
456 gpo->user_extensions = ads_pull_string(ads, mem_ctx, res,
457 "gPCUserExtensionNames");
459 ads_pull_sd(ads, mem_ctx, res, "ntSecurityDescriptor",
460 &gpo->security_descriptor);
461 if (gpo->security_descriptor == NULL) {
462 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
465 return ADS_ERROR(LDAP_SUCCESS);
468 /****************************************************************
469 get a GROUP_POLICY_OBJECT structure based on different input parameters
470 ****************************************************************/
472 ADS_STATUS ads_get_gpo(ADS_STRUCT *ads,
475 const char *display_name,
476 const char *guid_name,
477 struct GROUP_POLICY_OBJECT *gpo)
479 ADS_STATUS status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
480 LDAPMessage *res = NULL;
483 const char *attrs[] = {
488 "gPCFunctionalityVersion",
489 "gPCMachineExtensionNames",
490 "gPCUserExtensionNames",
493 "ntSecurityDescriptor",
496 uint32_t sd_flags = SECINFO_DACL;
500 if (!gpo_dn && !display_name && !guid_name) {
501 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
506 if (strnequal(gpo_dn, "LDAP://", strlen("LDAP://")) != 0) {
507 gpo_dn = gpo_dn + strlen("LDAP://");
510 status = ads_search_retry_dn_sd_flags(ads, &res,
514 } else if (display_name || guid_name) {
516 filter = talloc_asprintf(mem_ctx,
517 "(&(objectclass=groupPolicyContainer)(%s=%s))",
518 display_name ? "displayName" : "name",
519 display_name ? display_name : guid_name);
520 ADS_ERROR_HAVE_NO_MEMORY(filter);
522 status = ads_do_search_all_sd_flags(ads, ads->config.bind_path,
523 LDAP_SCOPE_SUBTREE, filter,
524 attrs, sd_flags, &res);
527 if (!ADS_ERR_OK(status)) {
528 DEBUG(10,("ads_get_gpo: search failed with %s\n",
529 ads_errstr(status)));
533 if (ads_count_replies(ads, res) != 1) {
534 DEBUG(10,("ads_get_gpo: no result\n"));
535 ads_msgfree(ads, res);
536 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
539 dn = ads_get_dn(ads, mem_ctx, res);
541 ads_msgfree(ads, res);
542 return ADS_ERROR(LDAP_NO_MEMORY);
545 status = ads_parse_gpo(ads, mem_ctx, res, dn, gpo);
546 ads_msgfree(ads, res);
552 /****************************************************************
553 add a gplink to the GROUP_POLICY_OBJECT linked list
554 ****************************************************************/
556 static ADS_STATUS add_gplink_to_gpo_list(ADS_STRUCT *ads,
558 struct GROUP_POLICY_OBJECT **gpo_list,
559 struct GROUP_POLICY_OBJECT **forced_gpo_list,
561 struct GP_LINK *gp_link,
562 enum GPO_LINK_TYPE link_type,
563 bool only_add_forced_gpos,
564 const struct security_token *token)
570 * Note: DLIST_ADD pushes to the front,
571 * so loop from last to first to get the
574 for (count = gp_link->num_links; count > 0; count--) {
575 /* NB. Index into arrays is one less than counter. */
576 uint32_t i = count - 1;
577 struct GROUP_POLICY_OBJECT **target_list = NULL;
578 struct GROUP_POLICY_OBJECT *new_gpo = NULL;
580 (gp_link->link_opts[i] & GPO_LINK_OPT_ENFORCED) != 0;
582 if (gp_link->link_opts[i] & GPO_LINK_OPT_DISABLED) {
583 DEBUG(10,("skipping disabled GPO\n"));
587 if (only_add_forced_gpos) {
590 DEBUG(10,("skipping nonenforced GPO link "
591 "because GPOPTIONS_BLOCK_INHERITANCE "
595 DEBUG(10,("adding enforced GPO link although "
596 "the GPOPTIONS_BLOCK_INHERITANCE "
601 new_gpo = talloc_zero(mem_ctx, struct GROUP_POLICY_OBJECT);
602 ADS_ERROR_HAVE_NO_MEMORY(new_gpo);
604 status = ads_get_gpo(ads, mem_ctx, gp_link->link_names[i],
605 NULL, NULL, new_gpo);
606 if (!ADS_ERR_OK(status)) {
607 DEBUG(10,("failed to get gpo: %s\n",
608 gp_link->link_names[i]));
609 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
610 (status.err.rc == LDAP_NO_SUCH_ATTRIBUTE)) {
611 DEBUG(10,("skipping empty gpo: %s\n",
612 gp_link->link_names[i]));
613 talloc_free(new_gpo);
619 status = ADS_ERROR_NT(gpo_apply_security_filtering(new_gpo,
621 if (!ADS_ERR_OK(status)) {
622 DEBUG(10,("skipping GPO \"%s\" as object "
623 "has no access to it\n",
624 new_gpo->display_name));
625 talloc_free(new_gpo);
629 new_gpo->link = link_dn;
630 new_gpo->link_type = link_type;
632 target_list = is_forced ? forced_gpo_list : gpo_list;
633 DLIST_ADD(*target_list, new_gpo);
635 DEBUG(10,("add_gplink_to_gplist: added GPLINK #%d %s "
636 "to GPO list\n", i, gp_link->link_names[i]));
639 return ADS_ERROR(LDAP_SUCCESS);
642 /****************************************************************
643 ****************************************************************/
645 ADS_STATUS ads_get_sid_token(ADS_STRUCT *ads,
648 struct security_token **token)
651 struct dom_sid object_sid;
652 struct dom_sid primary_group_sid;
653 struct dom_sid *ad_token_sids;
654 size_t num_ad_token_sids = 0;
655 struct dom_sid *token_sids;
656 uint32_t num_token_sids = 0;
657 struct security_token *new_token = NULL;
660 status = ads_get_tokensids(ads, mem_ctx, dn,
661 &object_sid, &primary_group_sid,
662 &ad_token_sids, &num_ad_token_sids);
663 if (!ADS_ERR_OK(status)) {
667 token_sids = talloc_array(mem_ctx, struct dom_sid, 1);
668 ADS_ERROR_HAVE_NO_MEMORY(token_sids);
670 status = ADS_ERROR_NT(add_sid_to_array_unique(mem_ctx,
674 if (!ADS_ERR_OK(status)) {
678 for (i = 0; i < num_ad_token_sids; i++) {
680 if (sid_check_is_in_builtin(&ad_token_sids[i])) {
684 status = ADS_ERROR_NT(add_sid_to_array_unique(mem_ctx,
688 if (!ADS_ERR_OK(status)) {
693 status = ADS_ERROR_NT(create_local_nt_token(mem_ctx,
695 num_token_sids, token_sids, &new_token));
696 if (!ADS_ERR_OK(status)) {
702 security_token_debug(DBGC_CLASS, 5, *token);
704 return ADS_ERROR_LDAP(LDAP_SUCCESS);
707 /****************************************************************
708 ****************************************************************/
710 static ADS_STATUS add_local_policy_to_gpo_list(TALLOC_CTX *mem_ctx,
711 struct GROUP_POLICY_OBJECT **gpo_list,
712 enum GPO_LINK_TYPE link_type)
714 struct GROUP_POLICY_OBJECT *gpo = NULL;
716 ADS_ERROR_HAVE_NO_MEMORY(gpo_list);
718 gpo = talloc_zero(mem_ctx, struct GROUP_POLICY_OBJECT);
719 ADS_ERROR_HAVE_NO_MEMORY(gpo);
721 gpo->name = talloc_strdup(mem_ctx, "Local Policy");
722 ADS_ERROR_HAVE_NO_MEMORY(gpo->name);
724 gpo->display_name = talloc_strdup(mem_ctx, "Local Policy");
725 ADS_ERROR_HAVE_NO_MEMORY(gpo->display_name);
727 gpo->link_type = link_type;
729 DLIST_ADD(*gpo_list, gpo);
731 return ADS_ERROR_NT(NT_STATUS_OK);
734 /****************************************************************
735 Get the full list of GROUP_POLICY_OBJECTs for a given dn.
736 ****************************************************************/
738 static ADS_STATUS ads_get_gpo_list_internal(ADS_STRUCT *ads,
742 const struct security_token *token,
743 struct GROUP_POLICY_OBJECT **gpo_list,
744 struct GROUP_POLICY_OBJECT **forced_gpo_list)
747 * Push GPOs to gpo_list so that the traversal order of the list matches
748 * the order of application:
749 * (L)ocal (S)ite (D)omain (O)rganizational(U)nit
750 * For different domains and OUs: parent-to-child.
751 * Within same level of domains and OUs: Link order.
752 * Since GPOs are pushed to the front of gpo_list, GPOs have to be
753 * pushed in the opposite order of application (OUs first, local last,
755 * Forced GPOs are appended in the end since they override all others.
759 struct GP_LINK gp_link;
760 const char *parent_dn, *site_dn, *tmp_dn;
761 bool add_only_forced_gpos = false;
763 ZERO_STRUCTP(gpo_list);
764 ZERO_STRUCTP(forced_gpo_list);
767 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
770 if (!ads_set_sasl_wrap_flags(ads, ADS_AUTH_SASL_SIGN)) {
771 return ADS_ERROR(LDAP_INVALID_CREDENTIALS);
774 DEBUG(10,("ads_get_gpo_list: getting GPO list for [%s]\n", dn));
778 while ((parent_dn = ads_parent_dn(tmp_dn)) &&
779 (!strequal(parent_dn, ads_parent_dn(ads->config.bind_path)))) {
782 /* (O)rganizational(U)nit */
784 /* An account can be a member of more OUs */
785 if (strncmp(parent_dn, "OU=", strlen("OU=")) == 0) {
787 DEBUG(10,("ads_get_gpo_list: query OU: [%s] for GPOs\n",
790 status = ads_get_gpo_link(ads, mem_ctx, parent_dn,
792 if (ADS_ERR_OK(status)) {
794 if (DEBUGLEVEL >= 100) {
795 dump_gplink(&gp_link);
798 status = add_gplink_to_gpo_list(ads,
805 add_only_forced_gpos,
807 if (!ADS_ERR_OK(status)) {
811 /* block inheritance from now on */
812 if (gp_link.gp_opts &
813 GPOPTIONS_BLOCK_INHERITANCE) {
814 add_only_forced_gpos = true;
826 while ((parent_dn = ads_parent_dn(tmp_dn)) &&
827 (!strequal(parent_dn, ads_parent_dn(ads->config.bind_path)))) {
831 /* An account can just be a member of one domain */
832 if (strncmp(parent_dn, "DC=", strlen("DC=")) == 0) {
834 DEBUG(10,("ads_get_gpo_list: query DC: [%s] for GPOs\n",
837 status = ads_get_gpo_link(ads, mem_ctx, parent_dn,
839 if (ADS_ERR_OK(status)) {
841 if (DEBUGLEVEL >= 100) {
842 dump_gplink(&gp_link);
845 status = add_gplink_to_gpo_list(ads,
852 add_only_forced_gpos,
854 if (!ADS_ERR_OK(status)) {
858 /* block inheritance from now on */
859 if (gp_link.gp_opts &
860 GPOPTIONS_BLOCK_INHERITANCE) {
861 add_only_forced_gpos = true;
871 /* are site GPOs valid for users as well ??? */
872 if (flags & GPO_LIST_FLAG_MACHINE) {
874 status = ads_site_dn_for_machine(ads, mem_ctx,
875 ads->config.ldap_server_name,
877 if (!ADS_ERR_OK(status)) {
881 DEBUG(10,("ads_get_gpo_list: query SITE: [%s] for GPOs\n",
884 status = ads_get_gpo_link(ads, mem_ctx, site_dn, &gp_link);
885 if (ADS_ERR_OK(status)) {
887 if (DEBUGLEVEL >= 100) {
888 dump_gplink(&gp_link);
891 status = add_gplink_to_gpo_list(ads,
898 add_only_forced_gpos,
900 if (!ADS_ERR_OK(status)) {
904 if (flags & GPO_LIST_FLAG_SITEONLY) {
905 return ADS_ERROR(LDAP_SUCCESS);
908 /* inheritance can't be blocked at the site level */
913 status = add_local_policy_to_gpo_list(mem_ctx, gpo_list,
915 if (!ADS_ERR_OK(status)) {
919 return ADS_ERROR(LDAP_SUCCESS);
922 /****************************************************************
923 Get the full list of GROUP_POLICY_OBJECTs for a given dn, wrapper
924 around ads_get_gpo_list_internal() that ensures correct ordering.
925 ****************************************************************/
927 ADS_STATUS ads_get_gpo_list(ADS_STRUCT *ads,
931 const struct security_token *token,
932 struct GROUP_POLICY_OBJECT **gpo_list)
934 struct GROUP_POLICY_OBJECT *forced_gpo_list = NULL;
937 status = ads_get_gpo_list_internal(ads,
944 if (!ADS_ERR_OK(status)) {
948 * Append |forced_gpo_list| at the end of |gpo_list|,
949 * so that forced GPOs are applied on top of non enforced GPOs.
951 DLIST_CONCATENATE(*gpo_list, forced_gpo_list);
953 return ADS_ERROR(LDAP_SUCCESS);
956 #endif /* HAVE_LDAP */