Revoke some of the popt patch from metze I applied earlier today. It added
[ira/wip.git] / source3 / utils / smbcacls.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ACL get/set utility
4    
5    Copyright (C) Andrew Tridgell 2000
6    Copyright (C) Tim Potter      2000
7    Copyright (C) Jeremy Allison  2000
8    Copyright (C) Jelmer Vernooij 2003
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26
27 static pstring owner_username;
28 static fstring server;
29 static int test_args = False;
30 static TALLOC_CTX *ctx;
31
32 #define CREATE_ACCESS_READ READ_CONTROL_ACCESS
33 #define CREATE_ACCESS_WRITE (WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS)
34
35 /* numeric is set when the user wants numeric SIDs and ACEs rather
36    than going via LSA calls to resolve them */
37 static BOOL numeric = False;
38
39 enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
40 enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP};
41 enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
42
43 struct perm_value {
44         const char *perm;
45         uint32 mask;
46 };
47
48 /* These values discovered by inspection */
49
50 static const struct perm_value special_values[] = {
51         { "R", 0x00120089 },
52         { "W", 0x00120116 },
53         { "X", 0x001200a0 },
54         { "D", 0x00010000 },
55         { "P", 0x00040000 },
56         { "O", 0x00080000 },
57         { NULL, 0 },
58 };
59
60 static const struct perm_value standard_values[] = {
61         { "READ",   0x001200a9 },
62         { "CHANGE", 0x001301bf },
63         { "FULL",   0x001f01ff },
64         { NULL, 0 },
65 };
66
67 static struct cli_state *global_hack_cli;
68 static POLICY_HND pol;
69 static BOOL got_policy_hnd;
70
71 static struct cli_state *connect_one(const char *share);
72
73 /* Open cli connection and policy handle */
74
75 static BOOL cacls_open_policy_hnd(void)
76 {
77         /* Initialise cli LSA connection */
78
79         if (!global_hack_cli) {
80                 global_hack_cli = connect_one("IPC$");
81                 if (!cli_nt_session_open (global_hack_cli, PI_LSARPC)) {
82                                 return False;
83                 }
84         }
85         
86         /* Open policy handle */
87
88         if (!got_policy_hnd) {
89
90                 /* Some systems don't support SEC_RIGHTS_MAXIMUM_ALLOWED,
91                    but NT sends 0x2000000 so we might as well do it too. */
92
93                 if (!NT_STATUS_IS_OK(cli_lsa_open_policy(global_hack_cli, global_hack_cli->mem_ctx, True, 
94                                                          GENERIC_EXECUTE_ACCESS, &pol))) {
95                         return False;
96                 }
97
98                 got_policy_hnd = True;
99         }
100         
101         return True;
102 }
103
104 /* convert a SID to a string, either numeric or username/group */
105 static void SidToString(fstring str, DOM_SID *sid)
106 {
107         char **domains = NULL;
108         char **names = NULL;
109         uint32 *types = NULL;
110
111         sid_to_string(str, sid);
112
113         if (numeric) return;
114
115         /* Ask LSA to convert the sid to a name */
116
117         if (!cacls_open_policy_hnd() ||
118             !NT_STATUS_IS_OK(cli_lsa_lookup_sids(global_hack_cli, global_hack_cli->mem_ctx,  
119                                                  &pol, 1, sid, &domains, 
120                                                  &names, &types)) ||
121             !domains || !domains[0] || !names || !names[0]) {
122                 return;
123         }
124
125         /* Converted OK */
126
127         slprintf(str, sizeof(fstring) - 1, "%s%s%s",
128                  domains[0], lp_winbind_separator(),
129                  names[0]);
130         
131 }
132
133 /* convert a string to a SID, either numeric or username/group */
134 static BOOL StringToSid(DOM_SID *sid, const char *str)
135 {
136         uint32 *types = NULL;
137         DOM_SID *sids = NULL;
138         BOOL result = True;
139
140         if (strncmp(str, "S-", 2) == 0) {
141                 return string_to_sid(sid, str);
142         }
143
144         if (!cacls_open_policy_hnd() ||
145             !NT_STATUS_IS_OK(cli_lsa_lookup_names(global_hack_cli, global_hack_cli->mem_ctx, 
146                                                   &pol, 1, &str, &sids, 
147                                                   &types))) {
148                 result = False;
149                 goto done;
150         }
151
152         sid_copy(sid, &sids[0]);
153  done:
154
155         return result;
156 }
157
158
159 /* print an ACE on a FILE, using either numeric or ascii representation */
160 static void print_ace(FILE *f, SEC_ACE *ace)
161 {
162         const struct perm_value *v;
163         fstring sidstr;
164         int do_print = 0;
165         uint32 got_mask;
166
167         SidToString(sidstr, &ace->trustee);
168
169         fprintf(f, "%s:", sidstr);
170
171         if (numeric) {
172                 fprintf(f, "%d/%d/0x%08x", 
173                         ace->type, ace->flags, ace->info.mask);
174                 return;
175         }
176
177         /* Ace type */
178
179         if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
180                 fprintf(f, "ALLOWED");
181         } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
182                 fprintf(f, "DENIED");
183         } else {
184                 fprintf(f, "%d", ace->type);
185         }
186
187         /* Not sure what flags can be set in a file ACL */
188
189         fprintf(f, "/%d/", ace->flags);
190
191         /* Standard permissions */
192
193         for (v = standard_values; v->perm; v++) {
194                 if (ace->info.mask == v->mask) {
195                         fprintf(f, "%s", v->perm);
196                         return;
197                 }
198         }
199
200         /* Special permissions.  Print out a hex value if we have
201            leftover bits in the mask. */
202
203         got_mask = ace->info.mask;
204
205  again:
206         for (v = special_values; v->perm; v++) {
207                 if ((ace->info.mask & v->mask) == v->mask) {
208                         if (do_print) {
209                                 fprintf(f, "%s", v->perm);
210                         }
211                         got_mask &= ~v->mask;
212                 }
213         }
214
215         if (!do_print) {
216                 if (got_mask != 0) {
217                         fprintf(f, "0x%08x", ace->info.mask);
218                 } else {
219                         do_print = 1;
220                         goto again;
221                 }
222         }
223 }
224
225
226 /* parse an ACE in the same format as print_ace() */
227 static BOOL parse_ace(SEC_ACE *ace, char *str)
228 {
229         char *p;
230         const char *cp;
231         fstring tok;
232         unsigned atype, aflags, amask;
233         DOM_SID sid;
234         SEC_ACCESS mask;
235         const struct perm_value *v;
236
237         ZERO_STRUCTP(ace);
238         p = strchr_m(str,':');
239         if (!p) return False;
240         *p = '\0';
241         p++;
242         /* Try to parse numeric form */
243
244         if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
245             StringToSid(&sid, str)) {
246                 goto done;
247         }
248
249         /* Try to parse text form */
250
251         if (!StringToSid(&sid, str)) {
252                 return False;
253         }
254
255         cp = p;
256         if (!next_token(&cp, tok, "/", sizeof(fstring))) {
257                 return False;
258         }
259
260         if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
261                 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
262         } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
263                 atype = SEC_ACE_TYPE_ACCESS_DENIED;
264         } else {
265                 return False;
266         }
267
268         /* Only numeric form accepted for flags at present */
269
270         if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
271               sscanf(tok, "%i", &aflags))) {
272                 return False;
273         }
274
275         if (!next_token(&cp, tok, "/", sizeof(fstring))) {
276                 return False;
277         }
278
279         if (strncmp(tok, "0x", 2) == 0) {
280                 if (sscanf(tok, "%i", &amask) != 1) {
281                         return False;
282                 }
283                 goto done;
284         }
285
286         for (v = standard_values; v->perm; v++) {
287                 if (strcmp(tok, v->perm) == 0) {
288                         amask = v->mask;
289                         goto done;
290                 }
291         }
292
293         p = tok;
294
295         while(*p) {
296                 BOOL found = False;
297
298                 for (v = special_values; v->perm; v++) {
299                         if (v->perm[0] == *p) {
300                                 amask |= v->mask;
301                                 found = True;
302                         }
303                 }
304
305                 if (!found) return False;
306                 p++;
307         }
308
309         if (*p) {
310                 return False;
311         }
312
313  done:
314         mask.mask = amask;
315         init_sec_ace(ace, &sid, atype, mask, aflags);
316         return True;
317 }
318
319 /* add an ACE to a list of ACEs in a SEC_ACL */
320 static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace)
321 {
322         SEC_ACL *new;
323         SEC_ACE *aces;
324         if (! *the_acl) {
325                 (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
326                 return True;
327         }
328
329         aces = calloc(1+(*the_acl)->num_aces,sizeof(SEC_ACE));
330         memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
331         memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
332         new = make_sec_acl(ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
333         SAFE_FREE(aces);
334         (*the_acl) = new;
335         return True;
336 }
337
338 /* parse a ascii version of a security descriptor */
339 static SEC_DESC *sec_desc_parse(char *str)
340 {
341         const char *p = str;
342         fstring tok;
343         SEC_DESC *ret;
344         size_t sd_size;
345         DOM_SID *grp_sid=NULL, *owner_sid=NULL;
346         SEC_ACL *dacl=NULL;
347         int revision=1;
348
349         while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
350
351                 if (strncmp(tok,"REVISION:", 9) == 0) {
352                         revision = strtol(tok+9, NULL, 16);
353                         continue;
354                 }
355
356                 if (strncmp(tok,"OWNER:", 6) == 0) {
357                         owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
358                         if (!owner_sid ||
359                             !StringToSid(owner_sid, tok+6)) {
360                                 printf("Failed to parse owner sid\n");
361                                 return NULL;
362                         }
363                         continue;
364                 }
365
366                 if (strncmp(tok,"GROUP:", 6) == 0) {
367                         grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID));
368                         if (!grp_sid ||
369                             !StringToSid(grp_sid, tok+6)) {
370                                 printf("Failed to parse group sid\n");
371                                 return NULL;
372                         }
373                         continue;
374                 }
375
376                 if (strncmp(tok,"ACL:", 4) == 0) {
377                         SEC_ACE ace;
378                         if (!parse_ace(&ace, tok+4)) {
379                                 printf("Failed to parse ACL %s\n", tok);
380                                 return NULL;
381                         }
382                         if(!add_ace(&dacl, &ace)) {
383                                 printf("Failed to add ACL %s\n", tok);
384                                 return NULL;
385                         }
386                         continue;
387                 }
388
389                 printf("Failed to parse security descriptor\n");
390                 return NULL;
391         }
392
393         ret = make_sec_desc(ctx,revision, owner_sid, grp_sid, 
394                             NULL, dacl, &sd_size);
395
396         SAFE_FREE(grp_sid);
397         SAFE_FREE(owner_sid);
398
399         return ret;
400 }
401
402
403 /* print a ascii version of a security descriptor on a FILE handle */
404 static void sec_desc_print(FILE *f, SEC_DESC *sd)
405 {
406         fstring sidstr;
407         uint32 i;
408
409         printf("REVISION:%d\n", sd->revision);
410
411         /* Print owner and group sid */
412
413         if (sd->owner_sid) {
414                 SidToString(sidstr, sd->owner_sid);
415         } else {
416                 fstrcpy(sidstr, "");
417         }
418
419         printf("OWNER:%s\n", sidstr);
420
421         if (sd->grp_sid) {
422                 SidToString(sidstr, sd->grp_sid);
423         } else {
424                 fstrcpy(sidstr, "");
425         }
426
427         fprintf(f, "GROUP:%s\n", sidstr);
428
429         /* Print aces */
430         for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
431                 SEC_ACE *ace = &sd->dacl->ace[i];
432                 fprintf(f, "ACL:");
433                 print_ace(f, ace);
434                 fprintf(f, "\n");
435         }
436
437 }
438
439 /***************************************************** 
440 dump the acls for a file
441 *******************************************************/
442 static int cacl_dump(struct cli_state *cli, char *filename)
443 {
444         int result = EXIT_FAILED;
445         int fnum = -1;
446         SEC_DESC *sd;
447
448         if (test_args) 
449                 return EXIT_OK;
450
451         fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
452
453         if (fnum == -1) {
454                 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
455                 goto done;
456         }
457
458         sd = cli_query_secdesc(cli, fnum, ctx);
459
460         if (!sd) {
461                 printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli));
462                 goto done;
463         }
464
465         sec_desc_print(stdout, sd);
466
467         result = EXIT_OK;
468
469 done:
470         if (fnum != -1)
471                 cli_close(cli, fnum);
472
473         return result;
474 }
475
476 /***************************************************** 
477 Change the ownership or group ownership of a file. Just
478 because the NT docs say this can't be done :-). JRA.
479 *******************************************************/
480
481 static int owner_set(struct cli_state *cli, enum chown_mode change_mode, 
482                      char *filename, char *new_username)
483 {
484         int fnum;
485         DOM_SID sid;
486         SEC_DESC *sd, *old;
487         size_t sd_size;
488
489         fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
490
491         if (fnum == -1) {
492                 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
493                 return EXIT_FAILED;
494         }
495
496         if (!StringToSid(&sid, new_username))
497                 return EXIT_PARSE_ERROR;
498
499         old = cli_query_secdesc(cli, fnum, ctx);
500
501         cli_close(cli, fnum);
502
503         if (!old) {
504                 printf("owner_set: Failed to query old descriptor\n");
505                 return EXIT_FAILED;
506         }
507
508         sd = make_sec_desc(ctx,old->revision,
509                                 (change_mode == REQUEST_CHOWN) ? &sid : old->owner_sid,
510                                 (change_mode == REQUEST_CHGRP) ? &sid : old->grp_sid,
511                            NULL, old->dacl, &sd_size);
512
513         fnum = cli_nt_create(cli, filename, CREATE_ACCESS_WRITE);
514
515         if (fnum == -1) {
516                 printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
517                 return EXIT_FAILED;
518         }
519
520         if (!cli_set_secdesc(cli, fnum, sd)) {
521                 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
522         }
523
524         cli_close(cli, fnum);
525
526         return EXIT_OK;
527 }
528
529
530 /* The MSDN is contradictory over the ordering of ACE entries in an ACL.
531    However NT4 gives a "The information may have been modified by a
532    computer running Windows NT 5.0" if denied ACEs do not appear before
533    allowed ACEs. */
534
535 static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
536 {
537         if (sec_ace_equal(ace1, ace2)) 
538                 return 0;
539
540         if (ace1->type != ace2->type) 
541                 return ace2->type - ace1->type;
542
543         if (sid_compare(&ace1->trustee, &ace2->trustee)) 
544                 return sid_compare(&ace1->trustee, &ace2->trustee);
545
546         if (ace1->flags != ace2->flags) 
547                 return ace1->flags - ace2->flags;
548
549         if (ace1->info.mask != ace2->info.mask) 
550                 return ace1->info.mask - ace2->info.mask;
551
552         if (ace1->size != ace2->size) 
553                 return ace1->size - ace2->size;
554
555         return memcmp(ace1, ace2, sizeof(SEC_ACE));
556 }
557
558 static void sort_acl(SEC_ACL *the_acl)
559 {
560         uint32 i;
561         if (!the_acl) return;
562
563         qsort(the_acl->ace, the_acl->num_aces, sizeof(the_acl->ace[0]), QSORT_CAST ace_compare);
564
565         for (i=1;i<the_acl->num_aces;) {
566                 if (sec_ace_equal(&the_acl->ace[i-1], &the_acl->ace[i])) {
567                         int j;
568                         for (j=i; j<the_acl->num_aces-1; j++) {
569                                 the_acl->ace[j] = the_acl->ace[j+1];
570                         }
571                         the_acl->num_aces--;
572                 } else {
573                         i++;
574                 }
575         }
576 }
577
578 /***************************************************** 
579 set the ACLs on a file given an ascii description
580 *******************************************************/
581 static int cacl_set(struct cli_state *cli, char *filename, 
582                     char *the_acl, enum acl_mode mode)
583 {
584         int fnum;
585         SEC_DESC *sd, *old;
586         uint32 i, j;
587         size_t sd_size;
588         int result = EXIT_OK;
589
590         sd = sec_desc_parse(the_acl);
591
592         if (!sd) return EXIT_PARSE_ERROR;
593         if (test_args) return EXIT_OK;
594
595         /* The desired access below is the only one I could find that works
596            with NT4, W2KP and Samba */
597
598         fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
599
600         if (fnum == -1) {
601                 printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli));
602                 return EXIT_FAILED;
603         }
604
605         old = cli_query_secdesc(cli, fnum, ctx);
606
607         if (!old) {
608                 printf("calc_set: Failed to query old descriptor\n");
609                 return EXIT_FAILED;
610         }
611
612         cli_close(cli, fnum);
613
614         /* the logic here is rather more complex than I would like */
615         switch (mode) {
616         case SMB_ACL_DELETE:
617                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
618                         BOOL found = False;
619
620                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
621                                 if (sec_ace_equal(&sd->dacl->ace[i],
622                                                   &old->dacl->ace[j])) {
623                                         uint32 k;
624                                         for (k=j; k<old->dacl->num_aces-1;k++) {
625                                                 old->dacl->ace[k] = old->dacl->ace[k+1];
626                                         }
627                                         old->dacl->num_aces--;
628                                         if (old->dacl->num_aces == 0) {
629                                                 SAFE_FREE(old->dacl->ace);
630                                                 SAFE_FREE(old->dacl);
631                                                 old->off_dacl = 0;
632                                         }
633                                         found = True;
634                                         break;
635                                 }
636                         }
637
638                         if (!found) {
639                                 printf("ACL for ACE:"); 
640                                 print_ace(stdout, &sd->dacl->ace[i]);
641                                 printf(" not found\n");
642                         }
643                 }
644                 break;
645
646         case SMB_ACL_MODIFY:
647                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
648                         BOOL found = False;
649
650                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
651                                 if (sid_equal(&sd->dacl->ace[i].trustee,
652                                               &old->dacl->ace[j].trustee)) {
653                                         old->dacl->ace[j] = sd->dacl->ace[i];
654                                         found = True;
655                                 }
656                         }
657
658                         if (!found) {
659                                 fstring str;
660
661                                 SidToString(str, &sd->dacl->ace[i].trustee);
662                                 printf("ACL for SID %s not found\n", str);
663                         }
664                 }
665
666                 break;
667
668         case SMB_ACL_ADD:
669                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
670                         add_ace(&old->dacl, &sd->dacl->ace[i]);
671                 }
672                 break;
673
674         case SMB_ACL_SET:
675                 old = sd;
676                 break;
677         }
678
679         /* Denied ACE entries must come before allowed ones */
680         sort_acl(old->dacl);
681
682         /* Create new security descriptor and set it */
683         sd = make_sec_desc(ctx,old->revision, old->owner_sid, old->grp_sid, 
684                            NULL, old->dacl, &sd_size);
685
686         fnum = cli_nt_create(cli, filename, CREATE_ACCESS_WRITE);
687
688         if (fnum == -1) {
689                 printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli));
690                 return EXIT_FAILED;
691         }
692
693         if (!cli_set_secdesc(cli, fnum, sd)) {
694                 printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
695                 result = EXIT_FAILED;
696         }
697
698         /* Clean up */
699
700         cli_close(cli, fnum);
701
702         return result;
703 }
704
705
706 /***************************************************** 
707 return a connection to a server
708 *******************************************************/
709 static struct cli_state *connect_one(const char *share)
710 {
711         struct cli_state *c;
712         struct in_addr ip;
713         NTSTATUS nt_status;
714         zero_ip(&ip);
715         
716         if (!cmdline_auth_info.got_pass) {
717                 char *pass = getpass("Password: ");
718                 if (pass) {
719                         pstrcpy(cmdline_auth_info.password, pass);
720                         cmdline_auth_info.got_pass = True;
721                 }
722         }
723
724         if (NT_STATUS_IS_OK(nt_status = cli_full_connection(&c, global_myname(), server, 
725                                                             &ip, 0,
726                                                             share, "?????",  
727                                                             cmdline_auth_info.username, lp_workgroup(),
728                                                             cmdline_auth_info.password, 0, NULL))) {
729                 return c;
730         } else {
731                 DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
732                 return NULL;
733         }
734 }
735
736 /****************************************************************************
737   main program
738 ****************************************************************************/
739  int main(int argc, const char *argv[])
740 {
741         char *share;
742         int opt;
743         enum acl_mode mode = SMB_ACL_SET;
744         static char *the_acl = NULL;
745         enum chown_mode change_mode = REQUEST_NONE;
746         int result;
747         fstring path;
748         pstring filename;
749         poptContext pc;
750         struct poptOption long_options[] = {
751                 POPT_AUTOHELP
752                 { "delete", 'D', POPT_ARG_STRING, NULL, 'D', "Delete an acl", "ACL" },
753                 { "modify", 'M', POPT_ARG_STRING, NULL, 'M', "Modify an acl", "ACL" },
754                 { "add", 'A', POPT_ARG_STRING, NULL, 'A', "Add an acl", "ACL" },
755                 { "set", 'S', POPT_ARG_STRING, NULL, 'S', "Set acls", "ACLS" },
756                 { "chown", 'C', POPT_ARG_STRING, NULL, 'C', "Change ownership of a file", "USERNAME" },
757                 { "chgrp", 'G', POPT_ARG_STRING, NULL, 'G', "Change group ownership of a file", "GROUPNAME" },
758                 { "numeric", 0, POPT_ARG_NONE, &numeric, True, "Don't resolve sids or masks to names" },
759                 { "test-args", 't', POPT_ARG_NONE, &test_args, True, "Test arguments"},
760                 POPT_COMMON_SAMBA
761                 POPT_COMMON_CREDENTIALS
762                 { NULL }
763         };
764
765         struct cli_state *cli;
766
767         ctx=talloc_init("main");
768
769         setlinebuf(stdout);
770
771         dbf = x_stderr;
772
773         setup_logging(argv[0],True);
774
775         lp_load(dyn_CONFIGFILE,True,False,False);
776         load_interfaces();
777
778         pc = poptGetContext("smbcacls", argc, argv, long_options, 0);
779         
780         poptSetOtherOptionHelp(pc, "//server1/share1 filename");
781
782         while ((opt = poptGetNextOpt(pc)) != -1) {
783                 switch (opt) {
784                 case 'S':
785                         the_acl = smb_xstrdup(poptGetOptArg(pc));
786                         mode = SMB_ACL_SET;
787                         break;
788
789                 case 'D':
790                         the_acl = smb_xstrdup(poptGetOptArg(pc));
791                         mode = SMB_ACL_DELETE;
792                         break;
793
794                 case 'M':
795                         the_acl = smb_xstrdup(poptGetOptArg(pc));
796                         mode = SMB_ACL_MODIFY;
797                         break;
798
799                 case 'A':
800                         the_acl = smb_xstrdup(poptGetOptArg(pc));
801                         mode = SMB_ACL_ADD;
802                         break;
803
804                 case 'C':
805                         pstrcpy(owner_username,poptGetOptArg(pc));
806                         change_mode = REQUEST_CHOWN;
807                         break;
808
809                 case 'G':
810                         pstrcpy(owner_username,poptGetOptArg(pc));
811                         change_mode = REQUEST_CHGRP;
812                         break;
813                 }
814         }
815
816         /* Make connection to server */
817         if(!poptPeekArg(pc)) { 
818                 poptPrintUsage(pc, stderr, 0);
819                 return -1;
820         }
821         
822         fstrcpy(path, poptGetArg(pc));
823         
824         if(!poptPeekArg(pc)) { 
825                 poptPrintUsage(pc, stderr, 0);  
826                 return -1;
827         }
828         
829         pstrcpy(filename, poptGetArg(pc));
830
831         all_string_sub(path,"/","\\",0);
832
833         fstrcpy(server,path+2);
834         share = strchr_m(server,'\\');
835         if (!share) {
836                 share = strchr_m(server,'/');
837                 if (!share) {
838                         printf("Invalid argument: %s\n", share);
839                         return -1;
840                 }
841         }
842
843         *share = 0;
844         share++;
845
846         if (!test_args) {
847                 cli = connect_one(share);
848                 if (!cli) {
849                         talloc_destroy(ctx);
850                         exit(EXIT_FAILED);
851                 }
852         } else {
853                 exit(0);
854         }
855
856         all_string_sub(filename, "/", "\\", 0);
857         if (filename[0] != '\\') {
858                 pstring s;
859                 s[0] = '\\';
860                 safe_strcpy(&s[1], filename, sizeof(pstring)-2);
861                 pstrcpy(filename, s);
862         }
863
864         /* Perform requested action */
865
866         if (change_mode != REQUEST_NONE) {
867                 result = owner_set(cli, change_mode, filename, owner_username);
868         } else if (the_acl) {
869                 result = cacl_set(cli, filename, the_acl, mode);
870         } else {
871                 result = cacl_dump(cli, filename);
872         }
873
874         talloc_destroy(ctx);
875
876         return result;
877 }