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

Generated by: LCOV version 1.14