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