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