LCOV - code coverage report
Current view: top level - source3/utils - smbcacls.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 805 1122 71.7 %
Date: 2024-05-31 13:13:24 Functions: 33 33 100.0 %

          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             : }

Generated by: LCOV version 1.14