s3: Attempt to fix bug 7665
[samba.git] / source3 / lib / netapi / localgroup.c
1 /*
2  *  Unix SMB/CIFS implementation.
3  *  NetApi LocalGroup Support
4  *  Copyright (C) Guenther Deschner 2008
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 #include "includes.h"
21
22 #include "librpc/gen_ndr/libnetapi.h"
23 #include "lib/netapi/netapi.h"
24 #include "lib/netapi/netapi_private.h"
25 #include "lib/netapi/libnetapi.h"
26 #include "../librpc/gen_ndr/cli_samr.h"
27 #include "../librpc/gen_ndr/cli_lsa.h"
28
29 static NTSTATUS libnetapi_samr_lookup_and_open_alias(TALLOC_CTX *mem_ctx,
30                                                      struct rpc_pipe_client *pipe_cli,
31                                                      struct policy_handle *domain_handle,
32                                                      const char *group_name,
33                                                      uint32_t access_rights,
34                                                      struct policy_handle *alias_handle)
35 {
36         NTSTATUS status;
37
38         struct lsa_String lsa_account_name;
39         struct samr_Ids user_rids, name_types;
40
41         init_lsa_String(&lsa_account_name, group_name);
42
43         status = rpccli_samr_LookupNames(pipe_cli, mem_ctx,
44                                          domain_handle,
45                                          1,
46                                          &lsa_account_name,
47                                          &user_rids,
48                                          &name_types);
49         if (!NT_STATUS_IS_OK(status)) {
50                 return status;
51         }
52
53         switch (name_types.ids[0]) {
54                 case SID_NAME_ALIAS:
55                 case SID_NAME_WKN_GRP:
56                         break;
57                 default:
58                         return NT_STATUS_INVALID_SID;
59         }
60
61         return rpccli_samr_OpenAlias(pipe_cli, mem_ctx,
62                                      domain_handle,
63                                      access_rights,
64                                      user_rids.ids[0],
65                                      alias_handle);
66 }
67
68 /****************************************************************
69 ****************************************************************/
70
71 static NTSTATUS libnetapi_samr_open_alias_queryinfo(TALLOC_CTX *mem_ctx,
72                                                     struct rpc_pipe_client *pipe_cli,
73                                                     struct policy_handle *handle,
74                                                     uint32_t rid,
75                                                     uint32_t access_rights,
76                                                     enum samr_AliasInfoEnum level,
77                                                     union samr_AliasInfo **alias_info)
78 {
79         NTSTATUS status;
80         struct policy_handle alias_handle;
81         union samr_AliasInfo *_alias_info = NULL;
82
83         ZERO_STRUCT(alias_handle);
84
85         status = rpccli_samr_OpenAlias(pipe_cli, mem_ctx,
86                                        handle,
87                                        access_rights,
88                                        rid,
89                                        &alias_handle);
90         if (!NT_STATUS_IS_OK(status)) {
91                 goto done;
92         }
93
94         status = rpccli_samr_QueryAliasInfo(pipe_cli, mem_ctx,
95                                             &alias_handle,
96                                             level,
97                                             &_alias_info);
98         if (!NT_STATUS_IS_OK(status)) {
99                 goto done;
100         }
101
102         *alias_info = _alias_info;
103
104  done:
105         if (is_valid_policy_hnd(&alias_handle)) {
106                 rpccli_samr_Close(pipe_cli, mem_ctx, &alias_handle);
107         }
108
109         return status;
110 }
111
112 /****************************************************************
113 ****************************************************************/
114
115 WERROR NetLocalGroupAdd_r(struct libnetapi_ctx *ctx,
116                           struct NetLocalGroupAdd *r)
117 {
118         struct rpc_pipe_client *pipe_cli = NULL;
119         NTSTATUS status;
120         WERROR werr;
121         struct lsa_String lsa_account_name;
122         struct policy_handle connect_handle, domain_handle, builtin_handle, alias_handle;
123         struct dom_sid2 *domain_sid = NULL;
124         uint32_t rid;
125
126         struct LOCALGROUP_INFO_0 *info0 = NULL;
127         struct LOCALGROUP_INFO_1 *info1 = NULL;
128
129         const char *alias_name = NULL;
130
131         if (!r->in.buffer) {
132                 return WERR_INVALID_PARAM;
133         }
134
135         switch (r->in.level) {
136                 case 0:
137                         info0 = (struct LOCALGROUP_INFO_0 *)r->in.buffer;
138                         alias_name = info0->lgrpi0_name;
139                         break;
140                 case 1:
141                         info1 = (struct LOCALGROUP_INFO_1 *)r->in.buffer;
142                         alias_name = info1->lgrpi1_name;
143                         break;
144                 default:
145                         werr = WERR_UNKNOWN_LEVEL;
146                         goto done;
147         }
148
149         ZERO_STRUCT(connect_handle);
150         ZERO_STRUCT(builtin_handle);
151         ZERO_STRUCT(domain_handle);
152         ZERO_STRUCT(alias_handle);
153
154         werr = libnetapi_open_pipe(ctx, r->in.server_name,
155                                    &ndr_table_samr.syntax_id,
156                                    &pipe_cli);
157         if (!W_ERROR_IS_OK(werr)) {
158                 goto done;
159         }
160
161         werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli,
162                                                   SAMR_ACCESS_LOOKUP_DOMAIN |
163                                                   SAMR_ACCESS_ENUM_DOMAINS,
164                                                   SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
165                                                   &connect_handle,
166                                                   &builtin_handle);
167         if (!W_ERROR_IS_OK(werr)) {
168                 goto done;
169         }
170
171         status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli,
172                                                       &builtin_handle,
173                                                       alias_name,
174                                                       SAMR_ALIAS_ACCESS_LOOKUP_INFO,
175                                                       &alias_handle);
176         if (ctx->disable_policy_handle_cache) {
177                 libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
178         }
179
180         if (NT_STATUS_IS_OK(status)) {
181                 werr = WERR_ALIAS_EXISTS;
182                 goto done;
183         }
184
185         werr = libnetapi_samr_open_domain(ctx, pipe_cli,
186                                           SAMR_ACCESS_ENUM_DOMAINS |
187                                           SAMR_ACCESS_LOOKUP_DOMAIN,
188                                           SAMR_DOMAIN_ACCESS_CREATE_ALIAS |
189                                           SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
190                                           &connect_handle,
191                                           &domain_handle,
192                                           &domain_sid);
193         if (!W_ERROR_IS_OK(werr)) {
194                 goto done;
195         }
196
197         init_lsa_String(&lsa_account_name, alias_name);
198
199         status = rpccli_samr_CreateDomAlias(pipe_cli, talloc_tos(),
200                                             &domain_handle,
201                                             &lsa_account_name,
202                                             SEC_STD_DELETE |
203                                             SAMR_ALIAS_ACCESS_SET_INFO,
204                                             &alias_handle,
205                                             &rid);
206         if (!NT_STATUS_IS_OK(status)) {
207                 werr = ntstatus_to_werror(status);
208                 goto done;
209         }
210
211         if (r->in.level == 1 && info1->lgrpi1_comment) {
212
213                 union samr_AliasInfo alias_info;
214
215                 init_lsa_String(&alias_info.description, info1->lgrpi1_comment);
216
217                 status = rpccli_samr_SetAliasInfo(pipe_cli, talloc_tos(),
218                                                   &alias_handle,
219                                                   ALIASINFODESCRIPTION,
220                                                   &alias_info);
221                 if (!NT_STATUS_IS_OK(status)) {
222                         werr = ntstatus_to_werror(status);
223                         goto done;
224                 }
225         }
226
227         werr = WERR_OK;
228
229  done:
230         if (is_valid_policy_hnd(&alias_handle)) {
231                 rpccli_samr_Close(pipe_cli, talloc_tos(), &alias_handle);
232         }
233
234         if (ctx->disable_policy_handle_cache) {
235                 libnetapi_samr_close_domain_handle(ctx, &domain_handle);
236                 libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
237                 libnetapi_samr_close_connect_handle(ctx, &connect_handle);
238         }
239
240         return werr;
241 }
242
243 /****************************************************************
244 ****************************************************************/
245
246 WERROR NetLocalGroupAdd_l(struct libnetapi_ctx *ctx,
247                           struct NetLocalGroupAdd *r)
248 {
249         LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupAdd);
250 }
251
252 /****************************************************************
253 ****************************************************************/
254
255
256 WERROR NetLocalGroupDel_r(struct libnetapi_ctx *ctx,
257                           struct NetLocalGroupDel *r)
258 {
259         struct rpc_pipe_client *pipe_cli = NULL;
260         NTSTATUS status;
261         WERROR werr;
262         struct policy_handle connect_handle, domain_handle, builtin_handle, alias_handle;
263         struct dom_sid2 *domain_sid = NULL;
264
265         if (!r->in.group_name) {
266                 return WERR_INVALID_PARAM;
267         }
268
269         ZERO_STRUCT(connect_handle);
270         ZERO_STRUCT(builtin_handle);
271         ZERO_STRUCT(domain_handle);
272         ZERO_STRUCT(alias_handle);
273
274         werr = libnetapi_open_pipe(ctx, r->in.server_name,
275                                    &ndr_table_samr.syntax_id,
276                                    &pipe_cli);
277         if (!W_ERROR_IS_OK(werr)) {
278                 goto done;
279         }
280
281         werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli,
282                                                   SAMR_ACCESS_LOOKUP_DOMAIN |
283                                                   SAMR_ACCESS_ENUM_DOMAINS,
284                                                   SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
285                                                   &connect_handle,
286                                                   &builtin_handle);
287         if (!W_ERROR_IS_OK(werr)) {
288                 goto done;
289         }
290
291         status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli,
292                                                       &builtin_handle,
293                                                       r->in.group_name,
294                                                       SEC_STD_DELETE,
295                                                       &alias_handle);
296
297         if (ctx->disable_policy_handle_cache) {
298                 libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
299         }
300
301         if (NT_STATUS_IS_OK(status)) {
302                 goto delete_alias;
303         }
304
305         werr = libnetapi_samr_open_domain(ctx, pipe_cli,
306                                           SAMR_ACCESS_ENUM_DOMAINS |
307                                           SAMR_ACCESS_LOOKUP_DOMAIN,
308                                           SAMR_DOMAIN_ACCESS_CREATE_ALIAS |
309                                           SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
310                                           &connect_handle,
311                                           &domain_handle,
312                                           &domain_sid);
313         if (!W_ERROR_IS_OK(werr)) {
314                 goto done;
315         }
316
317         status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli,
318                                                       &domain_handle,
319                                                       r->in.group_name,
320                                                       SEC_STD_DELETE,
321                                                       &alias_handle);
322
323         if (ctx->disable_policy_handle_cache) {
324                 libnetapi_samr_close_domain_handle(ctx, &domain_handle);
325         }
326
327         if (!NT_STATUS_IS_OK(status)) {
328                 werr = ntstatus_to_werror(status);
329                 goto done;
330         }
331
332
333  delete_alias:
334         status = rpccli_samr_DeleteDomAlias(pipe_cli, talloc_tos(),
335                                             &alias_handle);
336         if (!NT_STATUS_IS_OK(status)) {
337                 werr = ntstatus_to_werror(status);
338                 goto done;
339         }
340
341         ZERO_STRUCT(alias_handle);
342
343         werr = WERR_OK;
344
345  done:
346         if (is_valid_policy_hnd(&alias_handle)) {
347                 rpccli_samr_Close(pipe_cli, talloc_tos(), &alias_handle);
348         }
349
350         if (ctx->disable_policy_handle_cache) {
351                 libnetapi_samr_close_domain_handle(ctx, &domain_handle);
352                 libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
353                 libnetapi_samr_close_connect_handle(ctx, &connect_handle);
354         }
355
356         return werr;
357 }
358
359 /****************************************************************
360 ****************************************************************/
361
362 WERROR NetLocalGroupDel_l(struct libnetapi_ctx *ctx,
363                           struct NetLocalGroupDel *r)
364 {
365         LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupDel);
366 }
367
368 /****************************************************************
369 ****************************************************************/
370
371 static WERROR map_alias_info_to_buffer(TALLOC_CTX *mem_ctx,
372                                        const char *alias_name,
373                                        struct samr_AliasInfoAll *info,
374                                        uint32_t level,
375                                        uint32_t *entries_read,
376                                        uint8_t **buffer)
377 {
378         struct LOCALGROUP_INFO_0 g0;
379         struct LOCALGROUP_INFO_1 g1;
380         struct LOCALGROUP_INFO_1002 g1002;
381
382         switch (level) {
383                 case 0:
384                         g0.lgrpi0_name          = talloc_strdup(mem_ctx, alias_name);
385                         W_ERROR_HAVE_NO_MEMORY(g0.lgrpi0_name);
386
387                         ADD_TO_ARRAY(mem_ctx, struct LOCALGROUP_INFO_0, g0,
388                                      (struct LOCALGROUP_INFO_0 **)buffer, entries_read);
389
390                         break;
391                 case 1:
392                         g1.lgrpi1_name          = talloc_strdup(mem_ctx, alias_name);
393                         g1.lgrpi1_comment       = talloc_strdup(mem_ctx, info->description.string);
394                         W_ERROR_HAVE_NO_MEMORY(g1.lgrpi1_name);
395
396                         ADD_TO_ARRAY(mem_ctx, struct LOCALGROUP_INFO_1, g1,
397                                      (struct LOCALGROUP_INFO_1 **)buffer, entries_read);
398
399                         break;
400                 case 1002:
401                         g1002.lgrpi1002_comment = talloc_strdup(mem_ctx, info->description.string);
402
403                         ADD_TO_ARRAY(mem_ctx, struct LOCALGROUP_INFO_1002, g1002,
404                                      (struct LOCALGROUP_INFO_1002 **)buffer, entries_read);
405
406                         break;
407                 default:
408                         return WERR_UNKNOWN_LEVEL;
409         }
410
411         return WERR_OK;
412 }
413
414 /****************************************************************
415 ****************************************************************/
416
417 WERROR NetLocalGroupGetInfo_r(struct libnetapi_ctx *ctx,
418                               struct NetLocalGroupGetInfo *r)
419 {
420         struct rpc_pipe_client *pipe_cli = NULL;
421         NTSTATUS status;
422         WERROR werr;
423         struct policy_handle connect_handle, domain_handle, builtin_handle, alias_handle;
424         struct dom_sid2 *domain_sid = NULL;
425         union samr_AliasInfo *alias_info = NULL;
426         uint32_t entries_read = 0;
427
428         if (!r->in.group_name) {
429                 return WERR_INVALID_PARAM;
430         }
431
432         switch (r->in.level) {
433                 case 0:
434                 case 1:
435                 case 1002:
436                         break;
437                 default:
438                         return WERR_UNKNOWN_LEVEL;
439         }
440
441         ZERO_STRUCT(connect_handle);
442         ZERO_STRUCT(builtin_handle);
443         ZERO_STRUCT(domain_handle);
444         ZERO_STRUCT(alias_handle);
445
446         werr = libnetapi_open_pipe(ctx, r->in.server_name,
447                                    &ndr_table_samr.syntax_id,
448                                    &pipe_cli);
449         if (!W_ERROR_IS_OK(werr)) {
450                 goto done;
451         }
452
453         werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli,
454                                                   SAMR_ACCESS_LOOKUP_DOMAIN |
455                                                   SAMR_ACCESS_ENUM_DOMAINS,
456                                                   SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
457                                                   &connect_handle,
458                                                   &builtin_handle);
459         if (!W_ERROR_IS_OK(werr)) {
460                 goto done;
461         }
462
463         status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli,
464                                                       &builtin_handle,
465                                                       r->in.group_name,
466                                                       SAMR_ALIAS_ACCESS_LOOKUP_INFO,
467                                                       &alias_handle);
468
469         if (ctx->disable_policy_handle_cache) {
470                 libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
471         }
472
473         if (NT_STATUS_IS_OK(status)) {
474                 goto query_alias;
475         }
476
477         werr = libnetapi_samr_open_domain(ctx, pipe_cli,
478                                           SAMR_ACCESS_ENUM_DOMAINS |
479                                           SAMR_ACCESS_LOOKUP_DOMAIN,
480                                           SAMR_DOMAIN_ACCESS_CREATE_ALIAS |
481                                           SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
482                                           &connect_handle,
483                                           &domain_handle,
484                                           &domain_sid);
485         if (!W_ERROR_IS_OK(werr)) {
486                 goto done;
487         }
488
489         status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli,
490                                                       &domain_handle,
491                                                       r->in.group_name,
492                                                       SAMR_ALIAS_ACCESS_LOOKUP_INFO,
493                                                       &alias_handle);
494
495         if (ctx->disable_policy_handle_cache) {
496                 libnetapi_samr_close_domain_handle(ctx, &domain_handle);
497         }
498
499         if (!NT_STATUS_IS_OK(status)) {
500                 werr = ntstatus_to_werror(status);
501                 goto done;
502         }
503
504  query_alias:
505         status = rpccli_samr_QueryAliasInfo(pipe_cli, talloc_tos(),
506                                             &alias_handle,
507                                             ALIASINFOALL,
508                                             &alias_info);
509         if (!NT_STATUS_IS_OK(status)) {
510                 werr = ntstatus_to_werror(status);
511                 goto done;
512         }
513
514         werr = map_alias_info_to_buffer(ctx,
515                                         r->in.group_name,
516                                         &alias_info->all,
517                                         r->in.level, &entries_read,
518                                         r->out.buffer);
519
520  done:
521         if (is_valid_policy_hnd(&alias_handle)) {
522                 rpccli_samr_Close(pipe_cli, talloc_tos(), &alias_handle);
523         }
524
525         if (ctx->disable_policy_handle_cache) {
526                 libnetapi_samr_close_domain_handle(ctx, &domain_handle);
527                 libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
528                 libnetapi_samr_close_connect_handle(ctx, &connect_handle);
529         }
530
531         return werr;
532 }
533
534 /****************************************************************
535 ****************************************************************/
536
537 WERROR NetLocalGroupGetInfo_l(struct libnetapi_ctx *ctx,
538                               struct NetLocalGroupGetInfo *r)
539 {
540         LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupGetInfo);
541 }
542
543 /****************************************************************
544 ****************************************************************/
545
546 static WERROR map_buffer_to_alias_info(TALLOC_CTX *mem_ctx,
547                                        uint32_t level,
548                                        uint8_t *buffer,
549                                        enum samr_AliasInfoEnum *alias_level,
550                                        union samr_AliasInfo **alias_info)
551 {
552         struct LOCALGROUP_INFO_0 *info0;
553         struct LOCALGROUP_INFO_1 *info1;
554         struct LOCALGROUP_INFO_1002 *info1002;
555         union samr_AliasInfo *info = NULL;
556
557         info = TALLOC_ZERO_P(mem_ctx, union samr_AliasInfo);
558         W_ERROR_HAVE_NO_MEMORY(info);
559
560         switch (level) {
561                 case 0:
562                         info0 = (struct LOCALGROUP_INFO_0 *)buffer;
563                         init_lsa_String(&info->name, info0->lgrpi0_name);
564                         *alias_level = ALIASINFONAME;
565                         break;
566                 case 1:
567                         info1 = (struct LOCALGROUP_INFO_1 *)buffer;
568                         /* group name will be ignored */
569                         init_lsa_String(&info->description, info1->lgrpi1_comment);
570                         *alias_level = ALIASINFODESCRIPTION;
571                         break;
572                 case 1002:
573                         info1002 = (struct LOCALGROUP_INFO_1002 *)buffer;
574                         init_lsa_String(&info->description, info1002->lgrpi1002_comment);
575                         *alias_level = ALIASINFODESCRIPTION;
576                         break;
577         }
578
579         *alias_info = info;
580
581         return WERR_OK;
582 }
583
584 /****************************************************************
585 ****************************************************************/
586
587 WERROR NetLocalGroupSetInfo_r(struct libnetapi_ctx *ctx,
588                               struct NetLocalGroupSetInfo *r)
589 {
590         struct rpc_pipe_client *pipe_cli = NULL;
591         NTSTATUS status;
592         WERROR werr;
593         struct lsa_String lsa_account_name;
594         struct policy_handle connect_handle, domain_handle, builtin_handle, alias_handle;
595         struct dom_sid2 *domain_sid = NULL;
596         enum samr_AliasInfoEnum alias_level = 0;
597         union samr_AliasInfo *alias_info = NULL;
598
599         if (!r->in.group_name) {
600                 return WERR_INVALID_PARAM;
601         }
602
603         switch (r->in.level) {
604                 case 0:
605                 case 1:
606                 case 1002:
607                         break;
608                 default:
609                         return WERR_UNKNOWN_LEVEL;
610         }
611
612         ZERO_STRUCT(connect_handle);
613         ZERO_STRUCT(builtin_handle);
614         ZERO_STRUCT(domain_handle);
615         ZERO_STRUCT(alias_handle);
616
617         werr = libnetapi_open_pipe(ctx, r->in.server_name,
618                                    &ndr_table_samr.syntax_id,
619                                    &pipe_cli);
620         if (!W_ERROR_IS_OK(werr)) {
621                 goto done;
622         }
623
624         werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli,
625                                                   SAMR_ACCESS_LOOKUP_DOMAIN |
626                                                   SAMR_ACCESS_ENUM_DOMAINS,
627                                                   SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
628                                                   &connect_handle,
629                                                   &builtin_handle);
630         if (!W_ERROR_IS_OK(werr)) {
631                 goto done;
632         }
633
634         init_lsa_String(&lsa_account_name, r->in.group_name);
635
636         status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli,
637                                                       &builtin_handle,
638                                                       r->in.group_name,
639                                                       SAMR_ALIAS_ACCESS_SET_INFO,
640                                                       &alias_handle);
641
642         if (ctx->disable_policy_handle_cache) {
643                 libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
644         }
645
646         if (NT_STATUS_IS_OK(status)) {
647                 goto set_alias;
648         }
649
650         werr = libnetapi_samr_open_domain(ctx, pipe_cli,
651                                           SAMR_ACCESS_ENUM_DOMAINS |
652                                           SAMR_ACCESS_LOOKUP_DOMAIN,
653                                           SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
654                                           &connect_handle,
655                                           &domain_handle,
656                                           &domain_sid);
657         if (!W_ERROR_IS_OK(werr)) {
658                 goto done;
659         }
660
661         status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli,
662                                                       &domain_handle,
663                                                       r->in.group_name,
664                                                       SAMR_ALIAS_ACCESS_SET_INFO,
665                                                       &alias_handle);
666         if (!NT_STATUS_IS_OK(status)) {
667                 werr = ntstatus_to_werror(status);
668                 goto done;
669         }
670
671         if (ctx->disable_policy_handle_cache) {
672                 libnetapi_samr_close_domain_handle(ctx, &domain_handle);
673         }
674
675  set_alias:
676
677         werr = map_buffer_to_alias_info(ctx, r->in.level, r->in.buffer,
678                                         &alias_level, &alias_info);
679         if (!W_ERROR_IS_OK(werr)) {
680                 goto done;
681         }
682
683         status = rpccli_samr_SetAliasInfo(pipe_cli, talloc_tos(),
684                                           &alias_handle,
685                                           alias_level,
686                                           alias_info);
687         if (!NT_STATUS_IS_OK(status)) {
688                 werr = ntstatus_to_werror(status);
689                 goto done;
690         }
691
692         werr = WERR_OK;
693
694  done:
695         if (is_valid_policy_hnd(&alias_handle)) {
696                 rpccli_samr_Close(pipe_cli, talloc_tos(), &alias_handle);
697         }
698
699         if (ctx->disable_policy_handle_cache) {
700                 libnetapi_samr_close_domain_handle(ctx, &domain_handle);
701                 libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
702                 libnetapi_samr_close_connect_handle(ctx, &connect_handle);
703         }
704
705         return werr;
706 }
707
708 /****************************************************************
709 ****************************************************************/
710
711 WERROR NetLocalGroupSetInfo_l(struct libnetapi_ctx *ctx,
712                               struct NetLocalGroupSetInfo *r)
713 {
714         LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupSetInfo);
715 }
716
717 /****************************************************************
718 ****************************************************************/
719
720 WERROR NetLocalGroupEnum_r(struct libnetapi_ctx *ctx,
721                            struct NetLocalGroupEnum *r)
722 {
723         struct rpc_pipe_client *pipe_cli = NULL;
724         NTSTATUS status;
725         WERROR werr;
726         struct policy_handle connect_handle, domain_handle, builtin_handle, alias_handle;
727         struct dom_sid2 *domain_sid = NULL;
728         uint32_t entries_read = 0;
729         union samr_DomainInfo *domain_info = NULL;
730         union samr_DomainInfo *builtin_info = NULL;
731         struct samr_SamArray *domain_sam_array = NULL;
732         struct samr_SamArray *builtin_sam_array = NULL;
733         int i;
734
735         if (!r->out.buffer) {
736                 return WERR_INVALID_PARAM;
737         }
738
739         switch (r->in.level) {
740                 case 0:
741                 case 1:
742                         break;
743                 default:
744                         return WERR_UNKNOWN_LEVEL;
745         }
746
747         if (r->out.total_entries) {
748                 *r->out.total_entries = 0;
749         }
750         if (r->out.entries_read) {
751                 *r->out.entries_read = 0;
752         }
753
754         ZERO_STRUCT(connect_handle);
755         ZERO_STRUCT(builtin_handle);
756         ZERO_STRUCT(domain_handle);
757         ZERO_STRUCT(alias_handle);
758
759         werr = libnetapi_open_pipe(ctx, r->in.server_name,
760                                    &ndr_table_samr.syntax_id,
761                                    &pipe_cli);
762         if (!W_ERROR_IS_OK(werr)) {
763                 goto done;
764         }
765
766         werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli,
767                                                   SAMR_ACCESS_LOOKUP_DOMAIN |
768                                                   SAMR_ACCESS_ENUM_DOMAINS,
769                                                   SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 |
770                                                   SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS |
771                                                   SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
772                                                   &connect_handle,
773                                                   &builtin_handle);
774         if (!W_ERROR_IS_OK(werr)) {
775                 goto done;
776         }
777
778         werr = libnetapi_samr_open_domain(ctx, pipe_cli,
779                                           SAMR_ACCESS_LOOKUP_DOMAIN |
780                                           SAMR_ACCESS_ENUM_DOMAINS,
781                                           SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2 |
782                                           SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS |
783                                           SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
784                                           &connect_handle,
785                                           &domain_handle,
786                                           &domain_sid);
787         if (!W_ERROR_IS_OK(werr)) {
788                 goto done;
789         }
790
791         status = rpccli_samr_QueryDomainInfo(pipe_cli, talloc_tos(),
792                                              &builtin_handle,
793                                              2,
794                                              &builtin_info);
795         if (!NT_STATUS_IS_OK(status)) {
796                 werr = ntstatus_to_werror(status);
797                 goto done;
798         }
799
800         if (r->out.total_entries) {
801                 *r->out.total_entries += builtin_info->general.num_aliases;
802         }
803
804         status = rpccli_samr_QueryDomainInfo(pipe_cli, talloc_tos(),
805                                              &domain_handle,
806                                              2,
807                                              &domain_info);
808         if (!NT_STATUS_IS_OK(status)) {
809                 werr = ntstatus_to_werror(status);
810                 goto done;
811         }
812
813         if (r->out.total_entries) {
814                 *r->out.total_entries += domain_info->general.num_aliases;
815         }
816
817         status = rpccli_samr_EnumDomainAliases(pipe_cli, talloc_tos(),
818                                                &builtin_handle,
819                                                r->in.resume_handle,
820                                                &builtin_sam_array,
821                                                r->in.prefmaxlen,
822                                                &entries_read);
823         if (!NT_STATUS_IS_OK(status)) {
824                 werr = ntstatus_to_werror(status);
825                 goto done;
826         }
827
828         for (i=0; i<builtin_sam_array->count; i++) {
829                 union samr_AliasInfo *alias_info = NULL;
830
831                 if (r->in.level == 1) {
832
833                         status = libnetapi_samr_open_alias_queryinfo(ctx, pipe_cli,
834                                                                      &builtin_handle,
835                                                                      builtin_sam_array->entries[i].idx,
836                                                                      SAMR_ALIAS_ACCESS_LOOKUP_INFO,
837                                                                      ALIASINFOALL,
838                                                                      &alias_info);
839                         if (!NT_STATUS_IS_OK(status)) {
840                                 werr = ntstatus_to_werror(status);
841                                 goto done;
842                         }
843                 }
844
845                 werr = map_alias_info_to_buffer(ctx,
846                                                 builtin_sam_array->entries[i].name.string,
847                                                 alias_info ? &alias_info->all : NULL,
848                                                 r->in.level,
849                                                 r->out.entries_read,
850                                                 r->out.buffer);
851         }
852
853         status = rpccli_samr_EnumDomainAliases(pipe_cli, talloc_tos(),
854                                                &domain_handle,
855                                                r->in.resume_handle,
856                                                &domain_sam_array,
857                                                r->in.prefmaxlen,
858                                                &entries_read);
859         if (!NT_STATUS_IS_OK(status)) {
860                 werr = ntstatus_to_werror(status);
861                 goto done;
862         }
863
864         for (i=0; i<domain_sam_array->count; i++) {
865
866                 union samr_AliasInfo *alias_info = NULL;
867
868                 if (r->in.level == 1) {
869                         status = libnetapi_samr_open_alias_queryinfo(ctx, pipe_cli,
870                                                                      &domain_handle,
871                                                                      domain_sam_array->entries[i].idx,
872                                                                      SAMR_ALIAS_ACCESS_LOOKUP_INFO,
873                                                                      ALIASINFOALL,
874                                                                      &alias_info);
875                         if (!NT_STATUS_IS_OK(status)) {
876                                 werr = ntstatus_to_werror(status);
877                                 goto done;
878                         }
879                 }
880
881                 werr = map_alias_info_to_buffer(ctx,
882                                                 domain_sam_array->entries[i].name.string,
883                                                 alias_info ? &alias_info->all : NULL,
884                                                 r->in.level,
885                                                 r->out.entries_read,
886                                                 r->out.buffer);
887         }
888
889  done:
890         if (ctx->disable_policy_handle_cache) {
891                 libnetapi_samr_close_domain_handle(ctx, &domain_handle);
892                 libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
893                 libnetapi_samr_close_connect_handle(ctx, &connect_handle);
894         }
895
896         return werr;
897 }
898
899 /****************************************************************
900 ****************************************************************/
901
902 WERROR NetLocalGroupEnum_l(struct libnetapi_ctx *ctx,
903                            struct NetLocalGroupEnum *r)
904 {
905         LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupEnum);
906 }
907
908 /****************************************************************
909 ****************************************************************/
910
911 static NTSTATUS libnetapi_lsa_lookup_names3(TALLOC_CTX *mem_ctx,
912                                             struct rpc_pipe_client *lsa_pipe,
913                                             const char *name,
914                                             struct dom_sid *sid)
915 {
916         NTSTATUS status;
917         struct policy_handle lsa_handle;
918
919         struct lsa_RefDomainList *domains = NULL;
920         struct lsa_TransSidArray3 sids;
921         uint32_t count = 0;
922
923         struct lsa_String names;
924         uint32_t num_names = 1;
925
926         if (!sid || !name) {
927                 return NT_STATUS_INVALID_PARAMETER;
928         }
929
930         ZERO_STRUCT(sids);
931
932         init_lsa_String(&names, name);
933
934         status = rpccli_lsa_open_policy2(lsa_pipe, mem_ctx,
935                                          false,
936                                          STD_RIGHT_READ_CONTROL_ACCESS |
937                                          LSA_POLICY_VIEW_LOCAL_INFORMATION |
938                                          LSA_POLICY_LOOKUP_NAMES,
939                                          &lsa_handle);
940         NT_STATUS_NOT_OK_RETURN(status);
941
942         status = rpccli_lsa_LookupNames3(lsa_pipe, mem_ctx,
943                                          &lsa_handle,
944                                          num_names,
945                                          &names,
946                                          &domains,
947                                          &sids,
948                                          LSA_LOOKUP_NAMES_ALL, /* sure ? */
949                                          &count,
950                                          0, 0);
951         NT_STATUS_NOT_OK_RETURN(status);
952
953         if (count != 1 || sids.count != 1) {
954                 return NT_STATUS_NONE_MAPPED;
955         }
956
957         sid_copy(sid, sids.sids[0].sid);
958
959         return NT_STATUS_OK;
960 }
961
962 /****************************************************************
963 ****************************************************************/
964
965 static WERROR NetLocalGroupModifyMembers_r(struct libnetapi_ctx *ctx,
966                                            struct NetLocalGroupAddMembers *add,
967                                            struct NetLocalGroupDelMembers *del,
968                                            struct NetLocalGroupSetMembers *set)
969 {
970         struct NetLocalGroupAddMembers *r = NULL;
971
972         struct rpc_pipe_client *pipe_cli = NULL;
973         struct rpc_pipe_client *lsa_pipe = NULL;
974         NTSTATUS status;
975         WERROR werr;
976         struct lsa_String lsa_account_name;
977         struct policy_handle connect_handle, domain_handle, builtin_handle, alias_handle;
978         struct dom_sid2 *domain_sid = NULL;
979         struct dom_sid *member_sids = NULL;
980         int i = 0, k = 0;
981
982         struct LOCALGROUP_MEMBERS_INFO_0 *info0 = NULL;
983         struct LOCALGROUP_MEMBERS_INFO_3 *info3 = NULL;
984
985         struct dom_sid *add_sids = NULL;
986         struct dom_sid *del_sids = NULL;
987         size_t num_add_sids = 0;
988         size_t num_del_sids = 0;
989
990         if ((!add && !del && !set) || (add && del && set)) {
991                 return WERR_INVALID_PARAM;
992         }
993
994         if (add) {
995                 r = add;
996         }
997
998         if (del) {
999                 r = (struct NetLocalGroupAddMembers *)del;
1000         }
1001
1002         if (set) {
1003                 r = (struct NetLocalGroupAddMembers *)set;
1004         }
1005
1006         if (!r->in.group_name) {
1007                 return WERR_INVALID_PARAM;
1008         }
1009
1010         switch (r->in.level) {
1011                 case 0:
1012                 case 3:
1013                         break;
1014                 default:
1015                         return WERR_UNKNOWN_LEVEL;
1016         }
1017
1018         if (r->in.total_entries == 0 || !r->in.buffer) {
1019                 return WERR_INVALID_PARAM;
1020         }
1021
1022         ZERO_STRUCT(connect_handle);
1023         ZERO_STRUCT(builtin_handle);
1024         ZERO_STRUCT(domain_handle);
1025         ZERO_STRUCT(alias_handle);
1026
1027         member_sids = TALLOC_ZERO_ARRAY(ctx, struct dom_sid,
1028                                         r->in.total_entries);
1029         W_ERROR_HAVE_NO_MEMORY(member_sids);
1030
1031         switch (r->in.level) {
1032                 case 0:
1033                         info0 = (struct LOCALGROUP_MEMBERS_INFO_0 *)r->in.buffer;
1034                         for (i=0; i < r->in.total_entries; i++) {
1035                                 sid_copy(&member_sids[i], (struct dom_sid *)info0[i].lgrmi0_sid);
1036                         }
1037                         break;
1038                 case 3:
1039                         info3 = (struct LOCALGROUP_MEMBERS_INFO_3 *)r->in.buffer;
1040                         break;
1041                 default:
1042                         break;
1043         }
1044
1045         if (r->in.level == 3) {
1046                 werr = libnetapi_open_pipe(ctx, r->in.server_name,
1047                                            &ndr_table_lsarpc.syntax_id,
1048                                            &lsa_pipe);
1049                 if (!W_ERROR_IS_OK(werr)) {
1050                         goto done;
1051                 }
1052
1053                 for (i=0; i < r->in.total_entries; i++) {
1054                         status = libnetapi_lsa_lookup_names3(ctx, lsa_pipe,
1055                                                              info3[i].lgrmi3_domainandname,
1056                                                              &member_sids[i]);
1057                         if (!NT_STATUS_IS_OK(status)) {
1058                                 werr = ntstatus_to_werror(status);
1059                                 goto done;
1060                         }
1061                 }
1062                 TALLOC_FREE(lsa_pipe);
1063         }
1064
1065         werr = libnetapi_open_pipe(ctx, r->in.server_name,
1066                                    &ndr_table_samr.syntax_id,
1067                                    &pipe_cli);
1068         if (!W_ERROR_IS_OK(werr)) {
1069                 goto done;
1070         }
1071
1072         werr = libnetapi_samr_open_builtin_domain(ctx, pipe_cli,
1073                                                   SAMR_ACCESS_LOOKUP_DOMAIN |
1074                                                   SAMR_ACCESS_ENUM_DOMAINS,
1075                                                   SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
1076                                                   &connect_handle,
1077                                                   &builtin_handle);
1078         if (!W_ERROR_IS_OK(werr)) {
1079                 goto done;
1080         }
1081
1082         init_lsa_String(&lsa_account_name, r->in.group_name);
1083
1084         status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli,
1085                                                       &builtin_handle,
1086                                                       r->in.group_name,
1087                                                       SAMR_ALIAS_ACCESS_ADD_MEMBER |
1088                                                       SAMR_ALIAS_ACCESS_REMOVE_MEMBER |
1089                                                       SAMR_ALIAS_ACCESS_GET_MEMBERS |
1090                                                       SAMR_ALIAS_ACCESS_LOOKUP_INFO,
1091                                                       &alias_handle);
1092
1093         if (ctx->disable_policy_handle_cache) {
1094                 libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
1095         }
1096
1097         if (NT_STATUS_IS_OK(status)) {
1098                 goto modify_membership;
1099         }
1100
1101         werr = libnetapi_samr_open_domain(ctx, pipe_cli,
1102                                           SAMR_ACCESS_ENUM_DOMAINS |
1103                                           SAMR_ACCESS_LOOKUP_DOMAIN,
1104                                           SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
1105                                           &connect_handle,
1106                                           &domain_handle,
1107                                           &domain_sid);
1108         if (!W_ERROR_IS_OK(werr)) {
1109                 goto done;
1110         }
1111
1112         status = libnetapi_samr_lookup_and_open_alias(ctx, pipe_cli,
1113                                                       &domain_handle,
1114                                                       r->in.group_name,
1115                                                       SAMR_ALIAS_ACCESS_ADD_MEMBER |
1116                                                       SAMR_ALIAS_ACCESS_REMOVE_MEMBER |
1117                                                       SAMR_ALIAS_ACCESS_GET_MEMBERS |
1118                                                       SAMR_ALIAS_ACCESS_LOOKUP_INFO,
1119                                                       &alias_handle);
1120         if (!NT_STATUS_IS_OK(status)) {
1121                 werr = ntstatus_to_werror(status);
1122                 goto done;
1123         }
1124
1125         if (ctx->disable_policy_handle_cache) {
1126                 libnetapi_samr_close_domain_handle(ctx, &domain_handle);
1127         }
1128
1129  modify_membership:
1130
1131         if (add) {
1132                 for (i=0; i < r->in.total_entries; i++) {
1133                         status = add_sid_to_array_unique(ctx, &member_sids[i],
1134                                                          &add_sids,
1135                                                          &num_add_sids);
1136                         if (!NT_STATUS_IS_OK(status)) {
1137                                 werr = ntstatus_to_werror(status);
1138                                 goto done;
1139                         }
1140                 }
1141         }
1142
1143         if (del) {
1144                 for (i=0; i < r->in.total_entries; i++) {
1145                         status = add_sid_to_array_unique(ctx, &member_sids[i],
1146                                                          &del_sids,
1147                                                          &num_del_sids);
1148                         if (!NT_STATUS_IS_OK(status)) {
1149                                 werr = ntstatus_to_werror(status);
1150                                 goto done;
1151                         }
1152                 }
1153         }
1154
1155         if (set) {
1156
1157                 struct lsa_SidArray current_sids;
1158
1159                 status = rpccli_samr_GetMembersInAlias(pipe_cli, talloc_tos(),
1160                                                        &alias_handle,
1161                                                        &current_sids);
1162                 if (!NT_STATUS_IS_OK(status)) {
1163                         werr = ntstatus_to_werror(status);
1164                         goto done;
1165                 }
1166
1167                 /* add list */
1168
1169                 for (i=0; i < r->in.total_entries; i++) {
1170                         bool already_member = false;
1171                         for (k=0; k < current_sids.num_sids; k++) {
1172                                 if (sid_equal(&member_sids[i],
1173                                               current_sids.sids[k].sid)) {
1174                                         already_member = true;
1175                                         break;
1176                                 }
1177                         }
1178                         if (!already_member) {
1179                                 status = add_sid_to_array_unique(ctx,
1180                                         &member_sids[i],
1181                                         &add_sids, &num_add_sids);
1182                                 if (!NT_STATUS_IS_OK(status)) {
1183                                         werr = ntstatus_to_werror(status);
1184                                         goto done;
1185                                 }
1186                         }
1187                 }
1188
1189                 /* del list */
1190
1191                 for (k=0; k < current_sids.num_sids; k++) {
1192                         bool keep_member = false;
1193                         for (i=0; i < r->in.total_entries; i++) {
1194                                 if (sid_equal(&member_sids[i],
1195                                               current_sids.sids[k].sid)) {
1196                                         keep_member = true;
1197                                         break;
1198                                 }
1199                         }
1200                         if (!keep_member) {
1201                                 status = add_sid_to_array_unique(ctx,
1202                                                 current_sids.sids[k].sid,
1203                                                 &del_sids, &num_del_sids);
1204                                 if (!NT_STATUS_IS_OK(status)) {
1205                                         werr = ntstatus_to_werror(status);
1206                                         goto done;
1207                                 }
1208                         }
1209                 }
1210         }
1211
1212         /* add list */
1213
1214         for (i=0; i < num_add_sids; i++) {
1215                 status = rpccli_samr_AddAliasMember(pipe_cli, talloc_tos(),
1216                                                     &alias_handle,
1217                                                     &add_sids[i]);
1218                 if (!NT_STATUS_IS_OK(status)) {
1219                         werr = ntstatus_to_werror(status);
1220                         goto done;
1221                 }
1222         }
1223
1224         /* del list */
1225
1226         for (i=0; i < num_del_sids; i++) {
1227                 status = rpccli_samr_DeleteAliasMember(pipe_cli, talloc_tos(),
1228                                                        &alias_handle,
1229                                                        &del_sids[i]);
1230                 if (!NT_STATUS_IS_OK(status)) {
1231                         werr = ntstatus_to_werror(status);
1232                         goto done;
1233                 }
1234         }
1235
1236         werr = WERR_OK;
1237
1238  done:
1239         if (is_valid_policy_hnd(&alias_handle)) {
1240                 rpccli_samr_Close(pipe_cli, talloc_tos(), &alias_handle);
1241         }
1242
1243         if (ctx->disable_policy_handle_cache) {
1244                 libnetapi_samr_close_domain_handle(ctx, &domain_handle);
1245                 libnetapi_samr_close_builtin_handle(ctx, &builtin_handle);
1246                 libnetapi_samr_close_connect_handle(ctx, &connect_handle);
1247         }
1248
1249         return werr;
1250 }
1251
1252 /****************************************************************
1253 ****************************************************************/
1254
1255 WERROR NetLocalGroupAddMembers_r(struct libnetapi_ctx *ctx,
1256                                  struct NetLocalGroupAddMembers *r)
1257 {
1258         return NetLocalGroupModifyMembers_r(ctx, r, NULL, NULL);
1259 }
1260
1261 /****************************************************************
1262 ****************************************************************/
1263
1264 WERROR NetLocalGroupAddMembers_l(struct libnetapi_ctx *ctx,
1265                                  struct NetLocalGroupAddMembers *r)
1266 {
1267         LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupAddMembers);
1268 }
1269
1270 /****************************************************************
1271 ****************************************************************/
1272
1273 WERROR NetLocalGroupDelMembers_r(struct libnetapi_ctx *ctx,
1274                                  struct NetLocalGroupDelMembers *r)
1275 {
1276         return NetLocalGroupModifyMembers_r(ctx, NULL, r, NULL);
1277 }
1278
1279 /****************************************************************
1280 ****************************************************************/
1281
1282 WERROR NetLocalGroupDelMembers_l(struct libnetapi_ctx *ctx,
1283                                  struct NetLocalGroupDelMembers *r)
1284 {
1285         LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupDelMembers);
1286 }
1287
1288 /****************************************************************
1289 ****************************************************************/
1290
1291 WERROR NetLocalGroupGetMembers_r(struct libnetapi_ctx *ctx,
1292                                  struct NetLocalGroupGetMembers *r)
1293 {
1294         return WERR_NOT_SUPPORTED;
1295 }
1296
1297 /****************************************************************
1298 ****************************************************************/
1299
1300 WERROR NetLocalGroupGetMembers_l(struct libnetapi_ctx *ctx,
1301                                  struct NetLocalGroupGetMembers *r)
1302 {
1303         LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupGetMembers);
1304 }
1305
1306 /****************************************************************
1307 ****************************************************************/
1308
1309 WERROR NetLocalGroupSetMembers_r(struct libnetapi_ctx *ctx,
1310                                  struct NetLocalGroupSetMembers *r)
1311 {
1312         return NetLocalGroupModifyMembers_r(ctx, NULL, NULL, r);
1313 }
1314
1315 /****************************************************************
1316 ****************************************************************/
1317
1318 WERROR NetLocalGroupSetMembers_l(struct libnetapi_ctx *ctx,
1319                                  struct NetLocalGroupSetMembers *r)
1320 {
1321         LIBNETAPI_REDIRECT_TO_LOCALHOST(ctx, r, NetLocalGroupSetMembers);
1322 }