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 const char *save_file = NULL;
42 : static const char *restore_file = NULL;
43 : static int recurse;
44 : static int test_args;
45 : static int sddl;
46 : static int query_sec_info = -1;
47 : static int set_sec_info = -1;
48 : static bool want_mxac;
49 :
50 : static const char *domain_sid = NULL;
51 :
52 : enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
53 : enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP, REQUEST_INHERIT};
54 : enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
55 :
56 : struct cacl_callback_state {
57 : struct cli_credentials *creds;
58 : struct cli_state *cli;
59 : struct security_descriptor *aclsd;
60 : struct security_acl *acl_to_add;
61 : enum acl_mode mode;
62 : char *the_acl;
63 : bool acl_no_propagate;
64 : bool numeric;
65 : };
66 :
67 128 : static NTSTATUS cli_lsa_lookup_domain_sid(struct cli_state *cli,
68 : struct dom_sid *sid)
69 : {
70 128 : union lsa_PolicyInformation *info = NULL;
71 128 : struct smbXcli_tcon *orig_tcon = NULL;
72 128 : char *orig_share = NULL;
73 128 : struct rpc_pipe_client *rpc_pipe = NULL;
74 : struct policy_handle handle;
75 : NTSTATUS status, result;
76 128 : TALLOC_CTX *frame = talloc_stackframe();
77 :
78 128 : if (cli_state_has_tcon(cli)) {
79 128 : cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
80 : }
81 :
82 128 : status = cli_tree_connect(cli, "IPC$", "?????", NULL);
83 128 : if (!NT_STATUS_IS_OK(status)) {
84 0 : goto done;
85 : }
86 :
87 128 : status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &rpc_pipe);
88 128 : if (!NT_STATUS_IS_OK(status)) {
89 0 : goto tdis;
90 : }
91 :
92 128 : status = rpccli_lsa_open_policy(rpc_pipe, frame, True,
93 : GENERIC_EXECUTE_ACCESS, &handle);
94 128 : if (!NT_STATUS_IS_OK(status)) {
95 0 : goto tdis;
96 : }
97 :
98 128 : status = dcerpc_lsa_QueryInfoPolicy2(rpc_pipe->binding_handle,
99 : frame, &handle,
100 : LSA_POLICY_INFO_DOMAIN,
101 : &info, &result);
102 :
103 128 : if (any_nt_status_not_ok(status, result, &status)) {
104 128 : goto tdis;
105 : }
106 :
107 0 : *sid = *info->domain.sid;
108 :
109 128 : tdis:
110 128 : TALLOC_FREE(rpc_pipe);
111 128 : cli_tdis(cli);
112 128 : done:
113 128 : cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
114 128 : TALLOC_FREE(frame);
115 128 : return status;
116 : }
117 :
118 128 : static struct dom_sid *get_domain_sid(struct cli_state *cli)
119 : {
120 : NTSTATUS status;
121 : struct dom_sid_buf buf;
122 :
123 128 : struct dom_sid *sid = talloc(talloc_tos(), struct dom_sid);
124 128 : if (sid == NULL) {
125 0 : DEBUG(0, ("Out of memory\n"));
126 0 : return NULL;
127 : }
128 :
129 128 : if (domain_sid) {
130 0 : if (!dom_sid_parse(domain_sid, sid)) {
131 0 : DEBUG(0,("failed to parse domain sid\n"));
132 0 : TALLOC_FREE(sid);
133 : }
134 : } else {
135 128 : status = cli_lsa_lookup_domain_sid(cli, sid);
136 :
137 128 : if (!NT_STATUS_IS_OK(status)) {
138 128 : DEBUG(0,("failed to lookup domain sid: %s\n", nt_errstr(status)));
139 128 : TALLOC_FREE(sid);
140 : }
141 :
142 : }
143 :
144 128 : DEBUG(2,("Domain SID: %s\n", dom_sid_str_buf(sid, &buf)));
145 128 : return sid;
146 : }
147 :
148 : /* add an ACE to a list of ACEs in a struct security_acl */
149 8490 : static bool add_ace_with_ctx(TALLOC_CTX *ctx, struct security_acl **the_acl,
150 : const struct security_ace *ace)
151 :
152 : {
153 8490 : struct security_acl *acl = *the_acl;
154 :
155 8490 : if (acl == NULL) {
156 2292 : acl = make_sec_acl(ctx, 3, 0, NULL);
157 2292 : if (acl == NULL) {
158 0 : return false;
159 : }
160 : }
161 :
162 8490 : if (acl->num_aces == UINT32_MAX) {
163 0 : return false;
164 : }
165 8490 : ADD_TO_ARRAY(
166 : acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
167 8490 : *the_acl = acl;
168 8490 : return True;
169 : }
170 :
171 1654 : static bool add_ace(struct security_acl **the_acl, struct security_ace *ace)
172 : {
173 1654 : return add_ace_with_ctx(talloc_tos(), the_acl, ace);
174 : }
175 :
176 : /* parse a ascii version of a security descriptor */
177 1370 : static struct security_descriptor *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str)
178 : {
179 1370 : const char *p = str;
180 : char *tok;
181 1370 : struct security_descriptor *ret = NULL;
182 : size_t sd_size;
183 1370 : struct dom_sid owner_sid = { .num_auths = 0 };
184 1370 : bool have_owner = false;
185 1370 : struct dom_sid group_sid = { .num_auths = 0 };
186 1370 : bool have_group = false;
187 1370 : struct security_acl *dacl=NULL;
188 1370 : int revision=1;
189 :
190 2776 : while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
191 1406 : if (strncmp(tok,"REVISION:", 9) == 0) {
192 12 : revision = strtol(tok+9, NULL, 16);
193 12 : continue;
194 : }
195 :
196 1394 : if (strncmp(tok,"OWNER:", 6) == 0) {
197 12 : if (have_owner) {
198 0 : printf("Only specify owner once\n");
199 0 : goto done;
200 : }
201 12 : if (!StringToSid(cli, &owner_sid, tok+6)) {
202 0 : printf("Failed to parse owner sid\n");
203 0 : goto done;
204 : }
205 12 : have_owner = true;
206 12 : continue;
207 : }
208 :
209 1382 : if (strncmp(tok,"GROUP:", 6) == 0) {
210 12 : if (have_group) {
211 0 : printf("Only specify group once\n");
212 0 : goto done;
213 : }
214 12 : if (!StringToSid(cli, &group_sid, tok+6)) {
215 0 : printf("Failed to parse group sid\n");
216 0 : goto done;
217 : }
218 12 : have_group = true;
219 12 : continue;
220 : }
221 :
222 1370 : if (strncmp(tok,"ACL:", 4) == 0) {
223 : struct security_ace ace;
224 1370 : if (!parse_ace(cli, &ace, tok+4)) {
225 0 : goto done;
226 : }
227 1370 : if(!add_ace(&dacl, &ace)) {
228 0 : printf("Failed to add ACL %s\n", tok);
229 0 : goto done;
230 : }
231 1370 : continue;
232 : }
233 :
234 0 : printf("Failed to parse token '%s' in security descriptor,\n", tok);
235 0 : goto done;
236 : }
237 :
238 1370 : ret = make_sec_desc(
239 : ctx,
240 : revision,
241 : SEC_DESC_SELF_RELATIVE,
242 : have_owner ? &owner_sid : NULL,
243 : have_group ? &group_sid : NULL,
244 : NULL,
245 : dacl,
246 : &sd_size);
247 :
248 1370 : done:
249 1370 : return ret;
250 : }
251 :
252 : /*****************************************************
253 : get fileinfo for filename
254 : *******************************************************/
255 746 : static uint16_t get_fileinfo(struct cli_state *cli, const char *filename)
256 : {
257 746 : uint16_t fnum = (uint16_t)-1;
258 : NTSTATUS status;
259 746 : struct smb_create_returns cr = {0};
260 :
261 : /* The desired access below is the only one I could find that works
262 : with NT4, W2KP and Samba */
263 :
264 746 : status = cli_ntcreate(
265 : cli, /* cli */
266 : filename, /* fname */
267 : 0, /* CreatFlags */
268 : READ_CONTROL_ACCESS, /* CreatFlags */
269 : 0, /* FileAttributes */
270 : FILE_SHARE_READ|
271 : FILE_SHARE_WRITE, /* ShareAccess */
272 : FILE_OPEN, /* CreateDisposition */
273 : 0x0, /* CreateOptions */
274 : 0x0, /* SecurityFlags */
275 : &fnum, /* pfid */
276 : &cr); /* cr */
277 746 : if (!NT_STATUS_IS_OK(status)) {
278 0 : printf("Failed to open %s: %s\n", filename, nt_errstr(status));
279 0 : return 0;
280 : }
281 :
282 746 : cli_close(cli, fnum);
283 746 : return cr.file_attributes;
284 : }
285 :
286 : /*****************************************************
287 : get sec desc for filename
288 : *******************************************************/
289 3104 : static struct security_descriptor *get_secdesc_with_ctx(TALLOC_CTX *ctx,
290 : struct cli_state *cli,
291 : const char *filename)
292 : {
293 3104 : uint16_t fnum = (uint16_t)-1;
294 : struct security_descriptor *sd;
295 : NTSTATUS status;
296 : uint32_t sec_info;
297 3104 : uint32_t desired_access = 0;
298 :
299 3104 : if (query_sec_info == -1) {
300 3104 : sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL;
301 : } else {
302 0 : sec_info = query_sec_info;
303 : }
304 :
305 3104 : if (sec_info & (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL)) {
306 3104 : desired_access |= SEC_STD_READ_CONTROL;
307 : }
308 3104 : if (sec_info & SECINFO_SACL) {
309 0 : desired_access |= SEC_FLAG_SYSTEM_SECURITY;
310 : }
311 :
312 3104 : if (desired_access == 0) {
313 0 : desired_access |= SEC_STD_READ_CONTROL;
314 : }
315 :
316 3104 : status = cli_ntcreate(cli, filename, 0, desired_access,
317 : 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
318 : FILE_OPEN, 0x0, 0x0, &fnum, NULL);
319 3104 : if (!NT_STATUS_IS_OK(status)) {
320 0 : printf("Failed to open %s: %s\n", filename, nt_errstr(status));
321 0 : return NULL;
322 : }
323 :
324 3104 : status = cli_query_security_descriptor(cli, fnum, sec_info,
325 : ctx, &sd);
326 :
327 3104 : cli_close(cli, fnum);
328 :
329 3104 : if (!NT_STATUS_IS_OK(status)) {
330 0 : printf("Failed to get security descriptor: %s\n",
331 : nt_errstr(status));
332 0 : return NULL;
333 : }
334 3104 : return sd;
335 : }
336 :
337 2054 : static struct security_descriptor *get_secdesc(struct cli_state *cli,
338 : const char *filename)
339 : {
340 2054 : return get_secdesc_with_ctx(talloc_tos(), cli, filename);
341 : }
342 : /*****************************************************
343 : set sec desc for filename
344 : *******************************************************/
345 2028 : static bool set_secdesc(struct cli_state *cli, const char *filename,
346 : struct security_descriptor *sd)
347 : {
348 2028 : uint16_t fnum = (uint16_t)-1;
349 2028 : bool result=true;
350 : NTSTATUS status;
351 2028 : uint32_t desired_access = 0;
352 : uint32_t sec_info;
353 :
354 2028 : if (set_sec_info == -1) {
355 2028 : sec_info = 0;
356 :
357 2028 : if (sd->dacl || (sd->type & SEC_DESC_DACL_PRESENT)) {
358 1988 : sec_info |= SECINFO_DACL;
359 : }
360 2028 : if (sd->sacl || (sd->type & SEC_DESC_SACL_PRESENT)) {
361 0 : sec_info |= SECINFO_SACL;
362 : }
363 2028 : if (sd->owner_sid) {
364 1922 : sec_info |= SECINFO_OWNER;
365 : }
366 2028 : if (sd->group_sid) {
367 1890 : sec_info |= SECINFO_GROUP;
368 : }
369 : } else {
370 0 : sec_info = set_sec_info;
371 : }
372 :
373 : /* Make the desired_access more specific. */
374 2028 : if (sec_info & SECINFO_DACL) {
375 1988 : desired_access |= SEC_STD_WRITE_DAC;
376 : }
377 2028 : if (sec_info & SECINFO_SACL) {
378 0 : desired_access |= SEC_FLAG_SYSTEM_SECURITY;
379 : }
380 2028 : if (sec_info & (SECINFO_OWNER | SECINFO_GROUP)) {
381 1926 : desired_access |= SEC_STD_WRITE_OWNER;
382 : }
383 :
384 2028 : status = cli_ntcreate(cli, filename, 0,
385 : desired_access,
386 : 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
387 : FILE_OPEN, 0x0, 0x0, &fnum, NULL);
388 2028 : if (!NT_STATUS_IS_OK(status)) {
389 0 : printf("Failed to open %s: %s\n", filename, nt_errstr(status));
390 0 : return false;
391 : }
392 :
393 2028 : status = cli_set_security_descriptor(cli, fnum, sec_info, sd);
394 2028 : if (!NT_STATUS_IS_OK(status)) {
395 2 : printf("ERROR: security descriptor set failed: %s\n",
396 : nt_errstr(status));
397 2 : result=false;
398 : }
399 :
400 2028 : cli_close(cli, fnum);
401 2028 : return result;
402 : }
403 :
404 : /*****************************************************
405 : get maximum access for a file
406 : *******************************************************/
407 4 : static int cacl_mxac(struct cli_state *cli, const char *filename)
408 : {
409 : NTSTATUS status;
410 : uint32_t mxac;
411 :
412 4 : status = cli_query_mxac(cli, filename, &mxac);
413 4 : if (!NT_STATUS_IS_OK(status)) {
414 0 : printf("Failed to get mxac: %s\n", nt_errstr(status));
415 0 : return EXIT_FAILED;
416 : }
417 :
418 4 : printf("Maximum access: 0x%x\n", mxac);
419 :
420 4 : return EXIT_OK;
421 : }
422 :
423 :
424 : /*****************************************************
425 : dump the acls for a file
426 : *******************************************************/
427 642 : static int cacl_dump(struct cli_state *cli, const char *filename, bool numeric)
428 : {
429 : struct security_descriptor *sd;
430 : int ret;
431 :
432 642 : if (test_args) {
433 0 : return EXIT_OK;
434 : }
435 :
436 642 : sd = get_secdesc(cli, filename);
437 642 : if (sd == NULL) {
438 0 : return EXIT_FAILED;
439 : }
440 :
441 642 : if (sddl) {
442 4 : char *str = sddl_encode(talloc_tos(), sd, get_domain_sid(cli));
443 4 : if (str == NULL) {
444 0 : return EXIT_FAILED;
445 : }
446 4 : printf("%s\n", str);
447 4 : TALLOC_FREE(str);
448 : } else {
449 638 : sec_desc_print(cli, stdout, sd, numeric);
450 : }
451 :
452 642 : if (want_mxac) {
453 4 : ret = cacl_mxac(cli, filename);
454 4 : if (ret != EXIT_OK) {
455 0 : return ret;
456 : }
457 : }
458 :
459 642 : return EXIT_OK;
460 : }
461 :
462 : /*****************************************************
463 : Change the ownership or group ownership of a file. Just
464 : because the NT docs say this can't be done :-). JRA.
465 : *******************************************************/
466 :
467 40 : static int owner_set(struct cli_state *cli, enum chown_mode change_mode,
468 : const char *filename, const char *new_username)
469 : {
470 : struct dom_sid sid;
471 : struct security_descriptor *sd;
472 : size_t sd_size;
473 :
474 40 : if (!StringToSid(cli, &sid, new_username))
475 0 : return EXIT_PARSE_ERROR;
476 :
477 40 : sd = make_sec_desc(talloc_tos(),
478 : SECURITY_DESCRIPTOR_REVISION_1,
479 : SEC_DESC_SELF_RELATIVE,
480 : (change_mode == REQUEST_CHOWN) ? &sid : NULL,
481 : (change_mode == REQUEST_CHGRP) ? &sid : NULL,
482 : NULL, NULL, &sd_size);
483 :
484 40 : if (!set_secdesc(cli, filename, sd)) {
485 2 : return EXIT_FAILED;
486 : }
487 :
488 38 : return EXIT_OK;
489 : }
490 :
491 :
492 : /* The MSDN is contradictory over the ordering of ACE entries in an
493 : ACL. However NT4 gives a "The information may have been modified
494 : by a computer running Windows NT 5.0" if denied ACEs do not appear
495 : before allowed ACEs. At
496 : http://technet.microsoft.com/en-us/library/cc781716.aspx the
497 : canonical order is specified as "Explicit Deny, Explicit Allow,
498 : Inherited ACEs unchanged" */
499 :
500 20906 : static int ace_compare(struct security_ace *ace1, struct security_ace *ace2)
501 : {
502 20906 : if (security_ace_equal(ace1, ace2))
503 396 : return 0;
504 :
505 20510 : if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
506 6176 : !(ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
507 1944 : return 1;
508 18566 : if (!(ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
509 14334 : (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
510 2822 : return -1;
511 15744 : if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
512 4232 : (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
513 4232 : return ace1 - ace2;
514 :
515 11512 : if (ace1->type != ace2->type)
516 0 : return ace2->type - ace1->type;
517 :
518 11512 : if (dom_sid_compare(&ace1->trustee, &ace2->trustee))
519 11312 : return dom_sid_compare(&ace1->trustee, &ace2->trustee);
520 :
521 200 : if (ace1->flags != ace2->flags)
522 196 : return ace1->flags - ace2->flags;
523 :
524 4 : if (ace1->access_mask != ace2->access_mask)
525 4 : return ace1->access_mask - ace2->access_mask;
526 :
527 0 : if (ace1->size != ace2->size)
528 0 : return ace1->size - ace2->size;
529 :
530 0 : return memcmp(ace1, ace2, sizeof(struct security_ace));
531 : }
532 :
533 1988 : static void sort_acl(struct security_acl *the_acl)
534 : {
535 : uint32_t i;
536 1988 : if (!the_acl) return;
537 :
538 1988 : TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
539 :
540 12344 : for (i=1;i<the_acl->num_aces;) {
541 10356 : if (security_ace_equal(&the_acl->aces[i-1],
542 10356 : &the_acl->aces[i])) {
543 46 : ARRAY_DEL_ELEMENT(
544 : the_acl->aces, i, the_acl->num_aces);
545 46 : the_acl->num_aces--;
546 : } else {
547 10310 : i++;
548 : }
549 : }
550 : }
551 :
552 : /*****************************************************
553 : set the ACLs on a file given a security descriptor
554 : *******************************************************/
555 :
556 1970 : static int cacl_set_from_sd(struct cli_state *cli, const char *filename,
557 : struct security_descriptor *sd, enum acl_mode mode,
558 : bool numeric)
559 : {
560 1970 : struct security_descriptor *old = NULL;
561 : uint32_t i, j;
562 : size_t sd_size;
563 1970 : int result = EXIT_OK;
564 :
565 1970 : if (!sd) return EXIT_PARSE_ERROR;
566 1970 : if (test_args) return EXIT_OK;
567 :
568 1970 : if (mode != SMB_ACL_SET) {
569 : /*
570 : * Do not fetch old ACL when it will be overwritten
571 : * completely with a new one.
572 : */
573 1282 : old = get_secdesc(cli, filename);
574 :
575 1282 : if (!old) {
576 0 : return EXIT_FAILED;
577 : }
578 : }
579 :
580 : /* the logic here is rather more complex than I would like */
581 1970 : switch (mode) {
582 66 : case SMB_ACL_DELETE:
583 132 : for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
584 66 : bool found = False;
585 :
586 316 : for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
587 290 : if (security_ace_equal(&sd->dacl->aces[i],
588 290 : &old->dacl->aces[j])) {
589 : uint32_t k;
590 84 : for (k=j; k<old->dacl->num_aces-1;k++) {
591 44 : old->dacl->aces[k] = old->dacl->aces[k+1];
592 : }
593 40 : old->dacl->num_aces--;
594 40 : found = True;
595 40 : break;
596 : }
597 : }
598 :
599 66 : if (!found) {
600 26 : printf("ACL for ACE:");
601 26 : print_ace(cli, stdout, &sd->dacl->aces[i],
602 : numeric);
603 26 : printf(" not found\n");
604 : }
605 : }
606 66 : break;
607 :
608 932 : case SMB_ACL_MODIFY:
609 1864 : for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
610 932 : bool found = False;
611 :
612 5068 : for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
613 4136 : if (dom_sid_equal(&sd->dacl->aces[i].trustee,
614 4136 : &old->dacl->aces[j].trustee)) {
615 968 : old->dacl->aces[j] = sd->dacl->aces[i];
616 968 : found = True;
617 : }
618 : }
619 :
620 932 : if (!found) {
621 : fstring str;
622 :
623 0 : SidToString(cli, str,
624 0 : &sd->dacl->aces[i].trustee,
625 : numeric);
626 0 : printf("ACL for SID %s not found\n", str);
627 : }
628 : }
629 :
630 932 : if (sd->owner_sid) {
631 0 : old->owner_sid = sd->owner_sid;
632 : }
633 :
634 932 : if (sd->group_sid) {
635 0 : old->group_sid = sd->group_sid;
636 : }
637 :
638 932 : break;
639 :
640 284 : case SMB_ACL_ADD:
641 568 : for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
642 284 : add_ace(&old->dacl, &sd->dacl->aces[i]);
643 : }
644 284 : break;
645 :
646 688 : case SMB_ACL_SET:
647 688 : old = sd;
648 688 : break;
649 : }
650 :
651 : /* Denied ACE entries must come before allowed ones */
652 1970 : sort_acl(old->dacl);
653 :
654 : /* Create new security descriptor and set it */
655 :
656 : /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER.
657 : But if we're sending an owner, even if it's the same as the one
658 : that already exists then W2K3 insists we open with WRITE_OWNER access.
659 : I need to check that setting a SD with no owner set works against WNT
660 : and W2K. JRA.
661 : */
662 :
663 1970 : sd = make_sec_desc(talloc_tos(),old->revision, old->type,
664 1970 : old->owner_sid, old->group_sid,
665 : NULL, old->dacl, &sd_size);
666 :
667 1970 : if (!set_secdesc(cli, filename, sd)) {
668 0 : result = EXIT_FAILED;
669 : }
670 :
671 1970 : return result;
672 : }
673 :
674 : /*****************************************************
675 : set the ACLs on a file given an ascii description
676 : *******************************************************/
677 :
678 1252 : static int cacl_set(struct cli_state *cli, const char *filename,
679 : char *the_acl, enum acl_mode mode, bool numeric)
680 : {
681 1252 : struct security_descriptor *sd = NULL;
682 :
683 1252 : if (sddl) {
684 4 : const char *msg = NULL;
685 4 : size_t msg_offset = 0;
686 4 : enum ace_condition_flags flags =
687 : ACE_CONDITION_FLAG_ALLOW_DEVICE;
688 4 : sd = sddl_decode_err_msg(talloc_tos(),
689 : the_acl,
690 4 : get_domain_sid(cli),
691 : flags,
692 : &msg,
693 : &msg_offset);
694 4 : if (sd == NULL) {
695 0 : DBG_ERR("could not decode '%s'\n", the_acl);
696 0 : if (msg != NULL) {
697 0 : DBG_ERR(" %*c\n",
698 : (int)msg_offset, '^');
699 0 : DBG_ERR("error '%s'\n", msg);
700 : }
701 : }
702 : } else {
703 1248 : sd = sec_desc_parse(talloc_tos(), cli, the_acl);
704 : }
705 :
706 1252 : if (sd == NULL) {
707 0 : return EXIT_PARSE_ERROR;
708 : }
709 1252 : if (test_args) {
710 0 : return EXIT_OK;
711 : }
712 1252 : return cacl_set_from_sd(cli, filename, sd, mode, numeric);
713 : }
714 :
715 : /*****************************************************
716 : set the inherit on a file
717 : *******************************************************/
718 18 : static int inherit(struct cli_state *cli, const char *filename,
719 : const char *type)
720 : {
721 : struct security_descriptor *old,*sd;
722 : uint32_t oldattr;
723 : size_t sd_size;
724 18 : int result = EXIT_OK;
725 :
726 18 : old = get_secdesc(cli, filename);
727 :
728 18 : if (!old) {
729 0 : return EXIT_FAILED;
730 : }
731 :
732 18 : oldattr = get_fileinfo(cli,filename);
733 :
734 18 : if (strcmp(type,"allow")==0) {
735 0 : if ((old->type & SEC_DESC_DACL_PROTECTED) ==
736 : SEC_DESC_DACL_PROTECTED) {
737 : uint32_t i;
738 : char *parentname,*temp;
739 : struct security_descriptor *parent;
740 0 : temp = talloc_strdup(talloc_tos(), filename);
741 :
742 0 : old->type=old->type & (~SEC_DESC_DACL_PROTECTED);
743 :
744 : /* look at parent and copy in all its inheritable ACL's. */
745 0 : string_replace(temp, '\\', '/');
746 0 : if (!parent_dirname(talloc_tos(),temp,&parentname,NULL)) {
747 0 : return EXIT_FAILED;
748 : }
749 0 : string_replace(parentname, '/', '\\');
750 0 : parent = get_secdesc(cli,parentname);
751 0 : if (parent == NULL) {
752 0 : return EXIT_FAILED;
753 : }
754 0 : for (i=0;i<parent->dacl->num_aces;i++) {
755 0 : struct security_ace *ace=&parent->dacl->aces[i];
756 : /* Add inherited flag to all aces */
757 0 : ace->flags=ace->flags|
758 : SEC_ACE_FLAG_INHERITED_ACE;
759 0 : if ((oldattr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
760 0 : if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) ==
761 : SEC_ACE_FLAG_CONTAINER_INHERIT) {
762 0 : add_ace(&old->dacl, ace);
763 : }
764 : } else {
765 0 : if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) ==
766 : SEC_ACE_FLAG_OBJECT_INHERIT) {
767 : /* clear flags for files */
768 0 : ace->flags=0;
769 0 : add_ace(&old->dacl, ace);
770 : }
771 : }
772 : }
773 : } else {
774 0 : printf("Already set to inheritable permissions.\n");
775 0 : return EXIT_FAILED;
776 : }
777 18 : } else if (strcmp(type,"remove")==0) {
778 0 : if ((old->type & SEC_DESC_DACL_PROTECTED) !=
779 : SEC_DESC_DACL_PROTECTED) {
780 0 : old->type=old->type | SEC_DESC_DACL_PROTECTED;
781 :
782 : /* remove all inherited ACL's. */
783 0 : if (old->dacl) {
784 : int i;
785 0 : struct security_acl *temp=old->dacl;
786 0 : old->dacl=make_sec_acl(talloc_tos(), 3, 0, NULL);
787 0 : for (i=temp->num_aces-1;i>=0;i--) {
788 0 : struct security_ace *ace=&temp->aces[i];
789 : /* Remove all ace with INHERITED flag set */
790 0 : if ((ace->flags & SEC_ACE_FLAG_INHERITED_ACE) !=
791 : SEC_ACE_FLAG_INHERITED_ACE) {
792 0 : add_ace(&old->dacl,ace);
793 : }
794 : }
795 : }
796 : } else {
797 0 : printf("Already set to no inheritable permissions.\n");
798 0 : return EXIT_FAILED;
799 : }
800 18 : } else if (strcmp(type,"copy")==0) {
801 18 : if ((old->type & SEC_DESC_DACL_PROTECTED) !=
802 : SEC_DESC_DACL_PROTECTED) {
803 18 : old->type=old->type | SEC_DESC_DACL_PROTECTED;
804 :
805 : /*
806 : * convert all inherited ACL's to non
807 : * inherited ACL's.
808 : */
809 18 : if (old->dacl) {
810 : uint32_t i;
811 108 : for (i=0;i<old->dacl->num_aces;i++) {
812 90 : struct security_ace *ace=&old->dacl->aces[i];
813 : /* Remove INHERITED FLAG from all aces */
814 90 : ace->flags=ace->flags&(~SEC_ACE_FLAG_INHERITED_ACE);
815 : }
816 : }
817 : } else {
818 0 : printf("Already set to no inheritable permissions.\n");
819 0 : return EXIT_FAILED;
820 : }
821 : }
822 :
823 : /* Denied ACE entries must come before allowed ones */
824 18 : sort_acl(old->dacl);
825 :
826 18 : sd = make_sec_desc(talloc_tos(),old->revision, old->type,
827 18 : old->owner_sid, old->group_sid,
828 : NULL, old->dacl, &sd_size);
829 :
830 18 : if (!set_secdesc(cli, filename, sd)) {
831 0 : result = EXIT_FAILED;
832 : }
833 :
834 18 : return result;
835 : }
836 :
837 : /*****************************************************
838 : Return a connection to a server.
839 : *******************************************************/
840 2114 : static struct cli_state *connect_one(struct cli_credentials *creds,
841 : const char *server, const char *share)
842 : {
843 2114 : struct cli_state *c = NULL;
844 : NTSTATUS nt_status;
845 2114 : uint32_t flags = 0;
846 :
847 2114 : nt_status = cli_full_connection_creds(&c, lp_netbios_name(), server,
848 : NULL, 0,
849 : share, "?????",
850 : creds,
851 : flags);
852 2114 : if (!NT_STATUS_IS_OK(nt_status)) {
853 0 : DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
854 0 : return NULL;
855 : }
856 :
857 2114 : return c;
858 : }
859 :
860 : /*
861 : * Process resulting combination of mask & fname ensuring
862 : * terminated with wildcard
863 : */
864 232 : static char *build_dirname(TALLOC_CTX *ctx,
865 : const char *mask, char *dir, char *fname)
866 : {
867 232 : char *mask2 = NULL;
868 232 : char *p = NULL;
869 :
870 232 : mask2 = talloc_strdup(ctx, mask);
871 232 : if (!mask2) {
872 0 : return NULL;
873 : }
874 232 : p = strrchr_m(mask2, DIRSEP_CHAR);
875 232 : if (p) {
876 232 : p[1] = 0;
877 : } else {
878 0 : mask2[0] = '\0';
879 : }
880 232 : mask2 = talloc_asprintf_append(mask2,
881 : "%s\\*",
882 : fname);
883 232 : return mask2;
884 : }
885 :
886 : /*
887 : * Returns a copy of the ACL flags in ace modified according
888 : * to some inheritance rules.
889 : * a) SEC_ACE_FLAG_INHERITED_ACE is propagated to children
890 : * b) SEC_ACE_FLAG_INHERIT_ONLY is set on container children for OI (only)
891 : * c) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
892 : * stripped from flags to be propagated to non-container children
893 : * d) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
894 : * stripped from flags to be propagated if the NP flag
895 : * SEC_ACE_FLAG_NO_PROPAGATE_INHERIT is present
896 : */
897 :
898 3566 : static uint8_t get_flags_to_propagate(bool is_container,
899 : struct security_ace *ace)
900 : {
901 3566 : uint8_t newflags = ace->flags;
902 : /* OBJECT inheritance */
903 3566 : bool acl_objinherit = (ace->flags &
904 : SEC_ACE_FLAG_OBJECT_INHERIT) == SEC_ACE_FLAG_OBJECT_INHERIT;
905 : /* CONTAINER inheritance */
906 3566 : bool acl_cntrinherit = (ace->flags &
907 : SEC_ACE_FLAG_CONTAINER_INHERIT) ==
908 : SEC_ACE_FLAG_CONTAINER_INHERIT;
909 : /* PROHIBIT inheritance */
910 3566 : bool prohibit_inheritance = ((ace->flags &
911 : SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) ==
912 : SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
913 :
914 : /* Assume we are not propagating the ACE */
915 :
916 3566 : newflags &= ~SEC_ACE_FLAG_INHERITED_ACE;
917 : /* all children need to have the SEC_ACE_FLAG_INHERITED_ACE set */
918 3566 : if (acl_cntrinherit || acl_objinherit) {
919 : /*
920 : * object inherit ( alone ) on a container needs
921 : * SEC_ACE_FLAG_INHERIT_ONLY
922 : */
923 3566 : if (is_container) {
924 1232 : if (acl_objinherit && !acl_cntrinherit) {
925 30 : newflags |= SEC_ACE_FLAG_INHERIT_ONLY;
926 : }
927 : /*
928 : * this is tricky, the only time we would not
929 : * propagate the ace for a container is if
930 : * prohibit_inheritance is set and object inheritance
931 : * alone is set
932 : */
933 1232 : if ((prohibit_inheritance
934 18 : && acl_objinherit
935 1250 : && !acl_cntrinherit) == false) {
936 1226 : newflags |= SEC_ACE_FLAG_INHERITED_ACE;
937 : }
938 : } else {
939 : /*
940 : * don't apply object/container inheritance flags to
941 : * non dirs
942 : */
943 2334 : newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
944 : | SEC_ACE_FLAG_CONTAINER_INHERIT
945 : | SEC_ACE_FLAG_INHERIT_ONLY);
946 : /*
947 : * only apply ace to file if object inherit
948 : */
949 2334 : if (acl_objinherit) {
950 2334 : newflags |= SEC_ACE_FLAG_INHERITED_ACE;
951 : }
952 : }
953 :
954 : /* if NP is specified strip NP and all OI/CI INHERIT flags */
955 3566 : if (prohibit_inheritance) {
956 30 : newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
957 : | SEC_ACE_FLAG_CONTAINER_INHERIT
958 : | SEC_ACE_FLAG_INHERIT_ONLY
959 : | SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
960 : }
961 : }
962 3566 : return newflags;
963 : }
964 :
965 : /*
966 : * This function builds a new acl for 'caclfile', first it removes any
967 : * existing inheritable ace(s) from the current acl of caclfile, secondly it
968 : * applies any inheritable acls of the parent of caclfile ( inheritable acls of
969 : * caclfile's parent are passed via acl_to_add member of cbstate )
970 : *
971 : */
972 580 : static NTSTATUS propagate_inherited_aces(char *caclfile,
973 : struct cacl_callback_state *cbstate)
974 : {
975 580 : TALLOC_CTX *aclctx = NULL;
976 : NTSTATUS status;
977 : int result;
978 : int fileattr;
979 580 : struct security_descriptor *old = NULL;
980 580 : bool is_container = false;
981 580 : struct security_acl *acl_to_add = cbstate->acl_to_add;
982 580 : struct security_acl *acl_to_remove = NULL;
983 : uint32_t i, j;
984 :
985 580 : aclctx = talloc_new(NULL);
986 580 : if (aclctx == NULL) {
987 0 : return NT_STATUS_NO_MEMORY;
988 : }
989 580 : old = get_secdesc_with_ctx(aclctx, cbstate->cli, caclfile);
990 :
991 580 : if (!old) {
992 0 : status = NT_STATUS_UNSUCCESSFUL;
993 0 : goto out;
994 : }
995 :
996 : /* inhibit propagation? */
997 580 : if ((old->type & SEC_DESC_DACL_PROTECTED) ==
998 : SEC_DESC_DACL_PROTECTED){
999 6 : status = NT_STATUS_OK;
1000 6 : goto out;
1001 : }
1002 :
1003 574 : fileattr = get_fileinfo(cbstate->cli, caclfile);
1004 574 : is_container = (fileattr & FILE_ATTRIBUTE_DIRECTORY);
1005 :
1006 : /* find acl(s) that are inherited */
1007 3122 : for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
1008 :
1009 2548 : if (old->dacl->aces[j].flags & SEC_ACE_FLAG_INHERITED_ACE) {
1010 882 : if (!add_ace_with_ctx(aclctx, &acl_to_remove,
1011 882 : &old->dacl->aces[j])) {
1012 0 : status = NT_STATUS_NO_MEMORY;
1013 0 : goto out;
1014 : }
1015 : }
1016 : }
1017 :
1018 : /* remove any acl(s) that are inherited */
1019 574 : if (acl_to_remove) {
1020 1456 : for (i = 0; i < acl_to_remove->num_aces; i++) {
1021 882 : struct security_ace ace = acl_to_remove->aces[i];
1022 3866 : for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
1023 :
1024 3866 : if (security_ace_equal(&ace,
1025 3866 : &old->dacl->aces[j])) {
1026 : uint32_t k;
1027 1670 : for (k = j; k < old->dacl->num_aces-1;
1028 788 : k++) {
1029 788 : old->dacl->aces[k] =
1030 788 : old->dacl->aces[k+1];
1031 : }
1032 882 : old->dacl->num_aces--;
1033 882 : break;
1034 : }
1035 : }
1036 : }
1037 : }
1038 : /* propagate any inheritable ace to be added */
1039 574 : if (acl_to_add) {
1040 4200 : for (i = 0; i < acl_to_add->num_aces; i++) {
1041 3626 : struct security_ace ace = acl_to_add->aces[i];
1042 3626 : bool is_objectinherit = (ace.flags &
1043 : SEC_ACE_FLAG_OBJECT_INHERIT) ==
1044 : SEC_ACE_FLAG_OBJECT_INHERIT;
1045 : bool is_inherited;
1046 : /* don't propagate flags to a file unless OI */
1047 3626 : if (!is_objectinherit && !is_container) {
1048 66 : continue;
1049 : }
1050 : /*
1051 : * adjust flags according to inheritance
1052 : * rules
1053 : */
1054 3566 : ace.flags = get_flags_to_propagate(is_container, &ace);
1055 3566 : is_inherited = (ace.flags &
1056 : SEC_ACE_FLAG_INHERITED_ACE) ==
1057 : SEC_ACE_FLAG_INHERITED_ACE;
1058 : /* don't propagate non inherited flags */
1059 3566 : if (!is_inherited) {
1060 6 : continue;
1061 : }
1062 3560 : if (!add_ace_with_ctx(aclctx, &old->dacl, &ace)) {
1063 0 : status = NT_STATUS_NO_MEMORY;
1064 0 : goto out;
1065 : }
1066 : }
1067 : }
1068 :
1069 574 : result = cacl_set_from_sd(cbstate->cli, caclfile,
1070 : old,
1071 574 : SMB_ACL_SET, cbstate->numeric);
1072 574 : if (result != EXIT_OK) {
1073 0 : status = NT_STATUS_UNSUCCESSFUL;
1074 0 : goto out;
1075 : }
1076 :
1077 574 : status = NT_STATUS_OK;
1078 580 : out:
1079 580 : TALLOC_FREE(aclctx);
1080 580 : return status;
1081 : }
1082 :
1083 : /*
1084 : * Returns true if 'ace' contains SEC_ACE_FLAG_OBJECT_INHERIT or
1085 : * SEC_ACE_FLAG_CONTAINER_INHERIT
1086 : */
1087 2906 : static bool is_inheritable_ace(struct security_ace *ace)
1088 : {
1089 2906 : uint8_t flags = ace->flags;
1090 2906 : if (flags & (SEC_ACE_FLAG_OBJECT_INHERIT
1091 : | SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1092 2510 : return true;
1093 : }
1094 396 : return false;
1095 : }
1096 :
1097 : /* This method does some basic sanity checking with respect to automatic
1098 : * inheritance. e.g. it checks if it is possible to do a set, it detects illegal
1099 : * attempts to set inherited permissions directly. Additionally this method
1100 : * does some basic initialisation for instance it parses the ACL passed on the
1101 : * command line.
1102 : */
1103 122 : static NTSTATUS prepare_inheritance_propagation(TALLOC_CTX *ctx, char *filename,
1104 : struct cacl_callback_state *cbstate)
1105 : {
1106 : NTSTATUS result;
1107 122 : char *the_acl = cbstate->the_acl;
1108 122 : struct cli_state *cli = cbstate->cli;
1109 122 : enum acl_mode mode = cbstate->mode;
1110 122 : struct security_descriptor *sd = NULL;
1111 122 : struct security_descriptor *old = NULL;
1112 : uint32_t j;
1113 122 : bool propagate = false;
1114 :
1115 122 : old = get_secdesc_with_ctx(ctx, cli, filename);
1116 122 : if (old == NULL) {
1117 0 : return NT_STATUS_NO_MEMORY;
1118 : }
1119 :
1120 : /* parse acl passed on the command line */
1121 122 : if (sddl) {
1122 0 : const char *msg = NULL;
1123 0 : size_t msg_offset = 0;
1124 0 : enum ace_condition_flags flags =
1125 : ACE_CONDITION_FLAG_ALLOW_DEVICE;
1126 :
1127 0 : cbstate->aclsd = sddl_decode_err_msg(ctx,
1128 : the_acl,
1129 0 : get_domain_sid(cli),
1130 : flags,
1131 : &msg,
1132 : &msg_offset);
1133 0 : if (cbstate->aclsd == NULL) {
1134 0 : DBG_ERR("could not decode '%s'\n", the_acl);
1135 0 : if (msg != NULL) {
1136 0 : DBG_ERR(" %*c\n",
1137 : (int)msg_offset, '^');
1138 0 : DBG_ERR("error '%s'\n", msg);
1139 : }
1140 : }
1141 : } else {
1142 122 : cbstate->aclsd = sec_desc_parse(ctx, cli, the_acl);
1143 : }
1144 :
1145 122 : if (!cbstate->aclsd) {
1146 0 : result = NT_STATUS_UNSUCCESSFUL;
1147 0 : goto out;
1148 : }
1149 :
1150 122 : sd = cbstate->aclsd;
1151 :
1152 : /* set operation if inheritance is enabled doesn't make sense */
1153 122 : if (mode == SMB_ACL_SET && ((old->type & SEC_DESC_DACL_PROTECTED) !=
1154 : SEC_DESC_DACL_PROTECTED)){
1155 6 : d_printf("Inheritance enabled at %s, can't apply set operation\n",filename);
1156 6 : result = NT_STATUS_UNSUCCESSFUL;
1157 6 : goto out;
1158 :
1159 : }
1160 :
1161 : /*
1162 : * search command line acl for any illegal SEC_ACE_FLAG_INHERITED_ACE
1163 : * flags that are set
1164 : */
1165 232 : for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1166 116 : struct security_ace *ace = &sd->dacl->aces[j];
1167 116 : if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE) {
1168 0 : d_printf("Illegal parameter %s\n", the_acl);
1169 0 : result = NT_STATUS_UNSUCCESSFUL;
1170 0 : goto out;
1171 : }
1172 116 : if (!propagate) {
1173 116 : if (is_inheritable_ace(ace)) {
1174 116 : propagate = true;
1175 : }
1176 : }
1177 : }
1178 :
1179 116 : result = NT_STATUS_OK;
1180 122 : out:
1181 122 : cbstate->acl_no_propagate = !propagate;
1182 122 : return result;
1183 : }
1184 :
1185 : /*
1186 : * This method builds inheritable ace(s) from filename (which should be
1187 : * a container) that need propagating to children in order to provide
1188 : * automatic inheritance. Those inheritable ace(s) are stored in
1189 : * acl_to_add member of cbstate for later processing
1190 : * (see propagate_inherited_aces)
1191 : */
1192 348 : static NTSTATUS get_inheritable_aces(TALLOC_CTX *ctx, char *filename,
1193 : struct cacl_callback_state *cbstate)
1194 : {
1195 : NTSTATUS result;
1196 348 : struct cli_state *cli = NULL;
1197 348 : struct security_descriptor *sd = NULL;
1198 348 : struct security_acl *acl_to_add = NULL;
1199 : uint32_t j;
1200 :
1201 348 : cli = cbstate->cli;
1202 348 : sd = get_secdesc_with_ctx(ctx, cli, filename);
1203 :
1204 348 : if (sd == NULL) {
1205 0 : return NT_STATUS_NO_MEMORY;
1206 : }
1207 :
1208 : /*
1209 : * Check if any inheritance related flags are used, if not then
1210 : * nothing to do. At the same time populate acls for inheritance
1211 : * related ace(s) that need to be added to or deleted from children as
1212 : * a result of inheritance propagation.
1213 : */
1214 :
1215 3138 : for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
1216 2790 : struct security_ace *ace = &sd->dacl->aces[j];
1217 2790 : if (is_inheritable_ace(ace)) {
1218 2394 : bool added = add_ace_with_ctx(ctx, &acl_to_add, ace);
1219 2394 : if (!added) {
1220 0 : result = NT_STATUS_NO_MEMORY;
1221 0 : goto out;
1222 : }
1223 : }
1224 : }
1225 348 : cbstate->acl_to_add = acl_to_add;
1226 348 : result = NT_STATUS_OK;
1227 348 : out:
1228 348 : return result;
1229 : }
1230 :
1231 : /*
1232 : * Callback handler to handle child elements processed by cli_list, we attempt
1233 : * to propagate inheritable ace(s) to each child via the function
1234 : * propagate_inherited_aces. Children that are themselves directories are passed
1235 : * to cli_list again ( to descend the directory structure )
1236 : */
1237 1276 : static NTSTATUS cacl_set_cb(struct file_info *f,
1238 : const char *mask, void *state)
1239 : {
1240 1276 : struct cacl_callback_state *cbstate =
1241 : (struct cacl_callback_state *)state;
1242 1276 : struct cli_state *cli = NULL;
1243 1276 : struct cli_credentials *creds = NULL;
1244 :
1245 1276 : TALLOC_CTX *dirctx = NULL;
1246 : NTSTATUS status;
1247 1276 : struct cli_state *targetcli = NULL;
1248 :
1249 1276 : char *dir = NULL;
1250 1276 : char *dir_end = NULL;
1251 1276 : char *mask2 = NULL;
1252 1276 : char *targetpath = NULL;
1253 1276 : char *caclfile = NULL;
1254 :
1255 1276 : dirctx = talloc_new(NULL);
1256 1276 : if (!dirctx) {
1257 0 : status = NT_STATUS_NO_MEMORY;
1258 0 : goto out;
1259 : }
1260 :
1261 1276 : cli = cbstate->cli;
1262 1276 : creds = cbstate->creds;
1263 :
1264 : /* Work out the directory. */
1265 1276 : dir = talloc_strdup(dirctx, mask);
1266 1276 : if (!dir) {
1267 0 : status = NT_STATUS_NO_MEMORY;
1268 0 : goto out;
1269 : }
1270 :
1271 1276 : dir_end = strrchr(dir, DIRSEP_CHAR);
1272 1276 : if (dir_end != NULL) {
1273 1276 : *dir_end = '\0';
1274 : }
1275 :
1276 1276 : if (!f->name || !f->name[0]) {
1277 0 : d_printf("Empty dir name returned. Possible server misconfiguration.\n");
1278 0 : status = NT_STATUS_UNSUCCESSFUL;
1279 0 : goto out;
1280 : }
1281 :
1282 1276 : if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
1283 : struct cacl_callback_state dir_cbstate;
1284 928 : uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
1285 : | FILE_ATTRIBUTE_SYSTEM
1286 : | FILE_ATTRIBUTE_HIDDEN;
1287 928 : dir_end = NULL;
1288 :
1289 : /* ignore special '.' & '..' */
1290 928 : if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
1291 696 : status = NT_STATUS_OK;
1292 696 : goto out;
1293 : }
1294 :
1295 232 : mask2 = build_dirname(dirctx, mask, dir, f->name);
1296 232 : if (mask2 == NULL) {
1297 0 : status = NT_STATUS_NO_MEMORY;
1298 0 : goto out;
1299 : }
1300 :
1301 : /* check for dfs */
1302 232 : status = cli_resolve_path(dirctx, "", creds, cli,
1303 : mask2, &targetcli, &targetpath);
1304 232 : if (!NT_STATUS_IS_OK(status)) {
1305 0 : goto out;
1306 : }
1307 :
1308 : /*
1309 : * prepare path to caclfile, remove any existing wildcard
1310 : * chars and convert path separators.
1311 : */
1312 :
1313 232 : caclfile = talloc_strdup(dirctx, targetpath);
1314 232 : if (!caclfile) {
1315 0 : status = NT_STATUS_NO_MEMORY;
1316 0 : goto out;
1317 : }
1318 232 : dir_end = strrchr(caclfile, '*');
1319 232 : if (dir_end != NULL) {
1320 232 : *dir_end = '\0';
1321 : }
1322 :
1323 232 : string_replace(caclfile, '/', '\\');
1324 : /*
1325 : * make directory specific copy of cbstate here
1326 : * (for this directory level) to be available as
1327 : * the parent cbstate for the children of this directory.
1328 : * Note: cbstate is overwritten for the current file being
1329 : * processed.
1330 : */
1331 232 : dir_cbstate = *cbstate;
1332 232 : dir_cbstate.cli = targetcli;
1333 :
1334 : /*
1335 : * propagate any inherited ace from our parent
1336 : */
1337 232 : status = propagate_inherited_aces(caclfile, &dir_cbstate);
1338 232 : if (!NT_STATUS_IS_OK(status)) {
1339 0 : goto out;
1340 : }
1341 :
1342 : /*
1343 : * get inheritable ace(s) for this dir/container
1344 : * that will be propagated to its children
1345 : */
1346 232 : status = get_inheritable_aces(dirctx, caclfile,
1347 : &dir_cbstate);
1348 232 : if (!NT_STATUS_IS_OK(status)) {
1349 0 : goto out;
1350 : }
1351 :
1352 : /*
1353 : * ensure cacl_set_cb gets called for children
1354 : * of this directory (targetpath)
1355 : */
1356 232 : status = cli_list(targetcli, targetpath,
1357 : attribute, cacl_set_cb,
1358 : (void *)&dir_cbstate);
1359 :
1360 232 : if (!NT_STATUS_IS_OK(status)) {
1361 0 : goto out;
1362 : }
1363 :
1364 : } else {
1365 : /*
1366 : * build full path to caclfile and replace '/' with '\' so
1367 : * other utility functions can deal with it
1368 : */
1369 :
1370 348 : targetpath = talloc_asprintf(dirctx, "%s/%s", dir, f->name);
1371 348 : if (!targetpath) {
1372 0 : status = NT_STATUS_NO_MEMORY;
1373 0 : goto out;
1374 : }
1375 348 : string_replace(targetpath, '/', '\\');
1376 :
1377 : /* attempt to propagate any inherited ace to file caclfile */
1378 348 : status = propagate_inherited_aces(targetpath, cbstate);
1379 :
1380 348 : if (!NT_STATUS_IS_OK(status)) {
1381 0 : goto out;
1382 : }
1383 : }
1384 580 : status = NT_STATUS_OK;
1385 1276 : out:
1386 1276 : if (!NT_STATUS_IS_OK(status)) {
1387 0 : d_printf("error %s: processing %s\n",
1388 : nt_errstr(status),
1389 : targetpath);
1390 : }
1391 1276 : TALLOC_FREE(dirctx);
1392 1276 : return status;
1393 : }
1394 :
1395 :
1396 : /*
1397 : * Wrapper around cl_list to descend the directory tree pointed to by 'filename',
1398 : * helper callback function 'cacl_set_cb' handles the child elements processed
1399 : * by cli_list.
1400 : */
1401 122 : static int inheritance_cacl_set(char *filename,
1402 : struct cacl_callback_state *cbstate)
1403 : {
1404 : int result;
1405 : NTSTATUS ntstatus;
1406 : int fileattr;
1407 122 : char *mask = NULL;
1408 122 : struct cli_state *cli = cbstate->cli;
1409 122 : TALLOC_CTX *ctx = NULL;
1410 122 : bool isdirectory = false;
1411 122 : uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM
1412 : | FILE_ATTRIBUTE_HIDDEN;
1413 122 : ctx = talloc_init("inherit_set");
1414 122 : if (ctx == NULL) {
1415 0 : d_printf("out of memory\n");
1416 0 : result = EXIT_FAILED;
1417 0 : goto out;
1418 : }
1419 :
1420 : /* ensure we have a filename that starts with '\' */
1421 122 : if (!filename || *filename != DIRSEP_CHAR) {
1422 : /* illegal or no filename */
1423 0 : result = EXIT_FAILED;
1424 0 : d_printf("illegal or missing name '%s'\n", filename);
1425 0 : goto out;
1426 : }
1427 :
1428 :
1429 122 : fileattr = get_fileinfo(cli, filename);
1430 122 : isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1431 : == FILE_ATTRIBUTE_DIRECTORY;
1432 :
1433 : /*
1434 : * if we've got as far as here then we have already evaluated
1435 : * the args.
1436 : */
1437 122 : if (test_args) {
1438 0 : result = EXIT_OK;
1439 0 : goto out;
1440 : }
1441 :
1442 122 : mask = NULL;
1443 : /* make sure we have a trailing '\*' for directory */
1444 122 : if (!isdirectory) {
1445 0 : mask = talloc_strdup(ctx, filename);
1446 122 : } else if (strlen(filename) > 1) {
1447 : /*
1448 : * if the passed file name doesn't have a trailing '\'
1449 : * append it.
1450 : */
1451 122 : char *name_end = strrchr(filename, DIRSEP_CHAR);
1452 122 : if (name_end != filename + strlen(filename) + 1) {
1453 122 : mask = talloc_asprintf(ctx, "%s\\*", filename);
1454 : } else {
1455 0 : mask = talloc_strdup(ctx, filename);
1456 : }
1457 : } else {
1458 : /* filename is a single '\', just append '*' */
1459 0 : mask = talloc_asprintf_append(mask, "%s*", filename);
1460 : }
1461 :
1462 122 : if (!mask) {
1463 0 : result = EXIT_FAILED;
1464 0 : goto out;
1465 : }
1466 :
1467 : /*
1468 : * prepare for automatic propagation of the acl passed on the
1469 : * cmdline.
1470 : */
1471 :
1472 122 : ntstatus = prepare_inheritance_propagation(ctx, filename,
1473 : cbstate);
1474 122 : if (!NT_STATUS_IS_OK(ntstatus)) {
1475 6 : d_printf("error: %s processing %s\n",
1476 : nt_errstr(ntstatus), filename);
1477 6 : result = EXIT_FAILED;
1478 6 : goto out;
1479 : }
1480 :
1481 116 : result = cacl_set_from_sd(cli, filename, cbstate->aclsd,
1482 116 : cbstate->mode, cbstate->numeric);
1483 :
1484 : /*
1485 : * strictly speaking it could be considered an error if a file was
1486 : * specified with '--propagate-inheritance'. However we really want
1487 : * to eventually get rid of '--propagate-inheritance' so we will be
1488 : * more forgiving here and instead just exit early.
1489 : */
1490 116 : if (!isdirectory || (result != EXIT_OK)) {
1491 0 : goto out;
1492 : }
1493 :
1494 : /* check if there is actually any need to propagate */
1495 116 : if (cbstate->acl_no_propagate) {
1496 0 : goto out;
1497 : }
1498 : /* get inheritable attributes this parent container (e.g. filename) */
1499 116 : ntstatus = get_inheritable_aces(ctx, filename, cbstate);
1500 116 : if (NT_STATUS_IS_OK(ntstatus)) {
1501 : /* process children */
1502 116 : ntstatus = cli_list(cli, mask, attribute,
1503 : cacl_set_cb,
1504 : (void *)cbstate);
1505 : }
1506 :
1507 116 : if (!NT_STATUS_IS_OK(ntstatus)) {
1508 0 : d_printf("error: %s processing %s\n",
1509 : nt_errstr(ntstatus), filename);
1510 0 : result = EXIT_FAILED;
1511 0 : goto out;
1512 : }
1513 :
1514 116 : out:
1515 122 : TALLOC_FREE(ctx);
1516 122 : return result;
1517 : }
1518 :
1519 : struct diritem {
1520 : struct diritem *prev, *next;
1521 : /*
1522 : * dirname and targetpath below are sanitized,
1523 : * e.g.
1524 : * + start and end with '\'
1525 : * + have no trailing '*'
1526 : * + all '/' have been converted to '\'
1527 : */
1528 : char *dirname;
1529 : char *targetpath;
1530 : struct cli_state *targetcli;
1531 : };
1532 :
1533 : struct save_restore_stats
1534 : {
1535 : int success;
1536 : int failure;
1537 : };
1538 :
1539 : struct dump_context {
1540 : struct diritem *list;
1541 : struct cli_credentials *creds;
1542 : struct cli_state *cli;
1543 : struct save_restore_stats *stats;
1544 : int save_fd;
1545 : struct diritem *dir;
1546 : NTSTATUS status;
1547 : };
1548 :
1549 112 : static int write_dacl(struct dump_context *ctx,
1550 : struct cli_state *cli,
1551 : const char *filename,
1552 : const char *origfname)
1553 : {
1554 112 : struct security_descriptor *sd = NULL;
1555 112 : char *str = NULL;
1556 112 : const char *output_fmt = "%s\r\n%s\r\n";
1557 112 : const char *tmp = NULL;
1558 112 : char *out_str = NULL;
1559 112 : uint8_t *dest = NULL;
1560 : ssize_t s_len;
1561 : size_t d_len;
1562 : bool ok;
1563 : int result;
1564 112 : TALLOC_CTX *frame = talloc_stackframe();
1565 :
1566 112 : if (test_args) {
1567 0 : return EXIT_OK;
1568 : }
1569 :
1570 112 : if (ctx->save_fd < 0) {
1571 0 : DBG_ERR("error processing %s no file descriptor\n", filename);
1572 0 : result = EXIT_FAILED;
1573 0 : goto out;
1574 : }
1575 :
1576 112 : sd = get_secdesc(cli, filename);
1577 112 : if (sd == NULL) {
1578 0 : result = EXIT_FAILED;
1579 0 : goto out;
1580 : }
1581 :
1582 112 : sd->owner_sid = NULL;
1583 112 : sd->group_sid = NULL;
1584 :
1585 112 : str = sddl_encode(frame, sd, get_domain_sid(cli));
1586 112 : if (str == NULL) {
1587 0 : DBG_ERR("error processing %s couldn't encode DACL\n", filename);
1588 0 : result = EXIT_FAILED;
1589 0 : goto out;
1590 : }
1591 : /*
1592 : * format of icacls save file is
1593 : * a line containing the path of the file/dir
1594 : * followed by a line containing the sddl format
1595 : * of the dacl.
1596 : * The format of the strings are null terminated
1597 : * 16-bit Unicode. Each line is terminated by "\r\n"
1598 : */
1599 :
1600 112 : tmp = origfname;
1601 : /* skip leading '\' */
1602 112 : if (tmp[0] == '\\') {
1603 112 : tmp++;
1604 : }
1605 112 : out_str = talloc_asprintf(frame, output_fmt, tmp, str);
1606 :
1607 112 : if (out_str == NULL) {
1608 0 : result = EXIT_FAILED;
1609 0 : goto out;
1610 : }
1611 :
1612 112 : s_len = strlen(out_str);
1613 :
1614 112 : ok = convert_string_talloc(out_str,
1615 : CH_UNIX,
1616 : CH_UTF16,
1617 : out_str,
1618 : s_len, (void **)(void *)&dest, &d_len);
1619 112 : if (!ok) {
1620 0 : DBG_ERR("error processing %s out of memory\n", tmp);
1621 0 : result = EXIT_FAILED;
1622 0 : goto out;
1623 : }
1624 :
1625 112 : if (write(ctx->save_fd, dest, d_len) != d_len) {
1626 0 : DBG_ERR("error processing %s failed to write to file.\n", tmp);
1627 0 : result = EXIT_FAILED;
1628 0 : goto out;
1629 : }
1630 112 : fsync(ctx->save_fd);
1631 :
1632 112 : result = EXIT_OK;
1633 112 : ctx->stats->success += 1;
1634 112 : fprintf(stdout, "Successfully processed file: %s\n", tmp);
1635 112 : out:
1636 112 : TALLOC_FREE(frame);
1637 112 : if (result != EXIT_OK) {
1638 0 : ctx->stats->failure += 1;
1639 : }
1640 112 : return result;
1641 : }
1642 :
1643 : /*
1644 : * Sanitize directory name.
1645 : * Given a directory name 'dir' ensure it;
1646 : * o starts with '\'
1647 : * o ends with '\'
1648 : * o doesn't end with trailing '*'
1649 : * o ensure all '/' are converted to '\'
1650 : */
1651 :
1652 320 : static char *sanitize_dirname(TALLOC_CTX *ctx,
1653 : const char *dir)
1654 : {
1655 320 : char *mask = NULL;
1656 320 : char *name_end = NULL;
1657 :
1658 320 : mask = talloc_strdup(ctx, dir);
1659 320 : name_end = strrchr(mask, '*');
1660 320 : if (name_end) {
1661 176 : *name_end = '\0';
1662 : }
1663 :
1664 320 : name_end = strrchr(mask, DIRSEP_CHAR);
1665 :
1666 320 : if (strlen(mask) > 0 && name_end != mask + (strlen(mask) - 1)) {
1667 112 : mask = talloc_asprintf(ctx, "%s\\", mask);
1668 : }
1669 :
1670 320 : string_replace(mask, '/', '\\');
1671 320 : return mask;
1672 : }
1673 :
1674 : /*
1675 : * Process each entry (child) of a directory.
1676 : * Each entry, regardless of whether it is itself a file or directory
1677 : * has it's dacl written to the restore/save file.
1678 : * Each directory is saved to context->list (for further processing)
1679 : * write_dacl will update the stats (success/fail)
1680 : */
1681 176 : static NTSTATUS cacl_dump_dacl_cb(struct file_info *f,
1682 : const char *mask, void *state)
1683 : {
1684 176 : struct dump_context *ctx = talloc_get_type_abort(state,
1685 : struct dump_context);
1686 :
1687 : NTSTATUS status;
1688 :
1689 176 : char *mask2 = NULL;
1690 176 : char *targetpath = NULL;
1691 176 : char *unresolved = NULL;
1692 :
1693 : /*
1694 : * if we have already encountered an error
1695 : * bail out
1696 : */
1697 176 : if (!NT_STATUS_IS_OK(ctx->status)) {
1698 0 : return ctx->status;
1699 : }
1700 :
1701 176 : if (!f->name || !f->name[0]) {
1702 0 : DBG_ERR("Empty dir name returned. Possible server "
1703 : "misconfiguration.\n");
1704 0 : status = NT_STATUS_UNSUCCESSFUL;
1705 0 : goto out;
1706 : }
1707 :
1708 176 : mask2 = sanitize_dirname(ctx, mask);
1709 176 : if (!mask2) {
1710 0 : status = NT_STATUS_NO_MEMORY;
1711 0 : goto out;
1712 : }
1713 176 : if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
1714 128 : struct diritem *item = NULL;
1715 :
1716 : /* ignore special '.' & '..' */
1717 128 : if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
1718 96 : status = NT_STATUS_OK;
1719 96 : goto out;
1720 : }
1721 :
1722 : /* Work out the directory. */
1723 32 : unresolved = sanitize_dirname(ctx, ctx->dir->dirname);
1724 32 : if (!unresolved) {
1725 0 : status = NT_STATUS_NO_MEMORY;
1726 0 : goto out;
1727 : }
1728 :
1729 32 : unresolved = talloc_asprintf(ctx, "%s%s", unresolved, f->name);
1730 :
1731 32 : if (unresolved == NULL) {
1732 0 : status = NT_STATUS_NO_MEMORY;
1733 0 : goto out;
1734 : }
1735 :
1736 32 : item = talloc_zero(ctx, struct diritem);
1737 32 : if (item == NULL) {
1738 0 : status = NT_STATUS_NO_MEMORY;
1739 0 : goto out;
1740 : }
1741 :
1742 32 : item->dirname = unresolved;
1743 :
1744 32 : mask2 = talloc_asprintf(ctx, "%s%s", mask2, f->name);
1745 32 : if (!mask2) {
1746 0 : status = NT_STATUS_NO_MEMORY;
1747 0 : goto out;
1748 : }
1749 :
1750 32 : status = cli_resolve_path(ctx, "", ctx->creds, ctx->cli,
1751 : mask2, &item->targetcli, &targetpath);
1752 :
1753 32 : if (!NT_STATUS_IS_OK(status)) {
1754 0 : DBG_ERR("error failed to resolve: %s\n",
1755 : nt_errstr(status));
1756 0 : goto out;
1757 : }
1758 :
1759 32 : item->targetpath = sanitize_dirname(ctx, targetpath);
1760 32 : if (!item->targetpath) {
1761 0 : status = NT_STATUS_NO_MEMORY;
1762 0 : goto out;
1763 : }
1764 :
1765 32 : if (write_dacl(ctx,
1766 : item->targetcli,
1767 32 : item->targetpath, unresolved) != EXIT_OK) {
1768 0 : status = NT_STATUS_UNSUCCESSFUL;
1769 : /*
1770 : * cli_list happily ignores error encountered
1771 : * when processing the callback so we need
1772 : * to save any error status encountered while
1773 : * processing directories (so we can stop recursing
1774 : * those as soon as possible).
1775 : * Changing the current behaviour of the callback
1776 : * handling by cli_list would be I think be too
1777 : * risky.
1778 : */
1779 0 : ctx->status = status;
1780 0 : goto out;
1781 : }
1782 :
1783 32 : DLIST_ADD_END(ctx->list, item);
1784 :
1785 : } else {
1786 48 : unresolved = sanitize_dirname(ctx, ctx->dir->dirname);
1787 48 : if (!unresolved) {
1788 0 : status = NT_STATUS_NO_MEMORY;
1789 0 : goto out;
1790 : }
1791 :
1792 48 : unresolved = talloc_asprintf(ctx, "%s%s", unresolved, f->name);
1793 :
1794 48 : if (!unresolved) {
1795 0 : status = NT_STATUS_NO_MEMORY;
1796 0 : goto out;
1797 : }
1798 : /*
1799 : * build full path to the file and replace '/' with '\' so
1800 : * other utility functions can deal with it
1801 : */
1802 :
1803 48 : targetpath = talloc_asprintf(ctx, "%s%s", mask2, f->name);
1804 :
1805 48 : if (!targetpath) {
1806 0 : status = NT_STATUS_NO_MEMORY;
1807 0 : goto out;
1808 : }
1809 :
1810 48 : if (write_dacl(ctx,
1811 48 : ctx->dir->targetcli,
1812 : targetpath, unresolved) != EXIT_OK) {
1813 0 : status = NT_STATUS_UNSUCCESSFUL;
1814 : /*
1815 : * cli_list happily ignores error encountered
1816 : * when processing the callback so we need
1817 : * to save any error status encountered while
1818 : * processing directories (so we can stop recursing
1819 : * those as soon as possible).
1820 : * Changing the current behaviour of the callback
1821 : * handling by cli_list would be I think be too
1822 : * risky.
1823 : */
1824 0 : ctx->status = status;
1825 0 : goto out;
1826 : }
1827 : }
1828 80 : status = NT_STATUS_OK;
1829 176 : out:
1830 176 : if (!NT_STATUS_IS_OK(status)) {
1831 0 : DBG_ERR("error %s: processing %s\n",
1832 : nt_errstr(status), targetpath);
1833 : }
1834 176 : return status;
1835 : }
1836 :
1837 : /*
1838 : * dump_ctx contains a list of directories to be processed
1839 : * + each directory 'dir' is scanned by cli_list, the cli_list
1840 : * callback 'cacl_dump_dacl_cb' writes out the dacl of each
1841 : * child of 'dir' (regardless of whether it is a dir or file)
1842 : * to the restore/save file. Additionally any directories encountered
1843 : * are returned in the passed in dump_ctx->list member
1844 : * + the directory list returned from cli_list is passed and processed
1845 : * by recursively calling dump_dacl_dirtree
1846 : *
1847 : */
1848 64 : static int dump_dacl_dirtree(struct dump_context *dump_ctx)
1849 : {
1850 64 : struct diritem *item = NULL;
1851 64 : struct dump_context *new_dump_ctx = NULL;
1852 : int result;
1853 112 : for (item = dump_ctx->list; item; item = item->next) {
1854 48 : uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
1855 : | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
1856 : NTSTATUS status;
1857 48 : char *mask = NULL;
1858 :
1859 48 : new_dump_ctx = talloc_zero(dump_ctx, struct dump_context);
1860 :
1861 48 : if (new_dump_ctx == NULL) {
1862 0 : DBG_ERR("out of memory\n");
1863 0 : result = EXIT_FAILED;
1864 0 : goto out;
1865 : }
1866 :
1867 48 : if (item->targetcli == NULL) {
1868 16 : status = cli_resolve_path(new_dump_ctx,
1869 : "",
1870 : dump_ctx->creds,
1871 : dump_ctx->cli,
1872 16 : item->dirname,
1873 : &item->targetcli,
1874 : &item->targetpath);
1875 16 : if (!NT_STATUS_IS_OK(status)) {
1876 0 : DBG_ERR("failed to resolve path %s "
1877 : "error: %s\n",
1878 : item->dirname, nt_errstr(status));
1879 0 : result = EXIT_FAILED;
1880 0 : goto out;
1881 : }
1882 : }
1883 48 : new_dump_ctx->creds = dump_ctx->creds;
1884 48 : new_dump_ctx->save_fd = dump_ctx->save_fd;
1885 48 : new_dump_ctx->stats = dump_ctx->stats;
1886 48 : new_dump_ctx->dir = item;
1887 48 : new_dump_ctx->cli = item->targetcli;
1888 :
1889 48 : mask = talloc_asprintf(new_dump_ctx, "%s*",
1890 48 : new_dump_ctx->dir->targetpath);
1891 48 : status = cli_list(new_dump_ctx->dir->targetcli,
1892 : mask,
1893 : attribute, cacl_dump_dacl_cb, new_dump_ctx);
1894 :
1895 48 : if (!NT_STATUS_IS_OK(status) ||
1896 48 : !NT_STATUS_IS_OK(new_dump_ctx->status)) {
1897 : NTSTATUS tmpstatus;
1898 0 : if (!NT_STATUS_IS_OK(status)) {
1899 : /*
1900 : * cli_list failed for some reason
1901 : * so we need to update the failure stat
1902 : */
1903 0 : new_dump_ctx->stats->failure += 1;
1904 0 : tmpstatus = status;
1905 : } else {
1906 : /* cacl_dump_dacl_cb should have updated stat */
1907 0 : tmpstatus = new_dump_ctx->status;
1908 : }
1909 0 : DBG_ERR("error %s: processing %s\n",
1910 : nt_errstr(tmpstatus), item->dirname);
1911 0 : result = EXIT_FAILED;
1912 0 : goto out;
1913 : }
1914 48 : result = dump_dacl_dirtree(new_dump_ctx);
1915 48 : if (result != EXIT_OK) {
1916 0 : goto out;
1917 : }
1918 : }
1919 :
1920 64 : result = EXIT_OK;
1921 64 : out:
1922 64 : TALLOC_FREE(new_dump_ctx);
1923 64 : return result;
1924 : }
1925 :
1926 32 : static int cacl_dump_dacl(struct cli_state *cli,
1927 : struct cli_credentials *creds,
1928 : char *filename)
1929 : {
1930 : int fileattr;
1931 32 : char *mask = NULL;
1932 32 : TALLOC_CTX *ctx = NULL;
1933 32 : bool isdirectory = false;
1934 : int result;
1935 32 : struct dump_context *dump_ctx = NULL;
1936 32 : struct save_restore_stats stats = {0};
1937 32 : struct diritem *item = NULL;
1938 32 : struct cli_state *targetcli = NULL;
1939 32 : char *targetpath = NULL;
1940 : NTSTATUS status;
1941 :
1942 32 : ctx = talloc_init("cacl_dump");
1943 32 : if (ctx == NULL) {
1944 0 : DBG_ERR("out of memory\n");
1945 0 : result = EXIT_FAILED;
1946 0 : goto out;
1947 : }
1948 :
1949 32 : dump_ctx = talloc_zero(ctx, struct dump_context);
1950 32 : if (dump_ctx == NULL) {
1951 0 : DBG_ERR("out of memory\n");
1952 0 : result = EXIT_FAILED;
1953 0 : goto out;
1954 : }
1955 :
1956 32 : dump_ctx->save_fd = open(save_file,
1957 : O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
1958 :
1959 32 : if (dump_ctx->save_fd < 0) {
1960 0 : result = EXIT_FAILED;
1961 0 : goto out;
1962 : }
1963 :
1964 32 : dump_ctx->creds = creds;
1965 32 : dump_ctx->cli = cli;
1966 32 : dump_ctx->stats = &stats;
1967 :
1968 : /* ensure we have a filename that starts with '\' */
1969 32 : if (!filename || *filename != DIRSEP_CHAR) {
1970 : /* illegal or no filename */
1971 0 : result = EXIT_FAILED;
1972 0 : DBG_ERR("illegal or missing name '%s'\n", filename);
1973 0 : goto out;
1974 : }
1975 :
1976 32 : status = cli_resolve_path(dump_ctx, "",
1977 : dump_ctx->creds,
1978 : dump_ctx->cli,
1979 : filename, &targetcli, &targetpath);
1980 32 : if (!NT_STATUS_IS_OK(status)) {
1981 0 : DBG_ERR("failed resolve %s\n", filename);
1982 0 : result = EXIT_FAILED;
1983 0 : goto out;
1984 : }
1985 :
1986 32 : fileattr = get_fileinfo(targetcli, targetpath);
1987 32 : isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1988 : == FILE_ATTRIBUTE_DIRECTORY;
1989 :
1990 : /*
1991 : * if we've got as far as here then we have already evaluated
1992 : * the args.
1993 : */
1994 32 : if (test_args) {
1995 0 : result = EXIT_OK;
1996 0 : goto out;
1997 : }
1998 :
1999 32 : mask = NULL;
2000 : /* make sure we have a trailing '\*' for directory */
2001 32 : if (!isdirectory) {
2002 0 : mask = talloc_strdup(ctx, filename);
2003 32 : } else if (strlen(filename) > 1) {
2004 32 : mask = sanitize_dirname(ctx, filename);
2005 : } else {
2006 : /* filename is a single '\' */
2007 0 : mask = talloc_strdup(ctx, filename);
2008 : }
2009 32 : if (!mask) {
2010 0 : result = EXIT_FAILED;
2011 0 : goto out;
2012 : }
2013 :
2014 32 : write_dacl(dump_ctx, targetcli, targetpath, filename);
2015 32 : if (isdirectory && recurse) {
2016 16 : item = talloc_zero(dump_ctx, struct diritem);
2017 16 : if (!item) {
2018 0 : result = EXIT_FAILED;
2019 0 : goto out;
2020 : }
2021 16 : item->dirname = mask;
2022 16 : DLIST_ADD_END(dump_ctx->list, item);
2023 16 : dump_dacl_dirtree(dump_ctx);
2024 : }
2025 :
2026 32 : fprintf(stdout, "Successfully processed %d files: "
2027 : "Failed processing %d files\n",
2028 32 : dump_ctx->stats->success, dump_ctx->stats->failure);
2029 32 : result = EXIT_OK;
2030 32 : out:
2031 32 : if (dump_ctx && dump_ctx->save_fd > 0) {
2032 32 : close(dump_ctx->save_fd);
2033 : }
2034 32 : TALLOC_FREE(ctx);
2035 32 : return result;
2036 : }
2037 :
2038 : struct restore_dacl {
2039 : const char *path;
2040 : struct security_descriptor *sd;
2041 : };
2042 :
2043 : /*
2044 : * Restore dacls from 'savefile' produced by
2045 : * 'icacls name /save' or 'smbcacls --save'
2046 : */
2047 8 : static int cacl_restore(struct cli_state *cli,
2048 : struct cli_credentials *creds,
2049 : bool numeric, const char *restorefile)
2050 : {
2051 : int restore_fd;
2052 : int result;
2053 8 : struct save_restore_stats stats = { 0 };
2054 :
2055 8 : char **lines = NULL;
2056 8 : char *content = NULL;
2057 8 : char *convert_content = NULL;
2058 : size_t content_size;
2059 8 : struct restore_dacl *entries = NULL;
2060 8 : int numlines, i = 0;
2061 : bool ok;
2062 8 : struct dom_sid *sid = NULL;
2063 :
2064 8 : if (restorefile == NULL) {
2065 0 : DBG_ERR("No restore file specified\n");
2066 0 : result = EXIT_FAILED;
2067 0 : goto out;
2068 : }
2069 :
2070 8 : if (test_args) {
2071 0 : result = EXIT_OK;
2072 0 : goto out;
2073 : }
2074 :
2075 8 : restore_fd = open(restorefile, O_RDONLY, S_IRUSR | S_IWUSR);
2076 8 : if (restore_fd < 0) {
2077 0 : DBG_ERR("Failed to open %s.\n", restorefile);
2078 0 : result = EXIT_FAILED;
2079 0 : goto out;
2080 : }
2081 :
2082 8 : content = fd_load(restore_fd, &content_size, 0, talloc_tos());
2083 :
2084 8 : close(restore_fd);
2085 :
2086 8 : if (content == NULL) {
2087 0 : DBG_ERR("Failed to load content from %s.\n", restorefile);
2088 0 : result = EXIT_FAILED;
2089 0 : goto out;
2090 : }
2091 :
2092 8 : ok = convert_string_talloc(talloc_tos(),
2093 : CH_UTF16,
2094 : CH_UNIX,
2095 : content,
2096 : utf16_len_n(content, content_size),
2097 : (void **)(void *)&convert_content,
2098 : &content_size);
2099 :
2100 8 : TALLOC_FREE(content);
2101 :
2102 8 : if (!ok) {
2103 0 : DBG_ERR("Failed to convert content from %s "
2104 : "to CH_UNIX.\n", restorefile);
2105 0 : result = EXIT_FAILED;
2106 0 : goto out;
2107 : }
2108 :
2109 8 : lines = file_lines_parse(convert_content,
2110 : content_size, &numlines, talloc_tos());
2111 :
2112 8 : if (lines == NULL) {
2113 0 : DBG_ERR("Failed to parse lines from content of %s.",
2114 : restorefile);
2115 0 : result = EXIT_FAILED;
2116 0 : goto out;
2117 : }
2118 :
2119 8 : entries = talloc_zero_array(lines, struct restore_dacl, numlines / 2);
2120 :
2121 8 : if (entries == NULL) {
2122 0 : DBG_ERR("error processing %s, out of memory\n", restorefile);
2123 0 : result = EXIT_FAILED;
2124 0 : goto out;
2125 : }
2126 :
2127 8 : sid = get_domain_sid(cli);
2128 :
2129 64 : while (i < numlines) {
2130 56 : int index = i / 2;
2131 56 : int first_line = (i % 2) == 0;
2132 :
2133 56 : if (first_line) {
2134 28 : char *tmp = NULL;
2135 28 : tmp = lines[i];
2136 : /* line can be blank if root of share */
2137 28 : if (strlen(tmp) == 0) {
2138 0 : entries[index].path = talloc_strdup(lines,
2139 : "\\");
2140 : } else {
2141 28 : entries[index].path = lines[i];
2142 : }
2143 : } else {
2144 28 : const char *msg = NULL;
2145 28 : size_t msg_offset = 0;
2146 28 : enum ace_condition_flags flags =
2147 : ACE_CONDITION_FLAG_ALLOW_DEVICE;
2148 56 : entries[index].sd = sddl_decode_err_msg(lines,
2149 28 : lines[i],
2150 : sid,
2151 : flags,
2152 : &msg,
2153 : &msg_offset);
2154 28 : if(entries[index].sd == NULL) {
2155 0 : DBG_ERR("could not decode '%s'\n", lines[i]);
2156 0 : if (msg != NULL) {
2157 0 : DBG_ERR(" %*c\n",
2158 : (int)msg_offset, '^');
2159 0 : DBG_ERR("error '%s'\n", msg);
2160 : }
2161 0 : result = EXIT_FAILED;
2162 0 : goto out;
2163 : }
2164 28 : entries[index].sd->type |=
2165 : SEC_DESC_DACL_AUTO_INHERIT_REQ;
2166 28 : entries[index].sd->type |= SEC_DESC_SACL_AUTO_INHERITED;
2167 : }
2168 56 : i++;
2169 : }
2170 36 : for (i = 0; i < (numlines / 2); i++) {
2171 28 : int mode = SMB_ACL_SET;
2172 : int set_result;
2173 28 : struct cli_state *targetcli = NULL;
2174 28 : char *targetpath = NULL;
2175 : NTSTATUS status;
2176 :
2177 : /* check for dfs */
2178 28 : status = cli_resolve_path(talloc_tos(),
2179 : "",
2180 : creds,
2181 : cli,
2182 28 : entries[i].path,
2183 : &targetcli, &targetpath);
2184 :
2185 28 : if (!NT_STATUS_IS_OK(status)) {
2186 0 : printf("Error failed to process file: %s\n",
2187 0 : entries[i].path);
2188 0 : stats.failure += 1;
2189 0 : continue;
2190 : }
2191 :
2192 28 : set_result = cacl_set_from_sd(targetcli,
2193 : targetpath,
2194 28 : entries[i].sd, mode, numeric);
2195 :
2196 28 : if (set_result == EXIT_OK) {
2197 28 : printf("Successfully processed file: %s\n",
2198 28 : entries[i].path);
2199 28 : stats.success += 1;
2200 : } else {
2201 0 : printf("Error failed to process file: %s\n",
2202 0 : entries[i].path);
2203 0 : stats.failure += 1;
2204 : }
2205 : }
2206 :
2207 8 : result = EXIT_OK;
2208 8 : out:
2209 8 : TALLOC_FREE(lines);
2210 8 : fprintf(stdout, "Successfully processed %d files: "
2211 : "Failed processing %d files\n", stats.success, stats.failure);
2212 8 : return result;
2213 : }
2214 :
2215 : /****************************************************************************
2216 : main program
2217 : ****************************************************************************/
2218 2114 : int main(int argc, char *argv[])
2219 : {
2220 2114 : const char **argv_const = discard_const_p(const char *, argv);
2221 : char *share;
2222 : int opt;
2223 2114 : enum acl_mode mode = SMB_ACL_SET;
2224 : static char *the_acl = NULL;
2225 2114 : enum chown_mode change_mode = REQUEST_NONE;
2226 : int result;
2227 : char *path;
2228 2114 : char *filename = NULL;
2229 : poptContext pc;
2230 : /* numeric is set when the user wants numeric SIDs and ACEs rather
2231 : than going via LSA calls to resolve them */
2232 2114 : int numeric = 0;
2233 2114 : struct cli_state *targetcli = NULL;
2234 2114 : struct cli_credentials *creds = NULL;
2235 2114 : char *targetfile = NULL;
2236 : NTSTATUS status;
2237 : bool ok;
2238 2114 : struct loadparm_context *lp_ctx = NULL;
2239 :
2240 12684 : struct poptOption long_options[] = {
2241 : POPT_AUTOHELP
2242 : {
2243 : .longName = "delete",
2244 : .shortName = 'D',
2245 : .argInfo = POPT_ARG_STRING,
2246 : .arg = NULL,
2247 : .val = 'D',
2248 : .descrip = "Delete an acl",
2249 : .argDescrip = "ACL",
2250 : },
2251 : {
2252 : .longName = "modify",
2253 : .shortName = 'M',
2254 : .argInfo = POPT_ARG_STRING,
2255 : .arg = NULL,
2256 : .val = 'M',
2257 : .descrip = "Modify an acl",
2258 : .argDescrip = "ACL",
2259 : },
2260 : {
2261 : .longName = "add",
2262 : .shortName = 'a',
2263 : .argInfo = POPT_ARG_STRING,
2264 : .arg = NULL,
2265 : .val = 'a',
2266 : .descrip = "Add an acl",
2267 : .argDescrip = "ACL",
2268 : },
2269 : {
2270 : .longName = "set",
2271 : .shortName = 'S',
2272 : .argInfo = POPT_ARG_STRING,
2273 : .arg = NULL,
2274 : .val = 'S',
2275 : .descrip = "Set acls",
2276 : .argDescrip = "ACLS",
2277 : },
2278 : {
2279 : .longName = "chown",
2280 : .shortName = 'C',
2281 : .argInfo = POPT_ARG_STRING,
2282 : .arg = NULL,
2283 : .val = 'C',
2284 : .descrip = "Change ownership of a file",
2285 : .argDescrip = "USERNAME",
2286 : },
2287 : {
2288 : .longName = "chgrp",
2289 : .shortName = 'G',
2290 : .argInfo = POPT_ARG_STRING,
2291 : .arg = NULL,
2292 : .val = 'G',
2293 : .descrip = "Change group ownership of a file",
2294 : .argDescrip = "GROUPNAME",
2295 : },
2296 : {
2297 : .longName = "inherit",
2298 : .shortName = 'I',
2299 : .argInfo = POPT_ARG_STRING,
2300 : .arg = NULL,
2301 : .val = 'I',
2302 : .descrip = "Inherit allow|remove|copy",
2303 : },
2304 : {
2305 : .longName = "propagate-inheritance",
2306 : .shortName = 0,
2307 : .argInfo = POPT_ARG_NONE,
2308 : .arg = &inheritance,
2309 : .val = 1,
2310 : .descrip = "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify",
2311 : },
2312 : {
2313 : .longName = "save",
2314 : .shortName = 0,
2315 : .argInfo = POPT_ARG_STRING,
2316 : .arg = &save_file,
2317 : .val = 1,
2318 : .descrip = "stores the DACLs in sddl format of the "
2319 : "specified file or folder for later use "
2320 : "with restore. SACLS, owner or integrity"
2321 : " labels are not stored",
2322 : },
2323 : {
2324 : .longName = "restore",
2325 : .shortName = 0,
2326 : .argInfo = POPT_ARG_STRING,
2327 : .arg = &restore_file,
2328 : .val = 1,
2329 : .descrip = "applies the stored DACLS to files in "
2330 : "directory.",
2331 : },
2332 : {
2333 : .longName = "recurse",
2334 : .shortName = 0,
2335 : .argInfo = POPT_ARG_NONE,
2336 : .arg = &recurse,
2337 : .val = 1,
2338 : .descrip = "indicates the operation is performed "
2339 : "on directory and all files/directories"
2340 : " below. (only applies to save option)",
2341 : },
2342 : {
2343 : .longName = "numeric",
2344 : .shortName = 0,
2345 : .argInfo = POPT_ARG_NONE,
2346 : .arg = &numeric,
2347 : .val = 1,
2348 : .descrip = "Don't resolve sids or masks to names",
2349 : },
2350 : {
2351 : .longName = "sddl",
2352 : .shortName = 0,
2353 : .argInfo = POPT_ARG_NONE,
2354 : .arg = &sddl,
2355 : .val = 1,
2356 : .descrip = "Output and input acls in sddl format",
2357 : },
2358 : {
2359 : .longName = "query-security-info",
2360 : .shortName = 0,
2361 : .argInfo = POPT_ARG_INT,
2362 : .arg = &query_sec_info,
2363 : .val = 1,
2364 : .descrip = "The security-info flags for queries"
2365 : },
2366 : {
2367 : .longName = "set-security-info",
2368 : .shortName = 0,
2369 : .argInfo = POPT_ARG_INT,
2370 : .arg = &set_sec_info,
2371 : .val = 1,
2372 : .descrip = "The security-info flags for modifications"
2373 : },
2374 : {
2375 : .longName = "test-args",
2376 : .shortName = 't',
2377 : .argInfo = POPT_ARG_NONE,
2378 : .arg = &test_args,
2379 : .val = 1,
2380 : .descrip = "Test arguments"
2381 : },
2382 : {
2383 : .longName = "domain-sid",
2384 : .shortName = 0,
2385 : .argInfo = POPT_ARG_STRING,
2386 : .arg = &domain_sid,
2387 : .val = 0,
2388 : .descrip = "Domain SID for sddl",
2389 : .argDescrip = "SID"},
2390 : {
2391 : .longName = "maximum-access",
2392 : .shortName = 'x',
2393 : .argInfo = POPT_ARG_NONE,
2394 : .arg = NULL,
2395 : .val = 'x',
2396 : .descrip = "Query maximum permissions",
2397 : },
2398 2114 : POPT_COMMON_SAMBA
2399 2114 : POPT_COMMON_CONNECTION
2400 2114 : POPT_COMMON_CREDENTIALS
2401 2114 : POPT_LEGACY_S3
2402 2114 : POPT_COMMON_VERSION
2403 : POPT_TABLEEND
2404 : };
2405 :
2406 : struct cli_state *cli;
2407 2114 : TALLOC_CTX *frame = talloc_stackframe();
2408 2114 : const char *owner_username = "";
2409 : char *server;
2410 :
2411 2114 : smb_init_locale();
2412 :
2413 2114 : ok = samba_cmdline_init(frame,
2414 : SAMBA_CMDLINE_CONFIG_CLIENT,
2415 : false /* require_smbconf */);
2416 2114 : if (!ok) {
2417 0 : DBG_ERR("Failed to init cmdline parser!\n");
2418 0 : TALLOC_FREE(frame);
2419 0 : exit(1);
2420 : }
2421 2114 : lp_ctx = samba_cmdline_get_lp_ctx();
2422 : /* set default debug level to 1 regardless of what smb.conf sets */
2423 2114 : lpcfg_set_cmdline(lp_ctx, "log level", "1");
2424 :
2425 2114 : setlinebuf(stdout);
2426 :
2427 2114 : pc = samba_popt_get_context(getprogname(),
2428 : argc,
2429 : argv_const,
2430 : long_options,
2431 : 0);
2432 2114 : if (pc == NULL) {
2433 0 : DBG_ERR("Failed to setup popt context!\n");
2434 0 : TALLOC_FREE(frame);
2435 0 : exit(1);
2436 : }
2437 :
2438 2114 : poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: "
2439 : "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
2440 :
2441 3780 : while ((opt = poptGetNextOpt(pc)) != -1) {
2442 1626 : switch (opt) {
2443 92 : case 'S':
2444 92 : the_acl = smb_xstrdup(poptGetOptArg(pc));
2445 92 : mode = SMB_ACL_SET;
2446 92 : break;
2447 :
2448 66 : case 'D':
2449 66 : the_acl = smb_xstrdup(poptGetOptArg(pc));
2450 66 : mode = SMB_ACL_DELETE;
2451 66 : break;
2452 :
2453 932 : case 'M':
2454 932 : the_acl = smb_xstrdup(poptGetOptArg(pc));
2455 932 : mode = SMB_ACL_MODIFY;
2456 932 : break;
2457 :
2458 284 : case 'a':
2459 284 : the_acl = smb_xstrdup(poptGetOptArg(pc));
2460 284 : mode = SMB_ACL_ADD;
2461 284 : break;
2462 :
2463 36 : case 'C':
2464 36 : owner_username = poptGetOptArg(pc);
2465 36 : change_mode = REQUEST_CHOWN;
2466 36 : break;
2467 :
2468 4 : case 'G':
2469 4 : owner_username = poptGetOptArg(pc);
2470 4 : change_mode = REQUEST_CHGRP;
2471 4 : break;
2472 :
2473 18 : case 'I':
2474 18 : owner_username = poptGetOptArg(pc);
2475 18 : change_mode = REQUEST_INHERIT;
2476 18 : break;
2477 0 : case 'm':
2478 0 : lpcfg_set_cmdline(lp_ctx, "client max protocol", poptGetOptArg(pc));
2479 0 : break;
2480 4 : case 'x':
2481 4 : want_mxac = true;
2482 4 : break;
2483 0 : case POPT_ERROR_BADOPT:
2484 0 : fprintf(stderr, "\nInvalid option %s: %s\n\n",
2485 : poptBadOption(pc, 0), poptStrerror(opt));
2486 0 : poptPrintUsage(pc, stderr, 0);
2487 0 : exit(1);
2488 : }
2489 : }
2490 2114 : if (inheritance && !the_acl) {
2491 0 : poptPrintUsage(pc, stderr, 0);
2492 0 : return -1;
2493 : }
2494 :
2495 2114 : if(!poptPeekArg(pc)) {
2496 0 : poptPrintUsage(pc, stderr, 0);
2497 0 : return -1;
2498 : }
2499 :
2500 2114 : path = talloc_strdup(frame, poptGetArg(pc));
2501 2114 : if (!path) {
2502 0 : return -1;
2503 : }
2504 :
2505 2114 : if (strncmp(path, "\\\\", 2) && strncmp(path, "//", 2)) {
2506 0 : printf("Invalid argument: %s\n", path);
2507 0 : return -1;
2508 : }
2509 :
2510 2114 : if(!poptPeekArg(pc)) {
2511 0 : poptPrintUsage(pc, stderr, 0);
2512 0 : return -1;
2513 : }
2514 :
2515 2114 : filename = talloc_strdup(frame, poptGetArg(pc));
2516 2114 : if (!filename) {
2517 0 : return -1;
2518 : }
2519 :
2520 2114 : poptFreeContext(pc);
2521 2114 : samba_cmdline_burn(argc, argv);
2522 :
2523 2114 : string_replace(path,'/','\\');
2524 :
2525 2114 : server = talloc_strdup(frame, path+2);
2526 2114 : if (!server) {
2527 0 : return -1;
2528 : }
2529 2114 : share = strchr_m(server,'\\');
2530 2114 : if (share == NULL) {
2531 0 : printf("Invalid argument\n");
2532 0 : return -1;
2533 : }
2534 :
2535 2114 : *share = 0;
2536 2114 : share++;
2537 :
2538 2114 : creds = samba_cmdline_get_creds();
2539 :
2540 : /* Make connection to server */
2541 2114 : if (!test_args) {
2542 2114 : cli = connect_one(creds, server, share);
2543 2114 : if (!cli) {
2544 0 : exit(EXIT_FAILED);
2545 : }
2546 : } else {
2547 0 : exit(0);
2548 : }
2549 :
2550 2114 : string_replace(filename, '/', '\\');
2551 2114 : if (filename[0] != '\\') {
2552 2106 : filename = talloc_asprintf(frame,
2553 : "\\%s",
2554 : filename);
2555 2106 : if (!filename) {
2556 0 : return -1;
2557 : }
2558 : }
2559 :
2560 2114 : status = cli_resolve_path(frame,
2561 : "",
2562 : creds,
2563 : cli,
2564 : filename,
2565 : &targetcli,
2566 : &targetfile);
2567 2114 : if (!NT_STATUS_IS_OK(status)) {
2568 0 : DEBUG(0,("cli_resolve_path failed for %s! (%s)\n", filename, nt_errstr(status)));
2569 0 : return -1;
2570 : }
2571 :
2572 : /* Perform requested action */
2573 :
2574 2114 : if (change_mode == REQUEST_INHERIT) {
2575 18 : result = inherit(targetcli, targetfile, owner_username);
2576 2096 : } else if (change_mode != REQUEST_NONE) {
2577 40 : result = owner_set(targetcli, change_mode, targetfile, owner_username);
2578 2056 : } else if (the_acl) {
2579 1374 : if (inheritance) {
2580 122 : struct cacl_callback_state cbstate = {
2581 : .creds = creds,
2582 : .cli = targetcli,
2583 : .mode = mode,
2584 : .the_acl = the_acl,
2585 : .numeric = numeric,
2586 : };
2587 122 : result = inheritance_cacl_set(targetfile, &cbstate);
2588 : } else {
2589 1252 : result = cacl_set(targetcli,
2590 : targetfile,
2591 : the_acl,
2592 : mode,
2593 : numeric);
2594 : }
2595 : } else {
2596 682 : if (save_file || restore_file) {
2597 40 : sddl = 1;
2598 40 : if (save_file) {
2599 32 : result = cacl_dump_dacl(cli, creds, filename);
2600 : } else {
2601 8 : result = cacl_restore(targetcli,
2602 : creds,
2603 : numeric, restore_file);
2604 : }
2605 : } else {
2606 642 : result = cacl_dump(targetcli, targetfile, numeric);
2607 : }
2608 : }
2609 :
2610 2114 : gfree_all();
2611 2114 : TALLOC_FREE(frame);
2612 :
2613 2114 : return result;
2614 : }
|