Line data Source code
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 : Copyright (C) Noel Power <noel.power@suse.com> 2013
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include "includes.h"
26 : #include "lib/cmdline/cmdline.h"
27 : #include "rpc_client/cli_pipe.h"
28 : #include "../librpc/gen_ndr/ndr_lsa.h"
29 : #include "rpc_client/cli_lsarpc.h"
30 : #include "../libcli/security/security.h"
31 : #include "libsmb/libsmb.h"
32 : #include "libsmb/clirap.h"
33 : #include "passdb/machine_sid.h"
34 : #include "../librpc/gen_ndr/ndr_lsa_c.h"
35 : #include "util_sd.h"
36 : #include "lib/param/param.h"
37 :
38 : static char DIRSEP_CHAR = '\\';
39 :
40 : static int inheritance = 0;
41 : static int test_args;
42 : static int sddl;
43 : static int query_sec_info = -1;
44 : static int set_sec_info = -1;
45 : static bool want_mxac;
46 :
47 : static const char *domain_sid = NULL;
48 :
49 : enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
50 : enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP, REQUEST_INHERIT};
51 : enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
52 :
53 : struct cacl_callback_state {
54 : struct cli_credentials *creds;
55 : struct cli_state *cli;
56 : struct security_descriptor *aclsd;
57 : struct security_acl *acl_to_add;
58 : enum acl_mode mode;
59 : char *the_acl;
60 : bool acl_no_propagate;
61 : bool numeric;
62 : };
63 :
64 4 : static NTSTATUS cli_lsa_lookup_domain_sid(struct cli_state *cli,
65 : struct dom_sid *sid)
66 : {
67 4 : union lsa_PolicyInformation *info = NULL;
68 4 : struct smbXcli_tcon *orig_tcon = NULL;
69 4 : char *orig_share = NULL;
70 4 : struct rpc_pipe_client *rpc_pipe = NULL;
71 : struct policy_handle handle;
72 : NTSTATUS status, result;
73 4 : TALLOC_CTX *frame = talloc_stackframe();
74 :
75 4 : if (cli_state_has_tcon(cli)) {
76 4 : cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
77 : }
78 :
79 4 : status = cli_tree_connect(cli, "IPC$", "?????", NULL);
80 4 : if (!NT_STATUS_IS_OK(status)) {
81 0 : goto done;
82 : }
83 :
84 4 : status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &rpc_pipe);
85 4 : if (!NT_STATUS_IS_OK(status)) {
86 0 : goto tdis;
87 : }
88 :
89 4 : status = rpccli_lsa_open_policy(rpc_pipe, frame, True,
90 : GENERIC_EXECUTE_ACCESS, &handle);
91 4 : if (!NT_STATUS_IS_OK(status)) {
92 0 : goto tdis;
93 : }
94 :
95 4 : status = dcerpc_lsa_QueryInfoPolicy2(rpc_pipe->binding_handle,
96 : frame, &handle,
97 : LSA_POLICY_INFO_DOMAIN,
98 : &info, &result);
99 :
100 4 : if (any_nt_status_not_ok(status, result, &status)) {
101 4 : goto tdis;
102 : }
103 :
104 0 : *sid = *info->domain.sid;
105 :
106 4 : tdis:
107 4 : TALLOC_FREE(rpc_pipe);
108 4 : cli_tdis(cli);
109 4 : done:
110 4 : cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
111 4 : TALLOC_FREE(frame);
112 4 : return status;
113 : }
114 :
115 4 : static struct dom_sid *get_domain_sid(struct cli_state *cli)
116 : {
117 : NTSTATUS status;
118 : struct dom_sid_buf buf;
119 :
120 4 : struct dom_sid *sid = talloc(talloc_tos(), struct dom_sid);
121 4 : if (sid == NULL) {
122 0 : DEBUG(0, ("Out of memory\n"));
123 0 : return NULL;
124 : }
125 :
126 4 : if (domain_sid) {
127 0 : if (!dom_sid_parse(domain_sid, sid)) {
128 0 : DEBUG(0,("failed to parse domain sid\n"));
129 0 : TALLOC_FREE(sid);
130 : }
131 : } else {
132 4 : status = cli_lsa_lookup_domain_sid(cli, sid);
133 :
134 4 : if (!NT_STATUS_IS_OK(status)) {
135 4 : DEBUG(0,("failed to lookup domain sid: %s\n", nt_errstr(status)));
136 4 : TALLOC_FREE(sid);
137 : }
138 :
139 : }
140 :
141 4 : DEBUG(2,("Domain SID: %s\n", dom_sid_str_buf(sid, &buf)));
142 4 : return sid;
143 : }
144 :
145 : /* add an ACE to a list of ACEs in a struct security_acl */
146 7834 : static bool add_ace_with_ctx(TALLOC_CTX *ctx, struct security_acl **the_acl,
147 : const struct security_ace *ace)
148 :
149 : {
150 7834 : struct security_acl *acl = *the_acl;
151 :
152 7834 : if (acl == NULL) {
153 2108 : acl = make_sec_acl(ctx, 3, 0, NULL);
154 2108 : if (acl == NULL) {
155 0 : return false;
156 : }
157 : }
158 :
159 7834 : if (acl->num_aces == UINT32_MAX) {
160 0 : return false;
161 : }
162 7834 : ADD_TO_ARRAY(
163 : acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
164 7834 : *the_acl = acl;
165 7834 : return True;
166 : }
167 :
168 1526 : static bool add_ace(struct security_acl **the_acl, struct security_ace *ace)
169 : {
170 1526 : return add_ace_with_ctx(talloc_tos(), the_acl, ace);
171 : }
172 :
173 : /* parse a ascii version of a security descriptor */
174 1250 : static struct security_descriptor *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str)
175 : {
176 1250 : const char *p = str;
177 : char *tok;
178 1250 : struct security_descriptor *ret = NULL;
179 : size_t sd_size;
180 1250 : struct dom_sid owner_sid = { .num_auths = 0 };
181 1250 : bool have_owner = false;
182 1250 : struct dom_sid group_sid = { .num_auths = 0 };
183 1250 : bool have_group = false;
184 1250 : struct security_acl *dacl=NULL;
185 1250 : int revision=1;
186 :
187 2536 : while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
188 1286 : if (strncmp(tok,"REVISION:", 9) == 0) {
189 12 : revision = strtol(tok+9, NULL, 16);
190 12 : continue;
191 : }
192 :
193 1274 : if (strncmp(tok,"OWNER:", 6) == 0) {
194 12 : if (have_owner) {
195 0 : printf("Only specify owner once\n");
196 0 : goto done;
197 : }
198 12 : if (!StringToSid(cli, &owner_sid, tok+6)) {
199 0 : printf("Failed to parse owner sid\n");
200 0 : goto done;
201 : }
202 12 : have_owner = true;
203 12 : continue;
204 : }
205 :
206 1262 : if (strncmp(tok,"GROUP:", 6) == 0) {
207 12 : if (have_group) {
208 0 : printf("Only specify group once\n");
209 0 : goto done;
210 : }
211 12 : if (!StringToSid(cli, &group_sid, tok+6)) {
212 0 : printf("Failed to parse group sid\n");
213 0 : goto done;
214 : }
215 12 : have_group = true;
216 12 : continue;
217 : }
218 :
219 1250 : if (strncmp(tok,"ACL:", 4) == 0) {
220 : struct security_ace ace;
221 1250 : if (!parse_ace(cli, &ace, tok+4)) {
222 0 : goto done;
223 : }
224 1250 : if(!add_ace(&dacl, &ace)) {
225 0 : printf("Failed to add ACL %s\n", tok);
226 0 : goto done;
227 : }
228 1250 : continue;
229 : }
230 :
231 0 : printf("Failed to parse token '%s' in security descriptor,\n", tok);
232 0 : goto done;
233 : }
234 :
235 1250 : ret = make_sec_desc(
236 : ctx,
237 : revision,
238 : SEC_DESC_SELF_RELATIVE,
239 : have_owner ? &owner_sid : NULL,
240 : have_group ? &group_sid : NULL,
241 : NULL,
242 : dacl,
243 : &sd_size);
244 :
245 1250 : done:
246 1250 : return ret;
247 : }
248 :
249 : /*****************************************************
250 : get fileinfo for filename
251 : *******************************************************/
252 666 : static uint16_t get_fileinfo(struct cli_state *cli, const char *filename)
253 : {
254 666 : uint16_t fnum = (uint16_t)-1;
255 : NTSTATUS status;
256 666 : struct smb_create_returns cr = {0};
257 :
258 : /* The desired access below is the only one I could find that works
259 : with NT4, W2KP and Samba */
260 :
261 666 : status = cli_ntcreate(
262 : cli, /* cli */
263 : filename, /* fname */
264 : 0, /* CreatFlags */
265 : READ_CONTROL_ACCESS, /* CreatFlags */
266 : 0, /* FileAttributes */
267 : FILE_SHARE_READ|
268 : FILE_SHARE_WRITE, /* ShareAccess */
269 : FILE_OPEN, /* CreateDisposition */
270 : 0x0, /* CreateOptions */
271 : 0x0, /* SecurityFlags */
272 : &fnum, /* pfid */
273 : &cr); /* cr */
274 666 : if (!NT_STATUS_IS_OK(status)) {
275 0 : printf("Failed to open %s: %s\n", filename, nt_errstr(status));
276 0 : return 0;
277 : }
278 :
279 666 : cli_close(cli, fnum);
280 666 : return cr.file_attributes;
281 : }
282 :
283 : /*****************************************************
284 : get sec desc for filename
285 : *******************************************************/
286 2800 : static struct security_descriptor *get_secdesc_with_ctx(TALLOC_CTX *ctx,
287 : struct cli_state *cli,
288 : const char *filename)
289 : {
290 2800 : uint16_t fnum = (uint16_t)-1;
291 : struct security_descriptor *sd;
292 : NTSTATUS status;
293 : uint32_t sec_info;
294 2800 : uint32_t desired_access = 0;
295 :
296 2800 : if (query_sec_info == -1) {
297 2800 : sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL;
298 : } else {
299 0 : sec_info = query_sec_info;
300 : }
301 :
302 2800 : if (sec_info & (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL)) {
303 2800 : desired_access |= SEC_STD_READ_CONTROL;
304 : }
305 2800 : if (sec_info & SECINFO_SACL) {
306 0 : desired_access |= SEC_FLAG_SYSTEM_SECURITY;
307 : }
308 :
309 2800 : if (desired_access == 0) {
310 0 : desired_access |= SEC_STD_READ_CONTROL;
311 : }
312 :
313 2800 : status = cli_ntcreate(cli, filename, 0, desired_access,
314 : 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
315 : FILE_OPEN, 0x0, 0x0, &fnum, NULL);
316 2800 : if (!NT_STATUS_IS_OK(status)) {
317 0 : printf("Failed to open %s: %s\n", filename, nt_errstr(status));
318 0 : return NULL;
319 : }
320 :
321 2800 : status = cli_query_security_descriptor(cli, fnum, sec_info,
322 : ctx, &sd);
323 :
324 2800 : cli_close(cli, fnum);
325 :
326 2800 : if (!NT_STATUS_IS_OK(status)) {
327 0 : printf("Failed to get security descriptor: %s\n",
328 : nt_errstr(status));
329 0 : return NULL;
330 : }
331 2800 : return sd;
332 : }
333 :
334 1822 : static struct security_descriptor *get_secdesc(struct cli_state *cli,
335 : const char *filename)
336 : {
337 1822 : return get_secdesc_with_ctx(talloc_tos(), cli, filename);
338 : }
339 : /*****************************************************
340 : set sec desc for filename
341 : *******************************************************/
342 1840 : static bool set_secdesc(struct cli_state *cli, const char *filename,
343 : struct security_descriptor *sd)
344 : {
345 1840 : uint16_t fnum = (uint16_t)-1;
346 1840 : bool result=true;
347 : NTSTATUS status;
348 1840 : uint32_t desired_access = 0;
349 : uint32_t sec_info;
350 :
351 1840 : if (set_sec_info == -1) {
352 1840 : sec_info = 0;
353 :
354 1840 : if (sd->dacl || (sd->type & SEC_DESC_DACL_PRESENT)) {
355 1800 : sec_info |= SECINFO_DACL;
356 : }
357 1840 : if (sd->sacl || (sd->type & SEC_DESC_SACL_PRESENT)) {
358 0 : sec_info |= SECINFO_SACL;
359 : }
360 1840 : if (sd->owner_sid) {
361 1762 : sec_info |= SECINFO_OWNER;
362 : }
363 1840 : if (sd->group_sid) {
364 1730 : sec_info |= SECINFO_GROUP;
365 : }
366 : } else {
367 0 : sec_info = set_sec_info;
368 : }
369 :
370 : /* Make the desired_access more specific. */
371 1840 : if (sec_info & SECINFO_DACL) {
372 1800 : desired_access |= SEC_STD_WRITE_DAC;
373 : }
374 1840 : if (sec_info & SECINFO_SACL) {
375 0 : desired_access |= SEC_FLAG_SYSTEM_SECURITY;
376 : }
377 1840 : if (sec_info & (SECINFO_OWNER | SECINFO_GROUP)) {
378 1766 : desired_access |= SEC_STD_WRITE_OWNER;
379 : }
380 :
381 1840 : status = cli_ntcreate(cli, filename, 0,
382 : desired_access,
383 : 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
384 : FILE_OPEN, 0x0, 0x0, &fnum, NULL);
385 1840 : if (!NT_STATUS_IS_OK(status)) {
386 0 : printf("Failed to open %s: %s\n", filename, nt_errstr(status));
387 0 : return false;
388 : }
389 :
390 1840 : status = cli_set_security_descriptor(cli, fnum, sec_info, sd);
391 1840 : if (!NT_STATUS_IS_OK(status)) {
392 2 : printf("ERROR: security descriptor set failed: %s\n",
393 : nt_errstr(status));
394 2 : result=false;
395 : }
396 :
397 1840 : cli_close(cli, fnum);
398 1840 : return result;
399 : }
400 :
401 : /*****************************************************
402 : get maximum access for a file
403 : *******************************************************/
404 4 : static int cacl_mxac(struct cli_state *cli, const char *filename)
405 : {
406 : NTSTATUS status;
407 : uint32_t mxac;
408 :
409 4 : status = cli_query_mxac(cli, filename, &mxac);
410 4 : if (!NT_STATUS_IS_OK(status)) {
411 0 : printf("Failed to get mxac: %s\n", nt_errstr(status));
412 0 : return EXIT_FAILED;
413 : }
414 :
415 4 : printf("Maximum access: 0x%x\n", mxac);
416 :
417 4 : return EXIT_OK;
418 : }
419 :
420 :
421 : /*****************************************************
422 : dump the acls for a file
423 : *******************************************************/
424 642 : static int cacl_dump(struct cli_state *cli, const char *filename, bool numeric)
425 : {
426 : struct security_descriptor *sd;
427 : int ret;
428 :
429 642 : if (test_args) {
430 0 : return EXIT_OK;
431 : }
432 :
433 642 : sd = get_secdesc(cli, filename);
434 642 : if (sd == NULL) {
435 0 : return EXIT_FAILED;
436 : }
437 :
438 642 : if (sddl) {
439 4 : char *str = sddl_encode(talloc_tos(), sd, get_domain_sid(cli));
440 4 : if (str == NULL) {
441 0 : return EXIT_FAILED;
442 : }
443 4 : printf("%s\n", str);
444 4 : TALLOC_FREE(str);
445 : } else {
446 638 : sec_desc_print(cli, stdout, sd, numeric);
447 : }
448 :
449 642 : if (want_mxac) {
450 4 : ret = cacl_mxac(cli, filename);
451 4 : if (ret != EXIT_OK) {
452 0 : return ret;
453 : }
454 : }
455 :
456 642 : return EXIT_OK;
457 : }
458 :
459 : /*****************************************************
460 : Change the ownership or group ownership of a file. Just
461 : because the NT docs say this can't be done :-). JRA.
462 : *******************************************************/
463 :
464 40 : static int owner_set(struct cli_state *cli, enum chown_mode change_mode,
465 : const char *filename, const char *new_username)
466 : {
467 : struct dom_sid sid;
468 : struct security_descriptor *sd;
469 : size_t sd_size;
470 :
471 40 : if (!StringToSid(cli, &sid, new_username))
472 0 : return EXIT_PARSE_ERROR;
473 :
474 40 : sd = make_sec_desc(talloc_tos(),
475 : SECURITY_DESCRIPTOR_REVISION_1,
476 : SEC_DESC_SELF_RELATIVE,
477 : (change_mode == REQUEST_CHOWN) ? &sid : NULL,
478 : (change_mode == REQUEST_CHGRP) ? &sid : NULL,
479 : NULL, NULL, &sd_size);
480 :
481 40 : if (!set_secdesc(cli, filename, sd)) {
482 2 : return EXIT_FAILED;
483 : }
484 :
485 38 : return EXIT_OK;
486 : }
487 :
488 :
489 : /* The MSDN is contradictory over the ordering of ACE entries in an
490 : ACL. However NT4 gives a "The information may have been modified
491 : by a computer running Windows NT 5.0" if denied ACEs do not appear
492 : before allowed ACEs. At
493 : http://technet.microsoft.com/en-us/library/cc781716.aspx the
494 : canonical order is specified as "Explicit Deny, Explicit Allow,
495 : Inherited ACEs unchanged" */
496 :
497 19266 : static int ace_compare(struct security_ace *ace1, struct security_ace *ace2)
498 : {
499 19266 : if (security_ace_equal(ace1, ace2))
500 364 : return 0;
501 :
502 18902 : if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
503 5592 : !(ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
504 1696 : return 1;
505 17206 : if (!(ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
506 13310 : (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
507 2622 : return -1;
508 14584 : if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
509 3896 : (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
510 3896 : return ace1 - ace2;
511 :
512 10688 : if (ace1->type != ace2->type)
513 0 : return ace2->type - ace1->type;
514 :
515 10688 : if (dom_sid_compare(&ace1->trustee, &ace2->trustee))
516 10488 : return dom_sid_compare(&ace1->trustee, &ace2->trustee);
517 :
518 200 : if (ace1->flags != ace2->flags)
519 196 : return ace1->flags - ace2->flags;
520 :
521 4 : if (ace1->access_mask != ace2->access_mask)
522 4 : return ace1->access_mask - ace2->access_mask;
523 :
524 0 : if (ace1->size != ace2->size)
525 0 : return ace1->size - ace2->size;
526 :
527 0 : return memcmp(ace1, ace2, sizeof(struct security_ace));
528 : }
529 :
530 1800 : static void sort_acl(struct security_acl *the_acl)
531 : {
532 : uint32_t i;
533 1800 : if (!the_acl) return;
534 :
535 1800 : TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
536 :
537 11308 : for (i=1;i<the_acl->num_aces;) {
538 9508 : if (security_ace_equal(&the_acl->aces[i-1],
539 9508 : &the_acl->aces[i])) {
540 46 : ARRAY_DEL_ELEMENT(
541 : the_acl->aces, i, the_acl->num_aces);
542 46 : the_acl->num_aces--;
543 : } else {
544 9462 : i++;
545 : }
546 : }
547 : }
548 :
549 : /*****************************************************
550 : set the ACLs on a file given a security descriptor
551 : *******************************************************/
552 :
553 1782 : static int cacl_set_from_sd(struct cli_state *cli, const char *filename,
554 : struct security_descriptor *sd, enum acl_mode mode,
555 : bool numeric)
556 : {
557 1782 : struct security_descriptor *old = NULL;
558 : uint32_t i, j;
559 : size_t sd_size;
560 1782 : int result = EXIT_OK;
561 :
562 1782 : if (!sd) return EXIT_PARSE_ERROR;
563 1782 : if (test_args) return EXIT_OK;
564 :
565 1782 : if (mode != SMB_ACL_SET) {
566 : /*
567 : * Do not fetch old ACL when it will be overwritten
568 : * completely with a new one.
569 : */
570 1162 : old = get_secdesc(cli, filename);
571 :
572 1162 : if (!old) {
573 0 : return EXIT_FAILED;
574 : }
575 : }
576 :
577 : /* the logic here is rather more complex than I would like */
578 1782 : switch (mode) {
579 66 : case SMB_ACL_DELETE:
580 132 : for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
581 66 : bool found = False;
582 :
583 316 : for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
584 290 : if (security_ace_equal(&sd->dacl->aces[i],
585 290 : &old->dacl->aces[j])) {
586 : uint32_t k;
587 84 : for (k=j; k<old->dacl->num_aces-1;k++) {
588 44 : old->dacl->aces[k] = old->dacl->aces[k+1];
589 : }
590 40 : old->dacl->num_aces--;
591 40 : found = True;
592 40 : break;
593 : }
594 : }
595 :
596 66 : if (!found) {
597 26 : printf("ACL for ACE:");
598 26 : print_ace(cli, stdout, &sd->dacl->aces[i],
599 : numeric);
600 26 : printf(" not found\n");
601 : }
602 : }
603 66 : break;
604 :
605 820 : case SMB_ACL_MODIFY:
606 1640 : for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
607 820 : bool found = False;
608 :
609 4492 : for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
610 3672 : if (dom_sid_equal(&sd->dacl->aces[i].trustee,
611 3672 : &old->dacl->aces[j].trustee)) {
612 856 : old->dacl->aces[j] = sd->dacl->aces[i];
613 856 : found = True;
614 : }
615 : }
616 :
617 820 : if (!found) {
618 : fstring str;
619 :
620 0 : SidToString(cli, str,
621 0 : &sd->dacl->aces[i].trustee,
622 : numeric);
623 0 : printf("ACL for SID %s not found\n", str);
624 : }
625 : }
626 :
627 820 : if (sd->owner_sid) {
628 0 : old->owner_sid = sd->owner_sid;
629 : }
630 :
631 820 : if (sd->group_sid) {
632 0 : old->group_sid = sd->group_sid;
633 : }
634 :
635 820 : break;
636 :
637 276 : case SMB_ACL_ADD:
638 552 : for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
639 276 : add_ace(&old->dacl, &sd->dacl->aces[i]);
640 : }
641 276 : break;
642 :
643 620 : case SMB_ACL_SET:
644 620 : old = sd;
645 620 : break;
646 : }
647 :
648 : /* Denied ACE entries must come before allowed ones */
649 1782 : sort_acl(old->dacl);
650 :
651 : /* Create new security descriptor and set it */
652 :
653 : /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER.
654 : But if we're sending an owner, even if it's the same as the one
655 : that already exists then W2K3 insists we open with WRITE_OWNER access.
656 : I need to check that setting a SD with no owner set works against WNT
657 : and W2K. JRA.
658 : */
659 :
660 1782 : sd = make_sec_desc(talloc_tos(),old->revision, old->type,
661 1782 : old->owner_sid, old->group_sid,
662 : NULL, old->dacl, &sd_size);
663 :
664 1782 : if (!set_secdesc(cli, filename, sd)) {
665 0 : result = EXIT_FAILED;
666 : }
667 :
668 1782 : return result;
669 : }
670 :
671 : /*****************************************************
672 : set the ACLs on a file given an ascii description
673 : *******************************************************/
674 :
675 1140 : static int cacl_set(struct cli_state *cli, const char *filename,
676 : char *the_acl, enum acl_mode mode, bool numeric)
677 : {
678 1140 : struct security_descriptor *sd = NULL;
679 :
680 1140 : if (sddl) {
681 4 : sd = sddl_decode(talloc_tos(), the_acl, get_global_sam_sid());
682 : } else {
683 1136 : sd = sec_desc_parse(talloc_tos(), cli, the_acl);
684 : }
685 :
686 1140 : if (sd == NULL) {
687 0 : return EXIT_PARSE_ERROR;
688 : }
689 1140 : if (test_args) {
690 0 : return EXIT_OK;
691 : }
692 1140 : return cacl_set_from_sd(cli, filename, sd, mode, numeric);
693 : }
694 :
695 : /*****************************************************
696 : set the inherit on a file
697 : *******************************************************/
698 18 : static int inherit(struct cli_state *cli, const char *filename,
699 : const char *type)
700 : {
701 : struct security_descriptor *old,*sd;
702 : uint32_t oldattr;
703 : size_t sd_size;
704 18 : int result = EXIT_OK;
705 :
706 18 : old = get_secdesc(cli, filename);
707 :
708 18 : if (!old) {
709 0 : return EXIT_FAILED;
710 : }
711 :
712 18 : oldattr = get_fileinfo(cli,filename);
713 :
714 18 : if (strcmp(type,"allow")==0) {
715 0 : if ((old->type & SEC_DESC_DACL_PROTECTED) ==
716 : SEC_DESC_DACL_PROTECTED) {
717 : uint32_t i;
718 : char *parentname,*temp;
719 : struct security_descriptor *parent;
720 0 : temp = talloc_strdup(talloc_tos(), filename);
721 :
722 0 : old->type=old->type & (~SEC_DESC_DACL_PROTECTED);
723 :
724 : /* look at parent and copy in all its inheritable ACL's. */
725 0 : string_replace(temp, '\\', '/');
726 0 : if (!parent_dirname(talloc_tos(),temp,&parentname,NULL)) {
727 0 : return EXIT_FAILED;
728 : }
729 0 : string_replace(parentname, '/', '\\');
730 0 : parent = get_secdesc(cli,parentname);
731 0 : if (parent == NULL) {
732 0 : return EXIT_FAILED;
733 : }
734 0 : for (i=0;i<parent->dacl->num_aces;i++) {
735 0 : struct security_ace *ace=&parent->dacl->aces[i];
736 : /* Add inherited flag to all aces */
737 0 : ace->flags=ace->flags|
738 : SEC_ACE_FLAG_INHERITED_ACE;
739 0 : if ((oldattr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
740 0 : if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) ==
741 : SEC_ACE_FLAG_CONTAINER_INHERIT) {
742 0 : add_ace(&old->dacl, ace);
743 : }
744 : } else {
745 0 : if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) ==
746 : SEC_ACE_FLAG_OBJECT_INHERIT) {
747 : /* clear flags for files */
748 0 : ace->flags=0;
749 0 : add_ace(&old->dacl, ace);
750 : }
751 : }
752 : }
753 : } else {
754 0 : printf("Already set to inheritable permissions.\n");
755 0 : return EXIT_FAILED;
756 : }
757 18 : } else if (strcmp(type,"remove")==0) {
758 0 : if ((old->type & SEC_DESC_DACL_PROTECTED) !=
759 : SEC_DESC_DACL_PROTECTED) {
760 0 : old->type=old->type | SEC_DESC_DACL_PROTECTED;
761 :
762 : /* remove all inherited ACL's. */
763 0 : if (old->dacl) {
764 : int i;
765 0 : struct security_acl *temp=old->dacl;
766 0 : old->dacl=make_sec_acl(talloc_tos(), 3, 0, NULL);
767 0 : for (i=temp->num_aces-1;i>=0;i--) {
768 0 : struct security_ace *ace=&temp->aces[i];
769 : /* Remove all ace with INHERITED flag set */
770 0 : if ((ace->flags & SEC_ACE_FLAG_INHERITED_ACE) !=
771 : SEC_ACE_FLAG_INHERITED_ACE) {
772 0 : add_ace(&old->dacl,ace);
773 : }
774 : }
775 : }
776 : } else {
777 0 : printf("Already set to no inheritable permissions.\n");
778 0 : return EXIT_FAILED;
779 : }
780 18 : } else if (strcmp(type,"copy")==0) {
781 18 : if ((old->type & SEC_DESC_DACL_PROTECTED) !=
782 : SEC_DESC_DACL_PROTECTED) {
783 18 : old->type=old->type | SEC_DESC_DACL_PROTECTED;
784 :
785 : /*
786 : * convert all inherited ACL's to non
787 : * inherited ACL's.
788 : */
789 18 : if (old->dacl) {
790 : uint32_t i;
791 108 : for (i=0;i<old->dacl->num_aces;i++) {
792 90 : struct security_ace *ace=&old->dacl->aces[i];
793 : /* Remove INHERITED FLAG from all aces */
794 90 : ace->flags=ace->flags&(~SEC_ACE_FLAG_INHERITED_ACE);
795 : }
796 : }
797 : } else {
798 0 : printf("Already set to no inheritable permissions.\n");
799 0 : return EXIT_FAILED;
800 : }
801 : }
802 :
803 : /* Denied ACE entries must come before allowed ones */
804 18 : sort_acl(old->dacl);
805 :
806 18 : sd = make_sec_desc(talloc_tos(),old->revision, old->type,
807 18 : old->owner_sid, old->group_sid,
808 : NULL, old->dacl, &sd_size);
809 :
810 18 : if (!set_secdesc(cli, filename, sd)) {
811 0 : result = EXIT_FAILED;
812 : }
813 :
814 18 : return result;
815 : }
816 :
817 : /*****************************************************
818 : Return a connection to a server.
819 : *******************************************************/
820 1954 : static struct cli_state *connect_one(struct cli_credentials *creds,
821 : const char *server, const char *share)
822 : {
823 1954 : struct cli_state *c = NULL;
824 : NTSTATUS nt_status;
825 1954 : uint32_t flags = 0;
826 :
827 1954 : nt_status = cli_full_connection_creds(&c, lp_netbios_name(), server,
828 : NULL, 0,
829 : share, "?????",
830 : creds,
831 : flags);
832 1954 : if (!NT_STATUS_IS_OK(nt_status)) {
833 0 : DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
834 0 : return NULL;
835 : }
836 :
837 1954 : return c;
838 : }
839 :
840 : /*
841 : * Process resulting combination of mask & fname ensuring
842 : * terminated with wildcard
843 : */
844 216 : static char *build_dirname(TALLOC_CTX *ctx,
845 : const char *mask, char *dir, char *fname)
846 : {
847 216 : char *mask2 = NULL;
848 216 : char *p = NULL;
849 :
850 216 : mask2 = talloc_strdup(ctx, mask);
851 216 : if (!mask2) {
852 0 : return NULL;
853 : }
854 216 : p = strrchr_m(mask2, DIRSEP_CHAR);
855 216 : if (p) {
856 216 : p[1] = 0;
857 : } else {
858 0 : mask2[0] = '\0';
859 : }
860 216 : mask2 = talloc_asprintf_append(mask2,
861 : "%s\\*",
862 : fname);
863 216 : return mask2;
864 : }
865 :
866 : /*
867 : * Returns a copy of the ACL flags in ace modified according
868 : * to some inheritance rules.
869 : * a) SEC_ACE_FLAG_INHERITED_ACE is propagated to children
870 : * b) SEC_ACE_FLAG_INHERIT_ONLY is set on container children for OI (only)
871 : * c) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
872 : * stripped from flags to be propagated to non-container children
873 : * d) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
874 : * stripped from flags to be propagated if the NP flag
875 : * SEC_ACE_FLAG_NO_PROPAGATE_INHERIT is present
876 : */
877 :
878 3270 : static uint8_t get_flags_to_propagate(bool is_container,
879 : struct security_ace *ace)
880 : {
881 3270 : uint8_t newflags = ace->flags;
882 : /* OBJECT inheritance */
883 3270 : bool acl_objinherit = (ace->flags &
884 : SEC_ACE_FLAG_OBJECT_INHERIT) == SEC_ACE_FLAG_OBJECT_INHERIT;
885 : /* CONTAINER inheritance */
886 3270 : bool acl_cntrinherit = (ace->flags &
887 : SEC_ACE_FLAG_CONTAINER_INHERIT) ==
888 : SEC_ACE_FLAG_CONTAINER_INHERIT;
889 : /* PROHIBIT inheritance */
890 3270 : bool prohibit_inheritance = ((ace->flags &
891 : SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) ==
892 : SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
893 :
894 : /* Assume we are not propagating the ACE */
895 :
896 3270 : newflags &= ~SEC_ACE_FLAG_INHERITED_ACE;
897 : /* all children need to have the SEC_ACE_FLAG_INHERITED_ACE set */
898 3270 : if (acl_cntrinherit || acl_objinherit) {
899 : /*
900 : * object inherit ( alone ) on a container needs
901 : * SEC_ACE_FLAG_INHERIT_ONLY
902 : */
903 3270 : if (is_container) {
904 1128 : if (acl_objinherit && !acl_cntrinherit) {
905 30 : newflags |= SEC_ACE_FLAG_INHERIT_ONLY;
906 : }
907 : /*
908 : * this is tricky, the only time we would not
909 : * propagate the ace for a container is if
910 : * prohibit_inheritance is set and object inheritance
911 : * alone is set
912 : */
913 1128 : if ((prohibit_inheritance
914 18 : && acl_objinherit
915 1146 : && !acl_cntrinherit) == false) {
916 1122 : newflags |= SEC_ACE_FLAG_INHERITED_ACE;
917 : }
918 : } else {
919 : /*
920 : * don't apply object/container inheritance flags to
921 : * non dirs
922 : */
923 2142 : newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
924 : | SEC_ACE_FLAG_CONTAINER_INHERIT
925 : | SEC_ACE_FLAG_INHERIT_ONLY);
926 : /*
927 : * only apply ace to file if object inherit
928 : */
929 2142 : if (acl_objinherit) {
930 2142 : newflags |= SEC_ACE_FLAG_INHERITED_ACE;
931 : }
932 : }
933 :
934 : /* if NP is specified strip NP and all OI/CI INHERIT flags */
935 3270 : if (prohibit_inheritance) {
936 30 : newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
937 : | SEC_ACE_FLAG_CONTAINER_INHERIT
938 : | SEC_ACE_FLAG_INHERIT_ONLY
939 : | SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
940 : }
941 : }
942 3270 : return newflags;
943 : }
944 :
945 : /*
946 : * This function builds a new acl for 'caclfile', first it removes any
947 : * existing inheritable ace(s) from the current acl of caclfile, secondly it
948 : * applies any inheritable acls of the parent of caclfile ( inheritable acls of
949 : * caclfile's parent are passed via acl_to_add member of cbstate )
950 : *
951 : */
952 540 : static NTSTATUS propagate_inherited_aces(char *caclfile,
953 : struct cacl_callback_state *cbstate)
954 : {
955 540 : TALLOC_CTX *aclctx = NULL;
956 : NTSTATUS status;
957 : int result;
958 : int fileattr;
959 540 : struct security_descriptor *old = NULL;
960 540 : bool is_container = false;
961 540 : struct security_acl *acl_to_add = cbstate->acl_to_add;
962 540 : struct security_acl *acl_to_remove = NULL;
963 : uint32_t i, j;
964 :
965 540 : aclctx = talloc_new(NULL);
966 540 : if (aclctx == NULL) {
967 0 : return NT_STATUS_NO_MEMORY;
968 : }
969 540 : old = get_secdesc_with_ctx(aclctx, cbstate->cli, caclfile);
970 :
971 540 : if (!old) {
972 0 : status = NT_STATUS_UNSUCCESSFUL;
973 0 : goto out;
974 : }
975 :
976 : /* inhibit propagation? */
977 540 : if ((old->type & SEC_DESC_DACL_PROTECTED) ==
978 : SEC_DESC_DACL_PROTECTED){
979 6 : status = NT_STATUS_OK;
980 6 : goto out;
981 : }
982 :
983 534 : fileattr = get_fileinfo(cbstate->cli, caclfile);
984 534 : is_container = (fileattr & FILE_ATTRIBUTE_DIRECTORY);
985 :
986 : /* find acl(s) that are inherited */
987 2930 : for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
988 :
989 2396 : if (old->dacl->aces[j].flags & SEC_ACE_FLAG_INHERITED_ACE) {
990 842 : if (!add_ace_with_ctx(aclctx, &acl_to_remove,
991 842 : &old->dacl->aces[j])) {
992 0 : status = NT_STATUS_NO_MEMORY;
993 0 : goto out;
994 : }
995 : }
996 : }
997 :
998 : /* remove any acl(s) that are inherited */
999 534 : if (acl_to_remove) {
1000 1376 : for (i = 0; i < acl_to_remove->num_aces; i++) {
1001 842 : struct security_ace ace = acl_to_remove->aces[i];
1002 3714 : for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
1003 :
1004 3714 : if (security_ace_equal(&ace,
1005 3714 : &old->dacl->aces[j])) {
1006 : uint32_t k;
1007 1630 : for (k = j; k < old->dacl->num_aces-1;
1008 788 : k++) {
1009 788 : old->dacl->aces[k] =
1010 788 : old->dacl->aces[k+1];
1011 : }
1012 842 : old->dacl->num_aces--;
1013 842 : break;
1014 : }
1015 : }
1016 : }
1017 : }
1018 : /* propagate any inheritable ace to be added */
1019 534 : if (acl_to_add) {
1020 3864 : for (i = 0; i < acl_to_add->num_aces; i++) {
1021 3330 : struct security_ace ace = acl_to_add->aces[i];
1022 3330 : bool is_objectinherit = (ace.flags &
1023 : SEC_ACE_FLAG_OBJECT_INHERIT) ==
1024 : SEC_ACE_FLAG_OBJECT_INHERIT;
1025 : bool is_inherited;
1026 : /* don't propagate flags to a file unless OI */
1027 3330 : if (!is_objectinherit && !is_container) {
1028 66 : continue;
1029 : }
1030 : /*
1031 : * adjust flags according to inheritance
1032 : * rules
1033 : */
1034 3270 : ace.flags = get_flags_to_propagate(is_container, &ace);
1035 3270 : is_inherited = (ace.flags &
1036 : SEC_ACE_FLAG_INHERITED_ACE) ==
1037 : SEC_ACE_FLAG_INHERITED_ACE;
1038 : /* don't propagate non inherited flags */
1039 3270 : if (!is_inherited) {
1040 6 : continue;
1041 : }
1042 3264 : if (!add_ace_with_ctx(aclctx, &old->dacl, &ace)) {
1043 0 : status = NT_STATUS_NO_MEMORY;
1044 0 : goto out;
1045 : }
1046 : }
1047 : }
1048 :
1049 534 : result = cacl_set_from_sd(cbstate->cli, caclfile,
1050 : old,
1051 534 : SMB_ACL_SET, cbstate->numeric);
1052 534 : if (result != EXIT_OK) {
1053 0 : status = NT_STATUS_UNSUCCESSFUL;
1054 0 : goto out;
1055 : }
1056 :
1057 534 : status = NT_STATUS_OK;
1058 540 : out:
1059 540 : TALLOC_FREE(aclctx);
1060 540 : return status;
1061 : }
1062 :
1063 : /*
1064 : * Returns true if 'ace' contains SEC_ACE_FLAG_OBJECT_INHERIT or
1065 : * SEC_ACE_FLAG_CONTAINER_INHERIT
1066 : */
1067 2682 : static bool is_inheritable_ace(struct security_ace *ace)
1068 : {
1069 2682 : uint8_t flags = ace->flags;
1070 2682 : if (flags & (SEC_ACE_FLAG_OBJECT_INHERIT
1071 : | SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1072 2310 : return true;
1073 : }
1074 372 : return false;
1075 : }
1076 :
1077 : /* This method does some basic sanity checking with respect to automatic
1078 : * inheritance. e.g. it checks if it is possible to do a set, it detects illegal
1079 : * attempts to set inherited permissions directly. Additionally this method
1080 : * does some basic initialisation for instance it parses the ACL passed on the
1081 : * command line.
1082 : */
1083 114 : static NTSTATUS prepare_inheritance_propagation(TALLOC_CTX *ctx, char *filename,
1084 : struct cacl_callback_state *cbstate)
1085 : {
1086 : NTSTATUS result;
1087 114 : char *the_acl = cbstate->the_acl;
1088 114 : struct cli_state *cli = cbstate->cli;
1089 114 : enum acl_mode mode = cbstate->mode;
1090 114 : struct security_descriptor *sd = NULL;
1091 114 : struct security_descriptor *old = NULL;
1092 : uint32_t j;
1093 114 : bool propagate = false;
1094 :
1095 114 : old = get_secdesc_with_ctx(ctx, cli, filename);
1096 114 : if (old == NULL) {
1097 0 : return NT_STATUS_NO_MEMORY;
1098 : }
1099 :
1100 : /* parse acl passed on the command line */
1101 114 : if (sddl) {
1102 0 : cbstate->aclsd = sddl_decode(ctx, the_acl,
1103 0 : get_global_sam_sid());
1104 : } else {
1105 114 : cbstate->aclsd = sec_desc_parse(ctx, cli, the_acl);
1106 : }
1107 :
1108 114 : if (!cbstate->aclsd) {
1109 0 : result = NT_STATUS_UNSUCCESSFUL;
1110 0 : goto out;
1111 : }
1112 :
1113 114 : sd = cbstate->aclsd;
1114 :
1115 : /* set operation if inheritance is enabled doesn't make sense */
1116 114 : if (mode == SMB_ACL_SET && ((old->type & SEC_DESC_DACL_PROTECTED) !=
1117 : SEC_DESC_DACL_PROTECTED)){
1118 6 : d_printf("Inheritance enabled at %s, can't apply set operation\n",filename);
1119 6 : result = NT_STATUS_UNSUCCESSFUL;
1120 6 : goto out;
1121 :
1122 : }
1123 :
1124 : /*
1125 : * search command line acl for any illegal SEC_ACE_FLAG_INHERITED_ACE
1126 : * flags that are set
1127 : */
1128 216 : for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1129 108 : struct security_ace *ace = &sd->dacl->aces[j];
1130 108 : if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE) {
1131 0 : d_printf("Illegal parameter %s\n", the_acl);
1132 0 : result = NT_STATUS_UNSUCCESSFUL;
1133 0 : goto out;
1134 : }
1135 108 : if (!propagate) {
1136 108 : if (is_inheritable_ace(ace)) {
1137 108 : propagate = true;
1138 : }
1139 : }
1140 : }
1141 :
1142 108 : result = NT_STATUS_OK;
1143 114 : out:
1144 114 : cbstate->acl_no_propagate = !propagate;
1145 114 : return result;
1146 : }
1147 :
1148 : /*
1149 : * This method builds inheritable ace(s) from filename (which should be
1150 : * a container) that need propagating to children in order to provide
1151 : * automatic inheritance. Those inheritable ace(s) are stored in
1152 : * acl_to_add member of cbstate for later processing
1153 : * (see propagate_inherited_aces)
1154 : */
1155 324 : static NTSTATUS get_inheritable_aces(TALLOC_CTX *ctx, char *filename,
1156 : struct cacl_callback_state *cbstate)
1157 : {
1158 : NTSTATUS result;
1159 324 : struct cli_state *cli = NULL;
1160 324 : struct security_descriptor *sd = NULL;
1161 324 : struct security_acl *acl_to_add = NULL;
1162 : uint32_t j;
1163 :
1164 324 : cli = cbstate->cli;
1165 324 : sd = get_secdesc_with_ctx(ctx, cli, filename);
1166 :
1167 324 : if (sd == NULL) {
1168 0 : return NT_STATUS_NO_MEMORY;
1169 : }
1170 :
1171 : /*
1172 : * Check if any inheritance related flags are used, if not then
1173 : * nothing to do. At the same time populate acls for inheritance
1174 : * related ace(s) that need to be added to or deleted from children as
1175 : * a result of inheritance propagation.
1176 : */
1177 :
1178 2898 : for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1179 2574 : struct security_ace *ace = &sd->dacl->aces[j];
1180 2574 : if (is_inheritable_ace(ace)) {
1181 2202 : bool added = add_ace_with_ctx(ctx, &acl_to_add, ace);
1182 2202 : if (!added) {
1183 0 : result = NT_STATUS_NO_MEMORY;
1184 0 : goto out;
1185 : }
1186 : }
1187 : }
1188 324 : cbstate->acl_to_add = acl_to_add;
1189 324 : result = NT_STATUS_OK;
1190 324 : out:
1191 324 : return result;
1192 : }
1193 :
1194 : /*
1195 : * Callback handler to handle child elements processed by cli_list, we attempt
1196 : * to propagate inheritable ace(s) to each child via the function
1197 : * propagate_inherited_aces. Children that are themselves directories are passed
1198 : * to cli_list again ( to descend the directory structure )
1199 : */
1200 1188 : static NTSTATUS cacl_set_cb(struct file_info *f,
1201 : const char *mask, void *state)
1202 : {
1203 1188 : struct cacl_callback_state *cbstate =
1204 : (struct cacl_callback_state *)state;
1205 1188 : struct cli_state *cli = NULL;
1206 1188 : struct cli_credentials *creds = NULL;
1207 :
1208 1188 : TALLOC_CTX *dirctx = NULL;
1209 : NTSTATUS status;
1210 1188 : struct cli_state *targetcli = NULL;
1211 :
1212 1188 : char *dir = NULL;
1213 1188 : char *dir_end = NULL;
1214 1188 : char *mask2 = NULL;
1215 1188 : char *targetpath = NULL;
1216 1188 : char *caclfile = NULL;
1217 :
1218 1188 : dirctx = talloc_new(NULL);
1219 1188 : if (!dirctx) {
1220 0 : status = NT_STATUS_NO_MEMORY;
1221 0 : goto out;
1222 : }
1223 :
1224 1188 : cli = cbstate->cli;
1225 1188 : creds = cbstate->creds;
1226 :
1227 : /* Work out the directory. */
1228 1188 : dir = talloc_strdup(dirctx, mask);
1229 1188 : if (!dir) {
1230 0 : status = NT_STATUS_NO_MEMORY;
1231 0 : goto out;
1232 : }
1233 :
1234 1188 : dir_end = strrchr(dir, DIRSEP_CHAR);
1235 1188 : if (dir_end != NULL) {
1236 1188 : *dir_end = '\0';
1237 : }
1238 :
1239 1188 : if (!f->name || !f->name[0]) {
1240 0 : d_printf("Empty dir name returned. Possible server misconfiguration.\n");
1241 0 : status = NT_STATUS_UNSUCCESSFUL;
1242 0 : goto out;
1243 : }
1244 :
1245 1188 : if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
1246 : struct cacl_callback_state dir_cbstate;
1247 864 : uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
1248 : | FILE_ATTRIBUTE_SYSTEM
1249 : | FILE_ATTRIBUTE_HIDDEN;
1250 864 : dir_end = NULL;
1251 :
1252 : /* ignore special '.' & '..' */
1253 864 : if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
1254 648 : status = NT_STATUS_OK;
1255 648 : goto out;
1256 : }
1257 :
1258 216 : mask2 = build_dirname(dirctx, mask, dir, f->name);
1259 216 : if (mask2 == NULL) {
1260 0 : status = NT_STATUS_NO_MEMORY;
1261 0 : goto out;
1262 : }
1263 :
1264 : /* check for dfs */
1265 216 : status = cli_resolve_path(dirctx, "", creds, cli,
1266 : mask2, &targetcli, &targetpath);
1267 216 : if (!NT_STATUS_IS_OK(status)) {
1268 0 : goto out;
1269 : }
1270 :
1271 : /*
1272 : * prepare path to caclfile, remove any existing wildcard
1273 : * chars and convert path separators.
1274 : */
1275 :
1276 216 : caclfile = talloc_strdup(dirctx, targetpath);
1277 216 : if (!caclfile) {
1278 0 : status = NT_STATUS_NO_MEMORY;
1279 0 : goto out;
1280 : }
1281 216 : dir_end = strrchr(caclfile, '*');
1282 216 : if (dir_end != NULL) {
1283 216 : *dir_end = '\0';
1284 : }
1285 :
1286 216 : string_replace(caclfile, '/', '\\');
1287 : /*
1288 : * make directory specific copy of cbstate here
1289 : * (for this directory level) to be available as
1290 : * the parent cbstate for the children of this directory.
1291 : * Note: cbstate is overwritten for the current file being
1292 : * processed.
1293 : */
1294 216 : dir_cbstate = *cbstate;
1295 216 : dir_cbstate.cli = targetcli;
1296 :
1297 : /*
1298 : * propagate any inherited ace from our parent
1299 : */
1300 216 : status = propagate_inherited_aces(caclfile, &dir_cbstate);
1301 216 : if (!NT_STATUS_IS_OK(status)) {
1302 0 : goto out;
1303 : }
1304 :
1305 : /*
1306 : * get inheritable ace(s) for this dir/container
1307 : * that will be propagated to its children
1308 : */
1309 216 : status = get_inheritable_aces(dirctx, caclfile,
1310 : &dir_cbstate);
1311 216 : if (!NT_STATUS_IS_OK(status)) {
1312 0 : goto out;
1313 : }
1314 :
1315 : /*
1316 : * ensure cacl_set_cb gets called for children
1317 : * of this directory (targetpath)
1318 : */
1319 216 : status = cli_list(targetcli, targetpath,
1320 : attribute, cacl_set_cb,
1321 : (void *)&dir_cbstate);
1322 :
1323 216 : if (!NT_STATUS_IS_OK(status)) {
1324 0 : goto out;
1325 : }
1326 :
1327 : } else {
1328 : /*
1329 : * build full path to caclfile and replace '/' with '\' so
1330 : * other utility functions can deal with it
1331 : */
1332 :
1333 324 : targetpath = talloc_asprintf(dirctx, "%s/%s", dir, f->name);
1334 324 : if (!targetpath) {
1335 0 : status = NT_STATUS_NO_MEMORY;
1336 0 : goto out;
1337 : }
1338 324 : string_replace(targetpath, '/', '\\');
1339 :
1340 : /* attempt to propagate any inherited ace to file caclfile */
1341 324 : status = propagate_inherited_aces(targetpath, cbstate);
1342 :
1343 324 : if (!NT_STATUS_IS_OK(status)) {
1344 0 : goto out;
1345 : }
1346 : }
1347 540 : status = NT_STATUS_OK;
1348 1188 : out:
1349 1188 : if (!NT_STATUS_IS_OK(status)) {
1350 0 : d_printf("error %s: processing %s\n",
1351 : nt_errstr(status),
1352 : targetpath);
1353 : }
1354 1188 : TALLOC_FREE(dirctx);
1355 1188 : return status;
1356 : }
1357 :
1358 :
1359 : /*
1360 : * Wrapper around cl_list to descend the directory tree pointed to by 'filename',
1361 : * helper callback function 'cacl_set_cb' handles the child elements processed
1362 : * by cli_list.
1363 : */
1364 114 : static int inheritance_cacl_set(char *filename,
1365 : struct cacl_callback_state *cbstate)
1366 : {
1367 : int result;
1368 : NTSTATUS ntstatus;
1369 : int fileattr;
1370 114 : char *mask = NULL;
1371 114 : struct cli_state *cli = cbstate->cli;
1372 114 : TALLOC_CTX *ctx = NULL;
1373 114 : bool isdirectory = false;
1374 114 : uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM
1375 : | FILE_ATTRIBUTE_HIDDEN;
1376 114 : ctx = talloc_init("inherit_set");
1377 114 : if (ctx == NULL) {
1378 0 : d_printf("out of memory\n");
1379 0 : result = EXIT_FAILED;
1380 0 : goto out;
1381 : }
1382 :
1383 : /* ensure we have a filename that starts with '\' */
1384 114 : if (!filename || *filename != DIRSEP_CHAR) {
1385 : /* illegal or no filename */
1386 0 : result = EXIT_FAILED;
1387 0 : d_printf("illegal or missing name '%s'\n", filename);
1388 0 : goto out;
1389 : }
1390 :
1391 :
1392 114 : fileattr = get_fileinfo(cli, filename);
1393 114 : isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1394 : == FILE_ATTRIBUTE_DIRECTORY;
1395 :
1396 : /*
1397 : * if we've got as far as here then we have already evaluated
1398 : * the args.
1399 : */
1400 114 : if (test_args) {
1401 0 : result = EXIT_OK;
1402 0 : goto out;
1403 : }
1404 :
1405 114 : mask = NULL;
1406 : /* make sure we have a trailing '\*' for directory */
1407 114 : if (!isdirectory) {
1408 0 : mask = talloc_strdup(ctx, filename);
1409 114 : } else if (strlen(filename) > 1) {
1410 : /*
1411 : * if the passed file name doesn't have a trailing '\'
1412 : * append it.
1413 : */
1414 114 : char *name_end = strrchr(filename, DIRSEP_CHAR);
1415 114 : if (name_end != filename + strlen(filename) + 1) {
1416 114 : mask = talloc_asprintf(ctx, "%s\\*", filename);
1417 : } else {
1418 0 : mask = talloc_strdup(ctx, filename);
1419 : }
1420 : } else {
1421 : /* filename is a single '\', just append '*' */
1422 0 : mask = talloc_asprintf_append(mask, "%s*", filename);
1423 : }
1424 :
1425 114 : if (!mask) {
1426 0 : result = EXIT_FAILED;
1427 0 : goto out;
1428 : }
1429 :
1430 : /*
1431 : * prepare for automatic propagation of the acl passed on the
1432 : * cmdline.
1433 : */
1434 :
1435 114 : ntstatus = prepare_inheritance_propagation(ctx, filename,
1436 : cbstate);
1437 114 : if (!NT_STATUS_IS_OK(ntstatus)) {
1438 6 : d_printf("error: %s processing %s\n",
1439 : nt_errstr(ntstatus), filename);
1440 6 : result = EXIT_FAILED;
1441 6 : goto out;
1442 : }
1443 :
1444 108 : result = cacl_set_from_sd(cli, filename, cbstate->aclsd,
1445 108 : cbstate->mode, cbstate->numeric);
1446 :
1447 : /*
1448 : * strictly speaking it could be considered an error if a file was
1449 : * specified with '--propagate-inheritance'. However we really want
1450 : * to eventually get rid of '--propagate-inheritance' so we will be
1451 : * more forgiving here and instead just exit early.
1452 : */
1453 108 : if (!isdirectory || (result != EXIT_OK)) {
1454 0 : goto out;
1455 : }
1456 :
1457 : /* check if there is actually any need to propagate */
1458 108 : if (cbstate->acl_no_propagate) {
1459 0 : goto out;
1460 : }
1461 : /* get inheritable attributes this parent container (e.g. filename) */
1462 108 : ntstatus = get_inheritable_aces(ctx, filename, cbstate);
1463 108 : if (NT_STATUS_IS_OK(ntstatus)) {
1464 : /* process children */
1465 108 : ntstatus = cli_list(cli, mask, attribute,
1466 : cacl_set_cb,
1467 : (void *)cbstate);
1468 : }
1469 :
1470 108 : if (!NT_STATUS_IS_OK(ntstatus)) {
1471 0 : d_printf("error: %s processing %s\n",
1472 : nt_errstr(ntstatus), filename);
1473 0 : result = EXIT_FAILED;
1474 0 : goto out;
1475 : }
1476 :
1477 108 : out:
1478 114 : TALLOC_FREE(ctx);
1479 114 : return result;
1480 : }
1481 :
1482 : /****************************************************************************
1483 : main program
1484 : ****************************************************************************/
1485 1954 : int main(int argc, char *argv[])
1486 : {
1487 1954 : const char **argv_const = discard_const_p(const char *, argv);
1488 : char *share;
1489 : int opt;
1490 1954 : enum acl_mode mode = SMB_ACL_SET;
1491 : static char *the_acl = NULL;
1492 1954 : enum chown_mode change_mode = REQUEST_NONE;
1493 : int result;
1494 : char *path;
1495 1954 : char *filename = NULL;
1496 : poptContext pc;
1497 : /* numeric is set when the user wants numeric SIDs and ACEs rather
1498 : than going via LSA calls to resolve them */
1499 1954 : int numeric = 0;
1500 1954 : struct cli_state *targetcli = NULL;
1501 1954 : struct cli_credentials *creds = NULL;
1502 1954 : char *targetfile = NULL;
1503 : NTSTATUS status;
1504 : bool ok;
1505 1954 : struct loadparm_context *lp_ctx = NULL;
1506 :
1507 11724 : struct poptOption long_options[] = {
1508 : POPT_AUTOHELP
1509 : {
1510 : .longName = "delete",
1511 : .shortName = 'D',
1512 : .argInfo = POPT_ARG_STRING,
1513 : .arg = NULL,
1514 : .val = 'D',
1515 : .descrip = "Delete an acl",
1516 : .argDescrip = "ACL",
1517 : },
1518 : {
1519 : .longName = "modify",
1520 : .shortName = 'M',
1521 : .argInfo = POPT_ARG_STRING,
1522 : .arg = NULL,
1523 : .val = 'M',
1524 : .descrip = "Modify an acl",
1525 : .argDescrip = "ACL",
1526 : },
1527 : {
1528 : .longName = "add",
1529 : .shortName = 'a',
1530 : .argInfo = POPT_ARG_STRING,
1531 : .arg = NULL,
1532 : .val = 'a',
1533 : .descrip = "Add an acl",
1534 : .argDescrip = "ACL",
1535 : },
1536 : {
1537 : .longName = "set",
1538 : .shortName = 'S',
1539 : .argInfo = POPT_ARG_STRING,
1540 : .arg = NULL,
1541 : .val = 'S',
1542 : .descrip = "Set acls",
1543 : .argDescrip = "ACLS",
1544 : },
1545 : {
1546 : .longName = "chown",
1547 : .shortName = 'C',
1548 : .argInfo = POPT_ARG_STRING,
1549 : .arg = NULL,
1550 : .val = 'C',
1551 : .descrip = "Change ownership of a file",
1552 : .argDescrip = "USERNAME",
1553 : },
1554 : {
1555 : .longName = "chgrp",
1556 : .shortName = 'G',
1557 : .argInfo = POPT_ARG_STRING,
1558 : .arg = NULL,
1559 : .val = 'G',
1560 : .descrip = "Change group ownership of a file",
1561 : .argDescrip = "GROUPNAME",
1562 : },
1563 : {
1564 : .longName = "inherit",
1565 : .shortName = 'I',
1566 : .argInfo = POPT_ARG_STRING,
1567 : .arg = NULL,
1568 : .val = 'I',
1569 : .descrip = "Inherit allow|remove|copy",
1570 : },
1571 : {
1572 : .longName = "propagate-inheritance",
1573 : .shortName = 0,
1574 : .argInfo = POPT_ARG_NONE,
1575 : .arg = &inheritance,
1576 : .val = 1,
1577 : .descrip = "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify",
1578 : },
1579 : {
1580 : .longName = "numeric",
1581 : .shortName = 0,
1582 : .argInfo = POPT_ARG_NONE,
1583 : .arg = &numeric,
1584 : .val = 1,
1585 : .descrip = "Don't resolve sids or masks to names",
1586 : },
1587 : {
1588 : .longName = "sddl",
1589 : .shortName = 0,
1590 : .argInfo = POPT_ARG_NONE,
1591 : .arg = &sddl,
1592 : .val = 1,
1593 : .descrip = "Output and input acls in sddl format",
1594 : },
1595 : {
1596 : .longName = "query-security-info",
1597 : .shortName = 0,
1598 : .argInfo = POPT_ARG_INT,
1599 : .arg = &query_sec_info,
1600 : .val = 1,
1601 : .descrip = "The security-info flags for queries"
1602 : },
1603 : {
1604 : .longName = "set-security-info",
1605 : .shortName = 0,
1606 : .argInfo = POPT_ARG_INT,
1607 : .arg = &set_sec_info,
1608 : .val = 1,
1609 : .descrip = "The security-info flags for modifications"
1610 : },
1611 : {
1612 : .longName = "test-args",
1613 : .shortName = 't',
1614 : .argInfo = POPT_ARG_NONE,
1615 : .arg = &test_args,
1616 : .val = 1,
1617 : .descrip = "Test arguments"
1618 : },
1619 : {
1620 : .longName = "domain-sid",
1621 : .shortName = 0,
1622 : .argInfo = POPT_ARG_STRING,
1623 : .arg = &domain_sid,
1624 : .val = 0,
1625 : .descrip = "Domain SID for sddl",
1626 : .argDescrip = "SID"},
1627 : {
1628 : .longName = "maximum-access",
1629 : .shortName = 'x',
1630 : .argInfo = POPT_ARG_NONE,
1631 : .arg = NULL,
1632 : .val = 'x',
1633 : .descrip = "Query maximum permissions",
1634 : },
1635 1954 : POPT_COMMON_SAMBA
1636 1954 : POPT_COMMON_CONNECTION
1637 1954 : POPT_COMMON_CREDENTIALS
1638 1954 : POPT_LEGACY_S3
1639 1954 : POPT_COMMON_VERSION
1640 : POPT_TABLEEND
1641 : };
1642 :
1643 : struct cli_state *cli;
1644 1954 : TALLOC_CTX *frame = talloc_stackframe();
1645 1954 : const char *owner_username = "";
1646 : char *server;
1647 :
1648 1954 : smb_init_locale();
1649 :
1650 1954 : ok = samba_cmdline_init(frame,
1651 : SAMBA_CMDLINE_CONFIG_CLIENT,
1652 : false /* require_smbconf */);
1653 1954 : if (!ok) {
1654 0 : DBG_ERR("Failed to init cmdline parser!\n");
1655 0 : TALLOC_FREE(frame);
1656 0 : exit(1);
1657 : }
1658 1954 : lp_ctx = samba_cmdline_get_lp_ctx();
1659 : /* set default debug level to 1 regardless of what smb.conf sets */
1660 1954 : lpcfg_set_cmdline(lp_ctx, "log level", "1");
1661 :
1662 1954 : setlinebuf(stdout);
1663 :
1664 1954 : pc = samba_popt_get_context(getprogname(),
1665 : argc,
1666 : argv_const,
1667 : long_options,
1668 : 0);
1669 1954 : if (pc == NULL) {
1670 0 : DBG_ERR("Failed to setup popt context!\n");
1671 0 : TALLOC_FREE(frame);
1672 0 : exit(1);
1673 : }
1674 :
1675 1954 : poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: "
1676 : "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
1677 :
1678 3396 : while ((opt = poptGetNextOpt(pc)) != -1) {
1679 1442 : switch (opt) {
1680 92 : case 'S':
1681 92 : the_acl = smb_xstrdup(poptGetOptArg(pc));
1682 92 : mode = SMB_ACL_SET;
1683 92 : break;
1684 :
1685 66 : case 'D':
1686 66 : the_acl = smb_xstrdup(poptGetOptArg(pc));
1687 66 : mode = SMB_ACL_DELETE;
1688 66 : break;
1689 :
1690 820 : case 'M':
1691 820 : the_acl = smb_xstrdup(poptGetOptArg(pc));
1692 820 : mode = SMB_ACL_MODIFY;
1693 820 : break;
1694 :
1695 276 : case 'a':
1696 276 : the_acl = smb_xstrdup(poptGetOptArg(pc));
1697 276 : mode = SMB_ACL_ADD;
1698 276 : break;
1699 :
1700 36 : case 'C':
1701 36 : owner_username = poptGetOptArg(pc);
1702 36 : change_mode = REQUEST_CHOWN;
1703 36 : break;
1704 :
1705 4 : case 'G':
1706 4 : owner_username = poptGetOptArg(pc);
1707 4 : change_mode = REQUEST_CHGRP;
1708 4 : break;
1709 :
1710 18 : case 'I':
1711 18 : owner_username = poptGetOptArg(pc);
1712 18 : change_mode = REQUEST_INHERIT;
1713 18 : break;
1714 0 : case 'm':
1715 0 : lpcfg_set_cmdline(lp_ctx, "client max protocol", poptGetOptArg(pc));
1716 0 : break;
1717 4 : case 'x':
1718 4 : want_mxac = true;
1719 4 : break;
1720 0 : case POPT_ERROR_BADOPT:
1721 0 : fprintf(stderr, "\nInvalid option %s: %s\n\n",
1722 : poptBadOption(pc, 0), poptStrerror(opt));
1723 0 : poptPrintUsage(pc, stderr, 0);
1724 0 : exit(1);
1725 : }
1726 : }
1727 1954 : if (inheritance && !the_acl) {
1728 0 : poptPrintUsage(pc, stderr, 0);
1729 0 : return -1;
1730 : }
1731 :
1732 1954 : if(!poptPeekArg(pc)) {
1733 0 : poptPrintUsage(pc, stderr, 0);
1734 0 : return -1;
1735 : }
1736 :
1737 1954 : path = talloc_strdup(frame, poptGetArg(pc));
1738 1954 : if (!path) {
1739 0 : return -1;
1740 : }
1741 :
1742 1954 : if (strncmp(path, "\\\\", 2) && strncmp(path, "//", 2)) {
1743 0 : printf("Invalid argument: %s\n", path);
1744 0 : return -1;
1745 : }
1746 :
1747 1954 : if(!poptPeekArg(pc)) {
1748 0 : poptPrintUsage(pc, stderr, 0);
1749 0 : return -1;
1750 : }
1751 :
1752 1954 : filename = talloc_strdup(frame, poptGetArg(pc));
1753 1954 : if (!filename) {
1754 0 : return -1;
1755 : }
1756 :
1757 1954 : poptFreeContext(pc);
1758 1954 : samba_cmdline_burn(argc, argv);
1759 :
1760 1954 : string_replace(path,'/','\\');
1761 :
1762 1954 : server = talloc_strdup(frame, path+2);
1763 1954 : if (!server) {
1764 0 : return -1;
1765 : }
1766 1954 : share = strchr_m(server,'\\');
1767 1954 : if (share == NULL) {
1768 0 : printf("Invalid argument\n");
1769 0 : return -1;
1770 : }
1771 :
1772 1954 : *share = 0;
1773 1954 : share++;
1774 :
1775 1954 : creds = samba_cmdline_get_creds();
1776 :
1777 : /* Make connection to server */
1778 1954 : if (!test_args) {
1779 1954 : cli = connect_one(creds, server, share);
1780 1954 : if (!cli) {
1781 0 : exit(EXIT_FAILED);
1782 : }
1783 : } else {
1784 0 : exit(0);
1785 : }
1786 :
1787 1954 : string_replace(filename, '/', '\\');
1788 1954 : if (filename[0] != '\\') {
1789 1946 : filename = talloc_asprintf(frame,
1790 : "\\%s",
1791 : filename);
1792 1946 : if (!filename) {
1793 0 : return -1;
1794 : }
1795 : }
1796 :
1797 1954 : status = cli_resolve_path(frame,
1798 : "",
1799 : creds,
1800 : cli,
1801 : filename,
1802 : &targetcli,
1803 : &targetfile);
1804 1954 : if (!NT_STATUS_IS_OK(status)) {
1805 0 : DEBUG(0,("cli_resolve_path failed for %s! (%s)\n", filename, nt_errstr(status)));
1806 0 : return -1;
1807 : }
1808 :
1809 : /* Perform requested action */
1810 :
1811 1954 : if (change_mode == REQUEST_INHERIT) {
1812 18 : result = inherit(targetcli, targetfile, owner_username);
1813 1936 : } else if (change_mode != REQUEST_NONE) {
1814 40 : result = owner_set(targetcli, change_mode, targetfile, owner_username);
1815 1896 : } else if (the_acl) {
1816 1254 : if (inheritance) {
1817 114 : struct cacl_callback_state cbstate = {
1818 : .creds = creds,
1819 : .cli = targetcli,
1820 : .mode = mode,
1821 : .the_acl = the_acl,
1822 : .numeric = numeric,
1823 : };
1824 114 : result = inheritance_cacl_set(targetfile, &cbstate);
1825 : } else {
1826 1140 : result = cacl_set(targetcli,
1827 : targetfile,
1828 : the_acl,
1829 : mode,
1830 : numeric);
1831 : }
1832 : } else {
1833 642 : result = cacl_dump(targetcli, targetfile, numeric);
1834 : }
1835 :
1836 1954 : gfree_all();
1837 1954 : TALLOC_FREE(frame);
1838 :
1839 1954 : return result;
1840 : }
|