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