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