samr: for correctness, rename samr_RidTypeArray to samr_RidAttrArray.
[mat/samba.git] / source3 / lib / netapi / group.c
index ebaf3660ec26dcea3104f768e4020e7828f4898e..bf8d1667d61827e8687d98f42d75c62be563e5d9 100644 (file)
@@ -23,6 +23,9 @@
 #include "lib/netapi/netapi.h"
 #include "lib/netapi/netapi_private.h"
 #include "lib/netapi/libnetapi.h"
+#include "../librpc/gen_ndr/cli_samr.h"
+#include "rpc_client/init_lsa.h"
+#include "../libcli/security/dom_sid.h"
 
 /****************************************************************
 ****************************************************************/
 WERROR NetGroupAdd_r(struct libnetapi_ctx *ctx,
                     struct NetGroupAdd *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
-       POLICY_HND connect_handle, domain_handle, group_handle;
+       struct policy_handle connect_handle, domain_handle, group_handle;
        struct lsa_String lsa_group_name;
        struct dom_sid2 *domain_sid = NULL;
        uint32_t rid = 0;
@@ -49,41 +51,38 @@ WERROR NetGroupAdd_r(struct libnetapi_ctx *ctx,
        ZERO_STRUCT(domain_handle);
        ZERO_STRUCT(group_handle);
 
-       if (!r->in.buf) {
+       if (!r->in.buffer) {
                return WERR_INVALID_PARAM;
        }
 
        switch (r->in.level) {
                case 0:
-                       info0 = (struct GROUP_INFO_0 *)r->in.buf;
+                       info0 = (struct GROUP_INFO_0 *)r->in.buffer;
                        break;
                case 1:
-                       info1 = (struct GROUP_INFO_1 *)r->in.buf;
+                       info1 = (struct GROUP_INFO_1 *)r->in.buffer;
                        break;
                case 2:
-                       info2 = (struct GROUP_INFO_2 *)r->in.buf;
+                       info2 = (struct GROUP_INFO_2 *)r->in.buffer;
                        break;
                case 3:
-                       info3 = (struct GROUP_INFO_3 *)r->in.buf;
+                       info3 = (struct GROUP_INFO_3 *)r->in.buffer;
                        break;
                default:
                        werr = WERR_UNKNOWN_LEVEL;
                        goto done;
        }
 
-       werr = libnetapi_open_ipc_connection(ctx, r->in.server_name, &cli);
-       if (!W_ERROR_IS_OK(werr)) {
-               goto done;
-       }
-
-       werr = libnetapi_open_pipe(ctx, cli, PI_SAMR, &pipe_cli);
+       werr = libnetapi_open_pipe(ctx, r->in.server_name,
+                                  &ndr_table_samr.syntax_id,
+                                  &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
        }
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_CREATE_GROUP |
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
@@ -108,7 +107,7 @@ WERROR NetGroupAdd_r(struct libnetapi_ctx *ctx,
                        break;
        }
 
-       status = rpccli_samr_CreateDomainGroup(pipe_cli, ctx,
+       status = rpccli_samr_CreateDomainGroup(pipe_cli, talloc_tos(),
                                               &domain_handle,
                                               &lsa_group_name,
                                               SEC_STD_DELETE |
@@ -127,7 +126,7 @@ WERROR NetGroupAdd_r(struct libnetapi_ctx *ctx,
                                init_lsa_String(&info.description,
                                                info1->grpi1_comment);
 
-                               status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                               status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                                  &group_handle,
                                                                  GROUPINFODESCRIPTION,
                                                                  &info);
@@ -138,7 +137,7 @@ WERROR NetGroupAdd_r(struct libnetapi_ctx *ctx,
                                init_lsa_String(&info.description,
                                                info2->grpi2_comment);
 
-                               status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                               status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                                  &group_handle,
                                                                  GROUPINFODESCRIPTION,
                                                                  &info);
@@ -150,7 +149,7 @@ WERROR NetGroupAdd_r(struct libnetapi_ctx *ctx,
 
                        if (info2->grpi2_attributes != 0) {
                                info.attributes.attributes = info2->grpi2_attributes;
-                               status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                               status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                                  &group_handle,
                                                                  GROUPINFOATTRIBUTES,
                                                                  &info);
@@ -162,7 +161,7 @@ WERROR NetGroupAdd_r(struct libnetapi_ctx *ctx,
                                init_lsa_String(&info.description,
                                                info3->grpi3_comment);
 
-                               status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                               status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                                  &group_handle,
                                                                  GROUPINFODESCRIPTION,
                                                                  &info);
@@ -174,7 +173,7 @@ WERROR NetGroupAdd_r(struct libnetapi_ctx *ctx,
 
                        if (info3->grpi3_attributes != 0) {
                                info.attributes.attributes = info3->grpi3_attributes;
-                               status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                               status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                                  &group_handle,
                                                                  GROUPINFOATTRIBUTES,
                                                                  &info);
@@ -193,22 +192,17 @@ WERROR NetGroupAdd_r(struct libnetapi_ctx *ctx,
        goto done;
 
  failed:
-       rpccli_samr_DeleteDomainGroup(pipe_cli, ctx,
+       rpccli_samr_DeleteDomainGroup(pipe_cli, talloc_tos(),
                                      &group_handle);
 
  done:
-       if (!cli) {
-               return werr;
-       }
-
        if (is_valid_policy_hnd(&group_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &group_handle);
-       }
-       if (is_valid_policy_hnd(&domain_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &domain_handle);
+               rpccli_samr_Close(pipe_cli, talloc_tos(), &group_handle);
        }
-       if (is_valid_policy_hnd(&connect_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &connect_handle);
+
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
        }
 
        return werr;
@@ -220,7 +214,7 @@ WERROR NetGroupAdd_r(struct libnetapi_ctx *ctx,
 WERROR NetGroupAdd_l(struct libnetapi_ctx *ctx,
                     struct NetGroupAdd *r)
 {
-       return NetGroupAdd_r(ctx, r);
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupAdd);
 }
 
 /****************************************************************
@@ -229,11 +223,10 @@ WERROR NetGroupAdd_l(struct libnetapi_ctx *ctx,
 WERROR NetGroupDel_r(struct libnetapi_ctx *ctx,
                     struct NetGroupDel *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
-       POLICY_HND connect_handle, domain_handle, group_handle;
+       struct policy_handle connect_handle, domain_handle, group_handle;
        struct lsa_String lsa_group_name;
        struct dom_sid2 *domain_sid = NULL;
        int i = 0;
@@ -241,7 +234,7 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx,
        struct samr_Ids rids;
        struct samr_Ids types;
        union samr_GroupInfo *info = NULL;
-       struct samr_RidTypeArray *rid_array = NULL;
+       struct samr_RidAttrArray *rid_array = NULL;
 
        ZERO_STRUCT(connect_handle);
        ZERO_STRUCT(domain_handle);
@@ -251,19 +244,16 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx,
                return WERR_INVALID_PARAM;
        }
 
-       werr = libnetapi_open_ipc_connection(ctx, r->in.server_name, &cli);
-       if (!W_ERROR_IS_OK(werr)) {
-               goto done;
-       }
-
-       werr = libnetapi_open_pipe(ctx, cli, PI_SAMR, &pipe_cli);
+       werr = libnetapi_open_pipe(ctx, r->in.server_name,
+                                  &ndr_table_samr.syntax_id,
+                                  &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
        }
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
                                          &domain_handle,
@@ -274,7 +264,7 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx,
 
        init_lsa_String(&lsa_group_name, r->in.group_name);
 
-       status = rpccli_samr_LookupNames(pipe_cli, ctx,
+       status = rpccli_samr_LookupNames(pipe_cli, talloc_tos(),
                                         &domain_handle,
                                         1,
                                         &lsa_group_name,
@@ -290,7 +280,7 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx,
                goto done;
        }
 
-       status = rpccli_samr_OpenGroup(pipe_cli, ctx,
+       status = rpccli_samr_OpenGroup(pipe_cli, talloc_tos(),
                                       &domain_handle,
                                       SEC_STD_DELETE |
                                       SAMR_GROUP_ACCESS_GET_MEMBERS |
@@ -304,7 +294,7 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx,
                goto done;
        }
 
-       status = rpccli_samr_QueryGroupInfo(pipe_cli, ctx,
+       status = rpccli_samr_QueryGroupInfo(pipe_cli, talloc_tos(),
                                            &group_handle,
                                            GROUPINFOATTRIBUTES,
                                            &info);
@@ -313,12 +303,14 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx,
                goto done;
        }
 
+#if 0
+       /* breaks against NT4 */
        if (!(info->attributes.attributes & SE_GROUP_ENABLED)) {
                werr = WERR_ACCESS_DENIED;
                goto done;
        }
-
-       status = rpccli_samr_QueryGroupMember(pipe_cli, ctx,
+#endif
+       status = rpccli_samr_QueryGroupMember(pipe_cli, talloc_tos(),
                                              &group_handle,
                                              &rid_array);
        if (!NT_STATUS_IS_OK(status)) {
@@ -330,7 +322,7 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx,
        struct lsa_Strings names;
        struct samr_Ids member_types;
 
-       status = rpccli_samr_LookupRids(pipe_cli, ctx,
+       status = rpccli_samr_LookupRids(pipe_cli, talloc_tos(),
                                        &domain_handle,
                                        rid_array->count,
                                        rid_array->rids,
@@ -344,7 +336,7 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx,
 
        for (i=0; i < rid_array->count; i++) {
 
-               status = rpccli_samr_DeleteGroupMember(pipe_cli, ctx,
+               status = rpccli_samr_DeleteGroupMember(pipe_cli, talloc_tos(),
                                                       &group_handle,
                                                       rid_array->rids[i]);
                if (!NT_STATUS_IS_OK(status)) {
@@ -353,7 +345,7 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx,
                }
        }
 
-       status = rpccli_samr_DeleteDomainGroup(pipe_cli, ctx,
+       status = rpccli_samr_DeleteDomainGroup(pipe_cli, talloc_tos(),
                                               &group_handle);
        if (!NT_STATUS_IS_OK(status)) {
                werr = ntstatus_to_werror(status);
@@ -365,18 +357,13 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx,
        werr = WERR_OK;
 
  done:
-       if (!cli) {
-               return werr;
-       }
-
        if (is_valid_policy_hnd(&group_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &group_handle);
-       }
-       if (is_valid_policy_hnd(&domain_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &domain_handle);
+               rpccli_samr_Close(pipe_cli, talloc_tos(), &group_handle);
        }
-       if (is_valid_policy_hnd(&connect_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &connect_handle);
+
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
        }
 
        return werr;
@@ -388,7 +375,7 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx,
 WERROR NetGroupDel_l(struct libnetapi_ctx *ctx,
                     struct NetGroupDel *r)
 {
-       return NetGroupDel_r(ctx, r);
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupDel);
 }
 
 /****************************************************************
@@ -397,11 +384,10 @@ WERROR NetGroupDel_l(struct libnetapi_ctx *ctx,
 WERROR NetGroupSetInfo_r(struct libnetapi_ctx *ctx,
                         struct NetGroupSetInfo *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
-       POLICY_HND connect_handle, domain_handle, group_handle;
+       struct policy_handle connect_handle, domain_handle, group_handle;
        struct lsa_String lsa_group_name;
        struct dom_sid2 *domain_sid = NULL;
 
@@ -423,19 +409,16 @@ WERROR NetGroupSetInfo_r(struct libnetapi_ctx *ctx,
                return WERR_INVALID_PARAM;
        }
 
-       werr = libnetapi_open_ipc_connection(ctx, r->in.server_name, &cli);
-       if (!W_ERROR_IS_OK(werr)) {
-               goto done;
-       }
-
-       werr = libnetapi_open_pipe(ctx, cli, PI_SAMR, &pipe_cli);
+       werr = libnetapi_open_pipe(ctx, r->in.server_name,
+                                  &ndr_table_samr.syntax_id,
+                                  &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
        }
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
                                          &domain_handle,
@@ -446,7 +429,7 @@ WERROR NetGroupSetInfo_r(struct libnetapi_ctx *ctx,
 
        init_lsa_String(&lsa_group_name, r->in.group_name);
 
-       status = rpccli_samr_LookupNames(pipe_cli, ctx,
+       status = rpccli_samr_LookupNames(pipe_cli, talloc_tos(),
                                         &domain_handle,
                                         1,
                                         &lsa_group_name,
@@ -462,7 +445,7 @@ WERROR NetGroupSetInfo_r(struct libnetapi_ctx *ctx,
                goto done;
        }
 
-       status = rpccli_samr_OpenGroup(pipe_cli, ctx,
+       status = rpccli_samr_OpenGroup(pipe_cli, talloc_tos(),
                                       &domain_handle,
                                       SAMR_GROUP_ACCESS_SET_INFO |
                                       SAMR_GROUP_ACCESS_LOOKUP_INFO,
@@ -475,25 +458,25 @@ WERROR NetGroupSetInfo_r(struct libnetapi_ctx *ctx,
 
        switch (r->in.level) {
                case 0:
-                       g0 = (struct GROUP_INFO_0 *)r->in.buf;
+                       g0 = (struct GROUP_INFO_0 *)r->in.buffer;
                        init_lsa_String(&info.name, g0->grpi0_name);
-                       status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                       status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                          &group_handle,
                                                          GROUPINFONAME,
                                                          &info);
                        break;
                case 1:
-                       g1 = (struct GROUP_INFO_1 *)r->in.buf;
+                       g1 = (struct GROUP_INFO_1 *)r->in.buffer;
                        init_lsa_String(&info.description, g1->grpi1_comment);
-                       status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                       status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                          &group_handle,
                                                          GROUPINFODESCRIPTION,
                                                          &info);
                        break;
                case 2:
-                       g2 = (struct GROUP_INFO_2 *)r->in.buf;
+                       g2 = (struct GROUP_INFO_2 *)r->in.buffer;
                        init_lsa_String(&info.description, g2->grpi2_comment);
-                       status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                       status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                          &group_handle,
                                                          GROUPINFODESCRIPTION,
                                                          &info);
@@ -502,15 +485,15 @@ WERROR NetGroupSetInfo_r(struct libnetapi_ctx *ctx,
                                goto done;
                        }
                        info.attributes.attributes = g2->grpi2_attributes;
-                       status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                       status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                          &group_handle,
                                                          GROUPINFOATTRIBUTES,
                                                          &info);
                        break;
                case 3:
-                       g3 = (struct GROUP_INFO_3 *)r->in.buf;
+                       g3 = (struct GROUP_INFO_3 *)r->in.buffer;
                        init_lsa_String(&info.description, g3->grpi3_comment);
-                       status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                       status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                          &group_handle,
                                                          GROUPINFODESCRIPTION,
                                                          &info);
@@ -519,23 +502,23 @@ WERROR NetGroupSetInfo_r(struct libnetapi_ctx *ctx,
                                goto done;
                        }
                        info.attributes.attributes = g3->grpi3_attributes;
-                       status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                       status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                          &group_handle,
                                                          GROUPINFOATTRIBUTES,
                                                          &info);
                        break;
                case 1002:
-                       g1002 = (struct GROUP_INFO_1002 *)r->in.buf;
+                       g1002 = (struct GROUP_INFO_1002 *)r->in.buffer;
                        init_lsa_String(&info.description, g1002->grpi1002_comment);
-                       status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                       status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                          &group_handle,
                                                          GROUPINFODESCRIPTION,
                                                          &info);
                        break;
                case 1005:
-                       g1005 = (struct GROUP_INFO_1005 *)r->in.buf;
+                       g1005 = (struct GROUP_INFO_1005 *)r->in.buffer;
                        info.attributes.attributes = g1005->grpi1005_attributes;
-                       status = rpccli_samr_SetGroupInfo(pipe_cli, ctx,
+                       status = rpccli_samr_SetGroupInfo(pipe_cli, talloc_tos(),
                                                          &group_handle,
                                                          GROUPINFOATTRIBUTES,
                                                          &info);
@@ -553,18 +536,13 @@ WERROR NetGroupSetInfo_r(struct libnetapi_ctx *ctx,
        werr = WERR_OK;
 
  done:
-       if (!cli) {
-               return werr;
-       }
-
        if (is_valid_policy_hnd(&group_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &group_handle);
-       }
-       if (is_valid_policy_hnd(&domain_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &domain_handle);
+               rpccli_samr_Close(pipe_cli, talloc_tos(), &group_handle);
        }
-       if (is_valid_policy_hnd(&connect_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &connect_handle);
+
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
        }
 
        return werr;
@@ -576,7 +554,7 @@ WERROR NetGroupSetInfo_r(struct libnetapi_ctx *ctx,
 WERROR NetGroupSetInfo_l(struct libnetapi_ctx *ctx,
                         struct NetGroupSetInfo *r)
 {
-       return NetGroupSetInfo_r(ctx, r);
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupSetInfo);
 }
 
 /****************************************************************
@@ -626,7 +604,7 @@ static WERROR map_group_info_to_buffer(TALLOC_CTX *mem_ctx,
                        info3.grpi3_name        = info->name.string;
                        info3.grpi3_comment     = info->description.string;
                        info3.grpi3_attributes  = info->attributes;
-                       info3.grpi3_group_sid   = (struct domsid *)sid_dup_talloc(mem_ctx, &sid);
+                       info3.grpi3_group_sid   = (struct domsid *)dom_sid_dup(mem_ctx, &sid);
 
                        *buffer = (uint8_t *)talloc_memdup(mem_ctx, &info3, sizeof(info3));
 
@@ -646,17 +624,17 @@ static WERROR map_group_info_to_buffer(TALLOC_CTX *mem_ctx,
 WERROR NetGroupGetInfo_r(struct libnetapi_ctx *ctx,
                         struct NetGroupGetInfo *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
-       POLICY_HND connect_handle, domain_handle, group_handle;
+       struct policy_handle connect_handle, domain_handle, group_handle;
        struct lsa_String lsa_group_name;
        struct dom_sid2 *domain_sid = NULL;
 
        struct samr_Ids rids;
        struct samr_Ids types;
        union samr_GroupInfo *info = NULL;
+       bool group_info_all = false;
 
        ZERO_STRUCT(connect_handle);
        ZERO_STRUCT(domain_handle);
@@ -666,19 +644,16 @@ WERROR NetGroupGetInfo_r(struct libnetapi_ctx *ctx,
                return WERR_INVALID_PARAM;
        }
 
-       werr = libnetapi_open_ipc_connection(ctx, r->in.server_name, &cli);
-       if (!W_ERROR_IS_OK(werr)) {
-               goto done;
-       }
-
-       werr = libnetapi_open_pipe(ctx, cli, PI_SAMR, &pipe_cli);
+       werr = libnetapi_open_pipe(ctx, r->in.server_name,
+                                  &ndr_table_samr.syntax_id,
+                                  &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
        }
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
                                          &domain_handle,
@@ -689,7 +664,7 @@ WERROR NetGroupGetInfo_r(struct libnetapi_ctx *ctx,
 
        init_lsa_String(&lsa_group_name, r->in.group_name);
 
-       status = rpccli_samr_LookupNames(pipe_cli, ctx,
+       status = rpccli_samr_LookupNames(pipe_cli, talloc_tos(),
                                         &domain_handle,
                                         1,
                                         &lsa_group_name,
@@ -705,7 +680,7 @@ WERROR NetGroupGetInfo_r(struct libnetapi_ctx *ctx,
                goto done;
        }
 
-       status = rpccli_samr_OpenGroup(pipe_cli, ctx,
+       status = rpccli_samr_OpenGroup(pipe_cli, talloc_tos(),
                                       &domain_handle,
                                       SAMR_GROUP_ACCESS_LOOKUP_INFO,
                                       rids.ids[0],
@@ -715,34 +690,38 @@ WERROR NetGroupGetInfo_r(struct libnetapi_ctx *ctx,
                goto done;
        }
 
-       status = rpccli_samr_QueryGroupInfo(pipe_cli, ctx,
+       status = rpccli_samr_QueryGroupInfo(pipe_cli, talloc_tos(),
                                            &group_handle,
                                            GROUPINFOALL2,
                                            &info);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
+               status = rpccli_samr_QueryGroupInfo(pipe_cli, talloc_tos(),
+                                                   &group_handle,
+                                                   GROUPINFOALL,
+                                                   &info);
+               group_info_all = true;
+       }
+
        if (!NT_STATUS_IS_OK(status)) {
                werr = ntstatus_to_werror(status);
                goto done;
        }
 
        werr = map_group_info_to_buffer(ctx, r->in.level,
-                                       &info->all2, domain_sid, rids.ids[0],
-                                       r->out.buf);
+                                       group_info_all ? &info->all : &info->all2,
+                                       domain_sid, rids.ids[0],
+                                       r->out.buffer);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
        }
  done:
-       if (!cli) {
-               return werr;
-       }
-
        if (is_valid_policy_hnd(&group_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &group_handle);
-       }
-       if (is_valid_policy_hnd(&domain_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &domain_handle);
+               rpccli_samr_Close(pipe_cli, talloc_tos(), &group_handle);
        }
-       if (is_valid_policy_hnd(&connect_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &connect_handle);
+
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
        }
 
        return werr;
@@ -754,7 +733,7 @@ WERROR NetGroupGetInfo_r(struct libnetapi_ctx *ctx,
 WERROR NetGroupGetInfo_l(struct libnetapi_ctx *ctx,
                         struct NetGroupGetInfo *r)
 {
-       return NetGroupGetInfo_r(ctx, r);
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupGetInfo);
 }
 
 /****************************************************************
@@ -763,11 +742,10 @@ WERROR NetGroupGetInfo_l(struct libnetapi_ctx *ctx,
 WERROR NetGroupAddUser_r(struct libnetapi_ctx *ctx,
                         struct NetGroupAddUser *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
-       POLICY_HND connect_handle, domain_handle, group_handle;
+       struct policy_handle connect_handle, domain_handle, group_handle;
        struct lsa_String lsa_group_name, lsa_user_name;
        struct dom_sid2 *domain_sid = NULL;
 
@@ -782,19 +760,16 @@ WERROR NetGroupAddUser_r(struct libnetapi_ctx *ctx,
                return WERR_INVALID_PARAM;
        }
 
-       werr = libnetapi_open_ipc_connection(ctx, r->in.server_name, &cli);
-       if (!W_ERROR_IS_OK(werr)) {
-               goto done;
-       }
-
-       werr = libnetapi_open_pipe(ctx, cli, PI_SAMR, &pipe_cli);
+       werr = libnetapi_open_pipe(ctx, r->in.server_name,
+                                  &ndr_table_samr.syntax_id,
+                                  &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
        }
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
                                          &domain_handle,
@@ -805,23 +780,23 @@ WERROR NetGroupAddUser_r(struct libnetapi_ctx *ctx,
 
        init_lsa_String(&lsa_group_name, r->in.group_name);
 
-       status = rpccli_samr_LookupNames(pipe_cli, ctx,
+       status = rpccli_samr_LookupNames(pipe_cli, talloc_tos(),
                                         &domain_handle,
                                         1,
                                         &lsa_group_name,
                                         &rids,
                                         &types);
        if (!NT_STATUS_IS_OK(status)) {
-               werr = WERR_GROUP_NOT_FOUND;
+               werr = WERR_GROUPNOTFOUND;
                goto done;
        }
 
        if (types.ids[0] != SID_NAME_DOM_GRP) {
-               werr = WERR_GROUP_NOT_FOUND;
+               werr = WERR_GROUPNOTFOUND;
                goto done;
        }
 
-       status = rpccli_samr_OpenGroup(pipe_cli, ctx,
+       status = rpccli_samr_OpenGroup(pipe_cli, talloc_tos(),
                                       &domain_handle,
                                       SAMR_GROUP_ACCESS_ADD_MEMBER,
                                       rids.ids[0],
@@ -833,7 +808,7 @@ WERROR NetGroupAddUser_r(struct libnetapi_ctx *ctx,
 
        init_lsa_String(&lsa_user_name, r->in.user_name);
 
-       status = rpccli_samr_LookupNames(pipe_cli, ctx,
+       status = rpccli_samr_LookupNames(pipe_cli, talloc_tos(),
                                         &domain_handle,
                                         1,
                                         &lsa_user_name,
@@ -849,7 +824,7 @@ WERROR NetGroupAddUser_r(struct libnetapi_ctx *ctx,
                goto done;
        }
 
-       status = rpccli_samr_AddGroupMember(pipe_cli, ctx,
+       status = rpccli_samr_AddGroupMember(pipe_cli, talloc_tos(),
                                            &group_handle,
                                            rids.ids[0],
                                            7); /* why ? */
@@ -861,18 +836,13 @@ WERROR NetGroupAddUser_r(struct libnetapi_ctx *ctx,
        werr = WERR_OK;
 
  done:
-       if (!cli) {
-               return werr;
-       }
-
        if (is_valid_policy_hnd(&group_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &group_handle);
+               rpccli_samr_Close(pipe_cli, talloc_tos(), &group_handle);
        }
-       if (is_valid_policy_hnd(&domain_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &domain_handle);
-       }
-       if (is_valid_policy_hnd(&connect_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &connect_handle);
+
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
        }
 
        return werr;
@@ -884,7 +854,7 @@ WERROR NetGroupAddUser_r(struct libnetapi_ctx *ctx,
 WERROR NetGroupAddUser_l(struct libnetapi_ctx *ctx,
                         struct NetGroupAddUser *r)
 {
-       return NetGroupAddUser_r(ctx, r);
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupAddUser);
 }
 
 /****************************************************************
@@ -893,11 +863,10 @@ WERROR NetGroupAddUser_l(struct libnetapi_ctx *ctx,
 WERROR NetGroupDelUser_r(struct libnetapi_ctx *ctx,
                         struct NetGroupDelUser *r)
 {
-       struct cli_state *cli = NULL;
        struct rpc_pipe_client *pipe_cli = NULL;
        NTSTATUS status;
        WERROR werr;
-       POLICY_HND connect_handle, domain_handle, group_handle;
+       struct policy_handle connect_handle, domain_handle, group_handle;
        struct lsa_String lsa_group_name, lsa_user_name;
        struct dom_sid2 *domain_sid = NULL;
 
@@ -912,19 +881,16 @@ WERROR NetGroupDelUser_r(struct libnetapi_ctx *ctx,
                return WERR_INVALID_PARAM;
        }
 
-       werr = libnetapi_open_ipc_connection(ctx, r->in.server_name, &cli);
-       if (!W_ERROR_IS_OK(werr)) {
-               goto done;
-       }
-
-       werr = libnetapi_open_pipe(ctx, cli, PI_SAMR, &pipe_cli);
+       werr = libnetapi_open_pipe(ctx, r->in.server_name,
+                                  &ndr_table_samr.syntax_id,
+                                  &pipe_cli);
        if (!W_ERROR_IS_OK(werr)) {
                goto done;
        }
 
        werr = libnetapi_samr_open_domain(ctx, pipe_cli,
                                          SAMR_ACCESS_ENUM_DOMAINS |
-                                         SAMR_ACCESS_OPEN_DOMAIN,
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
                                          SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
                                          &connect_handle,
                                          &domain_handle,
@@ -935,23 +901,23 @@ WERROR NetGroupDelUser_r(struct libnetapi_ctx *ctx,
 
        init_lsa_String(&lsa_group_name, r->in.group_name);
 
-       status = rpccli_samr_LookupNames(pipe_cli, ctx,
+       status = rpccli_samr_LookupNames(pipe_cli, talloc_tos(),
                                         &domain_handle,
                                         1,
                                         &lsa_group_name,
                                         &rids,
                                         &types);
        if (!NT_STATUS_IS_OK(status)) {
-               werr = WERR_GROUP_NOT_FOUND;
+               werr = WERR_GROUPNOTFOUND;
                goto done;
        }
 
        if (types.ids[0] != SID_NAME_DOM_GRP) {
-               werr = WERR_GROUP_NOT_FOUND;
+               werr = WERR_GROUPNOTFOUND;
                goto done;
        }
 
-       status = rpccli_samr_OpenGroup(pipe_cli, ctx,
+       status = rpccli_samr_OpenGroup(pipe_cli, talloc_tos(),
                                       &domain_handle,
                                       SAMR_GROUP_ACCESS_REMOVE_MEMBER,
                                       rids.ids[0],
@@ -963,7 +929,7 @@ WERROR NetGroupDelUser_r(struct libnetapi_ctx *ctx,
 
        init_lsa_String(&lsa_user_name, r->in.user_name);
 
-       status = rpccli_samr_LookupNames(pipe_cli, ctx,
+       status = rpccli_samr_LookupNames(pipe_cli, talloc_tos(),
                                         &domain_handle,
                                         1,
                                         &lsa_user_name,
@@ -979,7 +945,7 @@ WERROR NetGroupDelUser_r(struct libnetapi_ctx *ctx,
                goto done;
        }
 
-       status = rpccli_samr_DeleteGroupMember(pipe_cli, ctx,
+       status = rpccli_samr_DeleteGroupMember(pipe_cli, talloc_tos(),
                                               &group_handle,
                                               rids.ids[0]);
        if (!NT_STATUS_IS_OK(status)) {
@@ -990,18 +956,13 @@ WERROR NetGroupDelUser_r(struct libnetapi_ctx *ctx,
        werr = WERR_OK;
 
  done:
-       if (!cli) {
-               return werr;
-       }
-
        if (is_valid_policy_hnd(&group_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &group_handle);
-       }
-       if (is_valid_policy_hnd(&domain_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &domain_handle);
+               rpccli_samr_Close(pipe_cli, talloc_tos(), &group_handle);
        }
-       if (is_valid_policy_hnd(&connect_handle)) {
-               rpccli_samr_Close(pipe_cli, ctx, &connect_handle);
+
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
        }
 
        return werr;
@@ -1013,7 +974,157 @@ WERROR NetGroupDelUser_r(struct libnetapi_ctx *ctx,
 WERROR NetGroupDelUser_l(struct libnetapi_ctx *ctx,
                         struct NetGroupDelUser *r)
 {
-       return NetGroupDelUser_r(ctx, r);
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupDelUser);
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR convert_samr_disp_groups_to_GROUP_INFO_0_buffer(TALLOC_CTX *mem_ctx,
+                                                             struct samr_DispInfoFullGroups *groups,
+                                                             uint8_t **buffer)
+{
+       struct GROUP_INFO_0 *g0;
+       int i;
+
+       g0 = TALLOC_ZERO_ARRAY(mem_ctx, struct GROUP_INFO_0, groups->count);
+       W_ERROR_HAVE_NO_MEMORY(g0);
+
+       for (i=0; i<groups->count; i++) {
+               g0[i].grpi0_name = talloc_strdup(mem_ctx,
+                       groups->entries[i].account_name.string);
+               W_ERROR_HAVE_NO_MEMORY(g0[i].grpi0_name);
+       }
+
+       *buffer = (uint8_t *)talloc_memdup(mem_ctx, g0,
+                                          sizeof(struct GROUP_INFO_0) * groups->count);
+       W_ERROR_HAVE_NO_MEMORY(*buffer);
+
+       return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR convert_samr_disp_groups_to_GROUP_INFO_1_buffer(TALLOC_CTX *mem_ctx,
+                                                             struct samr_DispInfoFullGroups *groups,
+                                                             uint8_t **buffer)
+{
+       struct GROUP_INFO_1 *g1;
+       int i;
+
+       g1 = TALLOC_ZERO_ARRAY(mem_ctx, struct GROUP_INFO_1, groups->count);
+       W_ERROR_HAVE_NO_MEMORY(g1);
+
+       for (i=0; i<groups->count; i++) {
+               g1[i].grpi1_name = talloc_strdup(mem_ctx,
+                       groups->entries[i].account_name.string);
+               g1[i].grpi1_comment = talloc_strdup(mem_ctx,
+                       groups->entries[i].description.string);
+               W_ERROR_HAVE_NO_MEMORY(g1[i].grpi1_name);
+       }
+
+       *buffer = (uint8_t *)talloc_memdup(mem_ctx, g1,
+                                          sizeof(struct GROUP_INFO_1) * groups->count);
+       W_ERROR_HAVE_NO_MEMORY(*buffer);
+
+       return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR convert_samr_disp_groups_to_GROUP_INFO_2_buffer(TALLOC_CTX *mem_ctx,
+                                                             struct samr_DispInfoFullGroups *groups,
+                                                             uint8_t **buffer)
+{
+       struct GROUP_INFO_2 *g2;
+       int i;
+
+       g2 = TALLOC_ZERO_ARRAY(mem_ctx, struct GROUP_INFO_2, groups->count);
+       W_ERROR_HAVE_NO_MEMORY(g2);
+
+       for (i=0; i<groups->count; i++) {
+               g2[i].grpi2_name = talloc_strdup(mem_ctx,
+                       groups->entries[i].account_name.string);
+               g2[i].grpi2_comment = talloc_strdup(mem_ctx,
+                       groups->entries[i].description.string);
+               g2[i].grpi2_group_id = groups->entries[i].rid;
+               g2[i].grpi2_attributes = groups->entries[i].acct_flags;
+               W_ERROR_HAVE_NO_MEMORY(g2[i].grpi2_name);
+       }
+
+       *buffer = (uint8_t *)talloc_memdup(mem_ctx, g2,
+                                          sizeof(struct GROUP_INFO_2) * groups->count);
+       W_ERROR_HAVE_NO_MEMORY(*buffer);
+
+       return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR convert_samr_disp_groups_to_GROUP_INFO_3_buffer(TALLOC_CTX *mem_ctx,
+                                                             struct samr_DispInfoFullGroups *groups,
+                                                             const struct dom_sid *domain_sid,
+                                                             uint8_t **buffer)
+{
+       struct GROUP_INFO_3 *g3;
+       int i;
+
+       g3 = TALLOC_ZERO_ARRAY(mem_ctx, struct GROUP_INFO_3, groups->count);
+       W_ERROR_HAVE_NO_MEMORY(g3);
+
+       for (i=0; i<groups->count; i++) {
+
+               struct dom_sid sid;
+
+               if (!sid_compose(&sid, domain_sid, groups->entries[i].rid)) {
+                       return WERR_NOMEM;
+               }
+
+               g3[i].grpi3_name = talloc_strdup(mem_ctx,
+                       groups->entries[i].account_name.string);
+               g3[i].grpi3_comment = talloc_strdup(mem_ctx,
+                       groups->entries[i].description.string);
+               g3[i].grpi3_group_sid = (struct domsid *)dom_sid_dup(mem_ctx, &sid);
+               g3[i].grpi3_attributes = groups->entries[i].acct_flags;
+               W_ERROR_HAVE_NO_MEMORY(g3[i].grpi3_name);
+       }
+
+       *buffer = (uint8_t *)talloc_memdup(mem_ctx, g3,
+                                          sizeof(struct GROUP_INFO_3) * groups->count);
+       W_ERROR_HAVE_NO_MEMORY(*buffer);
+
+       return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR convert_samr_disp_groups_to_GROUP_INFO_buffer(TALLOC_CTX *mem_ctx,
+                                                           uint32_t level,
+                                                           struct samr_DispInfoFullGroups *groups,
+                                                           const struct dom_sid *domain_sid,
+                                                           uint32_t *entries_read,
+                                                           uint8_t **buffer)
+{
+       if (entries_read) {
+               *entries_read = groups->count;
+       }
+
+       switch (level) {
+               case 0:
+                       return convert_samr_disp_groups_to_GROUP_INFO_0_buffer(mem_ctx, groups, buffer);
+               case 1:
+                       return convert_samr_disp_groups_to_GROUP_INFO_1_buffer(mem_ctx, groups, buffer);
+               case 2:
+                       return convert_samr_disp_groups_to_GROUP_INFO_2_buffer(mem_ctx, groups, buffer);
+               case 3:
+                       return convert_samr_disp_groups_to_GROUP_INFO_3_buffer(mem_ctx, groups, domain_sid, buffer);
+               default:
+                       return WERR_UNKNOWN_LEVEL;
+       }
 }
 
 /****************************************************************
@@ -1022,7 +1133,109 @@ WERROR NetGroupDelUser_l(struct libnetapi_ctx *ctx,
 WERROR NetGroupEnum_r(struct libnetapi_ctx *ctx,
                      struct NetGroupEnum *r)
 {
-       return WERR_NOT_SUPPORTED;
+       struct rpc_pipe_client *pipe_cli = NULL;
+       struct policy_handle connect_handle;
+       struct dom_sid2 *domain_sid = NULL;
+       struct policy_handle domain_handle;
+       union samr_DispInfo info;
+       union samr_DomainInfo *domain_info = NULL;
+
+       uint32_t total_size = 0;
+       uint32_t returned_size = 0;
+
+       NTSTATUS status = NT_STATUS_OK;
+       WERROR werr, tmp_werr;
+
+       ZERO_STRUCT(connect_handle);
+       ZERO_STRUCT(domain_handle);
+
+       switch (r->in.level) {
+               case 0:
+               case 1:
+               case 2:
+               case 3:
+                       break;
+               default:
+                       return WERR_UNKNOWN_LEVEL;
+       }
+
+       werr = libnetapi_open_pipe(ctx, r->in.server_name,
+                                  &ndr_table_samr.syntax_id,
+                                  &pipe_cli);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
+                                         SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 |
+                                         SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS |
+                                         SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+                                         &connect_handle,
+                                         &domain_handle,
+                                         &domain_sid);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       status = rpccli_samr_QueryDomainInfo(pipe_cli, talloc_tos(),
+                                            &domain_handle,
+                                            2,
+                                            &domain_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       if (r->out.total_entries) {
+               *r->out.total_entries = domain_info->general.num_groups;
+       }
+
+       status = rpccli_samr_QueryDisplayInfo2(pipe_cli,
+                                              ctx,
+                                              &domain_handle,
+                                              3,
+                                              r->in.resume_handle ?
+                                              *r->in.resume_handle : 0,
+                                              (uint32_t)-1,
+                                              r->in.prefmaxlen,
+                                              &total_size,
+                                              &returned_size,
+                                              &info);
+       werr = ntstatus_to_werror(status);
+       if (NT_STATUS_IS_ERR(status)) {
+               goto done;
+       }
+
+       if (r->out.resume_handle && info.info3.count > 0) {
+               *r->out.resume_handle =
+                       info.info3.entries[info.info3.count-1].idx;
+       }
+
+       tmp_werr = convert_samr_disp_groups_to_GROUP_INFO_buffer(ctx,
+                                                                r->in.level,
+                                                                &info.info3,
+                                                                domain_sid,
+                                                                r->out.entries_read,
+                                                                r->out.buffer);
+       if (!W_ERROR_IS_OK(tmp_werr)) {
+               werr = tmp_werr;
+               goto done;
+       }
+
+ done:
+       /* if last query */
+       if (NT_STATUS_IS_OK(status) ||
+           NT_STATUS_IS_ERR(status)) {
+
+               if (ctx->disable_policy_handle_cache) {
+                       libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+                       libnetapi_samr_close_connect_handle(ctx, &connect_handle);
+               }
+       }
+
+       return werr;
 }
 
 /****************************************************************
@@ -1031,5 +1244,391 @@ WERROR NetGroupEnum_r(struct libnetapi_ctx *ctx,
 WERROR NetGroupEnum_l(struct libnetapi_ctx *ctx,
                      struct NetGroupEnum *r)
 {
-       return WERR_NOT_SUPPORTED;
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupEnum);
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetGroupGetUsers_r(struct libnetapi_ctx *ctx,
+                         struct NetGroupGetUsers *r)
+{
+       /* FIXME: this call needs to cope with large replies */
+
+       struct rpc_pipe_client *pipe_cli = NULL;
+       struct policy_handle connect_handle, domain_handle, group_handle;
+       struct lsa_String lsa_account_name;
+       struct dom_sid2 *domain_sid = NULL;
+       struct samr_Ids group_rids, name_types;
+       struct samr_RidAttrArray *rid_array = NULL;
+       struct lsa_Strings names;
+       struct samr_Ids member_types;
+
+       int i;
+       uint32_t entries_read = 0;
+
+       NTSTATUS status = NT_STATUS_OK;
+       WERROR werr;
+
+       ZERO_STRUCT(connect_handle);
+       ZERO_STRUCT(domain_handle);
+
+       if (!r->out.buffer) {
+               return WERR_INVALID_PARAM;
+       }
+
+       *r->out.buffer = NULL;
+       *r->out.entries_read = 0;
+       *r->out.total_entries = 0;
+
+       switch (r->in.level) {
+               case 0:
+               case 1:
+                       break;
+               default:
+                       return WERR_UNKNOWN_LEVEL;
+       }
+
+
+       werr = libnetapi_open_pipe(ctx, r->in.server_name,
+                                  &ndr_table_samr.syntax_id,
+                                  &pipe_cli);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
+                                         SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+                                         &connect_handle,
+                                         &domain_handle,
+                                         &domain_sid);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       init_lsa_String(&lsa_account_name, r->in.group_name);
+
+       status = rpccli_samr_LookupNames(pipe_cli, talloc_tos(),
+                                        &domain_handle,
+                                        1,
+                                        &lsa_account_name,
+                                        &group_rids,
+                                        &name_types);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       status = rpccli_samr_OpenGroup(pipe_cli, talloc_tos(),
+                                      &domain_handle,
+                                      SAMR_GROUP_ACCESS_GET_MEMBERS,
+                                      group_rids.ids[0],
+                                      &group_handle);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       status = rpccli_samr_QueryGroupMember(pipe_cli, talloc_tos(),
+                                             &group_handle,
+                                             &rid_array);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       status = rpccli_samr_LookupRids(pipe_cli, talloc_tos(),
+                                       &domain_handle,
+                                       rid_array->count,
+                                       rid_array->rids,
+                                       &names,
+                                       &member_types);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       for (i=0; i < names.count; i++) {
+
+               if (member_types.ids[i] != SID_NAME_USER) {
+                       continue;
+               }
+
+               status = add_GROUP_USERS_INFO_X_buffer(ctx,
+                                                      r->in.level,
+                                                      names.names[i].string,
+                                                      7,
+                                                      r->out.buffer,
+                                                      &entries_read);
+               if (!NT_STATUS_IS_OK(status)) {
+                       werr = ntstatus_to_werror(status);
+                       goto done;
+               }
+       }
+
+       *r->out.entries_read = entries_read;
+       *r->out.total_entries = entries_read;
+
+       werr = WERR_OK;
+
+ done:
+       if (is_valid_policy_hnd(&group_handle)) {
+               rpccli_samr_Close(pipe_cli, talloc_tos(), &group_handle);
+       }
+
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
+       }
+
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetGroupGetUsers_l(struct libnetapi_ctx *ctx,
+                         struct NetGroupGetUsers *r)
+{
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupGetUsers);
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetGroupSetUsers_r(struct libnetapi_ctx *ctx,
+                         struct NetGroupSetUsers *r)
+{
+       struct rpc_pipe_client *pipe_cli = NULL;
+       struct policy_handle connect_handle, domain_handle, group_handle;
+       struct lsa_String lsa_account_name;
+       struct dom_sid2 *domain_sid = NULL;
+       union samr_GroupInfo *group_info = NULL;
+       struct samr_Ids user_rids, name_types;
+       struct samr_Ids group_rids, group_types;
+       struct samr_RidAttrArray *rid_array = NULL;
+       struct lsa_String *lsa_names = NULL;
+
+       uint32_t *add_rids = NULL;
+       uint32_t *del_rids = NULL;
+       size_t num_add_rids = 0;
+       size_t num_del_rids = 0;
+
+       uint32_t *member_rids = NULL;
+
+       struct GROUP_USERS_INFO_0 *i0 = NULL;
+       struct GROUP_USERS_INFO_1 *i1 = NULL;
+
+       int i, k;
+
+       NTSTATUS status = NT_STATUS_OK;
+       WERROR werr;
+
+       ZERO_STRUCT(connect_handle);
+       ZERO_STRUCT(domain_handle);
+
+       if (!r->in.buffer) {
+               return WERR_INVALID_PARAM;
+       }
+
+       switch (r->in.level) {
+               case 0:
+               case 1:
+                       break;
+               default:
+                       return WERR_UNKNOWN_LEVEL;
+       }
+
+       werr = libnetapi_open_pipe(ctx, r->in.server_name,
+                                  &ndr_table_samr.syntax_id,
+                                  &pipe_cli);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       werr = libnetapi_samr_open_domain(ctx, pipe_cli,
+                                         SAMR_ACCESS_ENUM_DOMAINS |
+                                         SAMR_ACCESS_LOOKUP_DOMAIN,
+                                         SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+                                         &connect_handle,
+                                         &domain_handle,
+                                         &domain_sid);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       init_lsa_String(&lsa_account_name, r->in.group_name);
+
+       status = rpccli_samr_LookupNames(pipe_cli, talloc_tos(),
+                                        &domain_handle,
+                                        1,
+                                        &lsa_account_name,
+                                        &group_rids,
+                                        &group_types);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       status = rpccli_samr_OpenGroup(pipe_cli, talloc_tos(),
+                                      &domain_handle,
+                                      SAMR_GROUP_ACCESS_GET_MEMBERS |
+                                      SAMR_GROUP_ACCESS_ADD_MEMBER |
+                                      SAMR_GROUP_ACCESS_REMOVE_MEMBER |
+                                      SAMR_GROUP_ACCESS_LOOKUP_INFO,
+                                      group_rids.ids[0],
+                                      &group_handle);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       status = rpccli_samr_QueryGroupInfo(pipe_cli, talloc_tos(),
+                                           &group_handle,
+                                           GROUPINFOATTRIBUTES,
+                                           &group_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       switch (r->in.level) {
+               case 0:
+                       i0 = (struct GROUP_USERS_INFO_0 *)r->in.buffer;
+                       break;
+               case 1:
+                       i1 = (struct GROUP_USERS_INFO_1 *)r->in.buffer;
+                       break;
+       }
+
+       lsa_names = talloc_array(ctx, struct lsa_String, r->in.num_entries);
+       if (!lsa_names) {
+               werr = WERR_NOMEM;
+               goto done;
+       }
+
+       for (i=0; i < r->in.num_entries; i++) {
+
+               switch (r->in.level) {
+                       case 0:
+                               init_lsa_String(&lsa_names[i], i0->grui0_name);
+                               i0++;
+                               break;
+                       case 1:
+                               init_lsa_String(&lsa_names[i], i1->grui1_name);
+                               i1++;
+                               break;
+               }
+       }
+
+       status = rpccli_samr_LookupNames(pipe_cli, talloc_tos(),
+                                        &domain_handle,
+                                        r->in.num_entries,
+                                        lsa_names,
+                                        &user_rids,
+                                        &name_types);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       member_rids = user_rids.ids;
+
+       status = rpccli_samr_QueryGroupMember(pipe_cli, talloc_tos(),
+                                             &group_handle,
+                                             &rid_array);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = ntstatus_to_werror(status);
+               goto done;
+       }
+
+       /* add list */
+
+       for (i=0; i < r->in.num_entries; i++) {
+               bool already_member = false;
+               for (k=0; k < rid_array->count; k++) {
+                       if (member_rids[i] == rid_array->rids[k]) {
+                               already_member = true;
+                               break;
+                       }
+               }
+               if (!already_member) {
+                       if (!add_rid_to_array_unique(ctx,
+                                                    member_rids[i],
+                                                    &add_rids, &num_add_rids)) {
+                               werr = WERR_GENERAL_FAILURE;
+                               goto done;
+                       }
+               }
+       }
+
+       /* del list */
+
+       for (k=0; k < rid_array->count; k++) {
+               bool keep_member = false;
+               for (i=0; i < r->in.num_entries; i++) {
+                       if (member_rids[i] == rid_array->rids[k]) {
+                               keep_member = true;
+                               break;
+                       }
+               }
+               if (!keep_member) {
+                       if (!add_rid_to_array_unique(ctx,
+                                                    rid_array->rids[k],
+                                                    &del_rids, &num_del_rids)) {
+                               werr = WERR_GENERAL_FAILURE;
+                               goto done;
+                       }
+               }
+       }
+
+       /* add list */
+
+       for (i=0; i < num_add_rids; i++) {
+               status = rpccli_samr_AddGroupMember(pipe_cli, talloc_tos(),
+                                                   &group_handle,
+                                                   add_rids[i],
+                                                   7 /* ? */);
+               if (!NT_STATUS_IS_OK(status)) {
+                       werr = ntstatus_to_werror(status);
+                       goto done;
+               }
+       }
+
+       /* del list */
+
+       for (i=0; i < num_del_rids; i++) {
+               status = rpccli_samr_DeleteGroupMember(pipe_cli, talloc_tos(),
+                                                      &group_handle,
+                                                      del_rids[i]);
+               if (!NT_STATUS_IS_OK(status)) {
+                       werr = ntstatus_to_werror(status);
+                       goto done;
+               }
+       }
+
+       werr = WERR_OK;
+
+ done:
+       if (is_valid_policy_hnd(&group_handle)) {
+               rpccli_samr_Close(pipe_cli, talloc_tos(), &group_handle);
+       }
+
+       if (ctx->disable_policy_handle_cache) {
+               libnetapi_samr_close_domain_handle(ctx, &domain_handle);
+               libnetapi_samr_close_connect_handle(ctx, &connect_handle);
+       }
+
+       return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR NetGroupSetUsers_l(struct libnetapi_ctx *ctx,
+                         struct NetGroupSetUsers *r)
+{
+       LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetGroupSetUsers);
 }