mirror of
https://github.com/samba-team/samba.git
synced 2025-01-12 09:18:10 +03:00
add new '--propagate-inheritance' option for smbcacls
smbcacls now can take a '--propagate-inheritance' flag to indicate that the add, delete, modify and set operations now support automatic propagation of inheritable ACE(s) Signed-off-by: Noel Power <noel.power@suse.com> Reviewed-by: Jeremy Allison <jra@samba.org>
This commit is contained in:
parent
c681f5bfe3
commit
0248fdd09a
@ -6,6 +6,7 @@
|
||||
Copyright (C) Tim Potter 2000
|
||||
Copyright (C) Jeremy Allison 2000
|
||||
Copyright (C) Jelmer Vernooij 2003
|
||||
Copyright (C) Noel Power <noel.power@suse.com> 2013
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -33,6 +34,9 @@
|
||||
#include "../librpc/gen_ndr/ndr_lsa_c.h"
|
||||
#include "util_sd.h"
|
||||
|
||||
static char DIRSEP_CHAR = '\\';
|
||||
|
||||
static int inheritance = 0;
|
||||
static int test_args;
|
||||
static int sddl;
|
||||
static int query_sec_info = -1;
|
||||
@ -45,6 +49,17 @@ enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
|
||||
enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP, REQUEST_INHERIT};
|
||||
enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
|
||||
|
||||
struct cacl_callback_state {
|
||||
struct user_auth_info *auth_info;
|
||||
struct cli_state *cli;
|
||||
struct security_descriptor *aclsd;
|
||||
struct security_acl *acl_to_add;
|
||||
enum acl_mode mode;
|
||||
char *the_acl;
|
||||
bool acl_no_propagate;
|
||||
bool numeric;
|
||||
};
|
||||
|
||||
static NTSTATUS cli_lsa_lookup_domain_sid(struct cli_state *cli,
|
||||
struct dom_sid *sid)
|
||||
{
|
||||
@ -130,12 +145,14 @@ static struct dom_sid *get_domain_sid(struct cli_state *cli)
|
||||
}
|
||||
|
||||
/* add an ACE to a list of ACEs in a struct security_acl */
|
||||
static bool add_ace(struct security_acl **the_acl, struct security_ace *ace)
|
||||
static bool add_ace_with_ctx(TALLOC_CTX *ctx, struct security_acl **the_acl,
|
||||
struct security_ace *ace)
|
||||
|
||||
{
|
||||
struct security_acl *new_ace;
|
||||
struct security_ace *aces;
|
||||
if (! *the_acl) {
|
||||
return (((*the_acl) = make_sec_acl(talloc_tos(), 3, 1, ace))
|
||||
return (((*the_acl) = make_sec_acl(ctx, 3, 1, ace))
|
||||
!= NULL);
|
||||
}
|
||||
|
||||
@ -145,12 +162,18 @@ static bool add_ace(struct security_acl **the_acl, struct security_ace *ace)
|
||||
memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(struct
|
||||
security_ace));
|
||||
memcpy(aces+(*the_acl)->num_aces, ace, sizeof(struct security_ace));
|
||||
new_ace = make_sec_acl(talloc_tos(),(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
|
||||
new_ace = make_sec_acl(ctx, (*the_acl)->revision,
|
||||
1+(*the_acl)->num_aces, aces);
|
||||
SAFE_FREE(aces);
|
||||
(*the_acl) = new_ace;
|
||||
return True;
|
||||
}
|
||||
|
||||
static bool add_ace(struct security_acl **the_acl, struct security_ace *ace)
|
||||
{
|
||||
return add_ace_with_ctx(talloc_tos(), the_acl, ace);
|
||||
}
|
||||
|
||||
/* parse a ascii version of a security descriptor */
|
||||
static struct security_descriptor *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str)
|
||||
{
|
||||
@ -259,7 +282,9 @@ static uint16_t get_fileinfo(struct cli_state *cli, const char *filename)
|
||||
/*****************************************************
|
||||
get sec desc for filename
|
||||
*******************************************************/
|
||||
static struct security_descriptor *get_secdesc(struct cli_state *cli, const char *filename)
|
||||
static struct security_descriptor *get_secdesc_with_ctx(TALLOC_CTX *ctx,
|
||||
struct cli_state *cli,
|
||||
const char *filename)
|
||||
{
|
||||
uint16_t fnum = (uint16_t)-1;
|
||||
struct security_descriptor *sd;
|
||||
@ -293,7 +318,7 @@ static struct security_descriptor *get_secdesc(struct cli_state *cli, const char
|
||||
}
|
||||
|
||||
status = cli_query_security_descriptor(cli, fnum, sec_info,
|
||||
talloc_tos(), &sd);
|
||||
ctx, &sd);
|
||||
|
||||
cli_close(cli, fnum);
|
||||
|
||||
@ -305,6 +330,11 @@ static struct security_descriptor *get_secdesc(struct cli_state *cli, const char
|
||||
return sd;
|
||||
}
|
||||
|
||||
static struct security_descriptor *get_secdesc(struct cli_state *cli,
|
||||
const char *filename)
|
||||
{
|
||||
return get_secdesc_with_ctx(talloc_tos(), cli, filename);
|
||||
}
|
||||
/*****************************************************
|
||||
set sec desc for filename
|
||||
*******************************************************/
|
||||
@ -516,23 +546,18 @@ static void sort_acl(struct security_acl *the_acl)
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
set the ACLs on a file given an ascii description
|
||||
set the ACLs on a file given a security descriptor
|
||||
*******************************************************/
|
||||
|
||||
static int cacl_set(struct cli_state *cli, const char *filename,
|
||||
char *the_acl, enum acl_mode mode, bool numeric)
|
||||
static int cacl_set_from_sd(struct cli_state *cli, const char *filename,
|
||||
struct security_descriptor *sd, enum acl_mode mode,
|
||||
bool numeric)
|
||||
{
|
||||
struct security_descriptor *sd, *old;
|
||||
struct security_descriptor *old = NULL;
|
||||
uint32_t i, j;
|
||||
size_t sd_size;
|
||||
int result = EXIT_OK;
|
||||
|
||||
if (sddl) {
|
||||
sd = sddl_decode(talloc_tos(), the_acl, get_domain_sid(cli));
|
||||
} else {
|
||||
sd = sec_desc_parse(talloc_tos(), cli, the_acl);
|
||||
}
|
||||
|
||||
if (!sd) return EXIT_PARSE_ERROR;
|
||||
if (test_args) return EXIT_OK;
|
||||
|
||||
@ -642,6 +667,30 @@ static int cacl_set(struct cli_state *cli, const char *filename,
|
||||
return result;
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
set the ACLs on a file given an ascii description
|
||||
*******************************************************/
|
||||
|
||||
static int cacl_set(struct cli_state *cli, const char *filename,
|
||||
char *the_acl, enum acl_mode mode, bool numeric)
|
||||
{
|
||||
struct security_descriptor *sd = NULL;
|
||||
|
||||
if (sddl) {
|
||||
sd = sddl_decode(talloc_tos(), the_acl, get_global_sam_sid());
|
||||
} else {
|
||||
sd = sec_desc_parse(talloc_tos(), cli, the_acl);
|
||||
}
|
||||
|
||||
if (sd == NULL) {
|
||||
return EXIT_PARSE_ERROR;
|
||||
}
|
||||
if (test_args) {
|
||||
return EXIT_OK;
|
||||
}
|
||||
return cacl_set_from_sd(cli, filename, sd, mode, numeric);
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
set the inherit on a file
|
||||
*******************************************************/
|
||||
@ -787,6 +836,647 @@ static struct cli_state *connect_one(const struct user_auth_info *auth_info,
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process resulting combination of mask & fname ensuring
|
||||
* terminated with wildcard
|
||||
*/
|
||||
static char *build_dirname(TALLOC_CTX *ctx,
|
||||
const char *mask, char *dir, char *fname)
|
||||
{
|
||||
char *mask2 = NULL;
|
||||
char *p = NULL;
|
||||
|
||||
mask2 = talloc_strdup(ctx, mask);
|
||||
if (!mask2) {
|
||||
return NULL;
|
||||
}
|
||||
p = strrchr_m(mask2, DIRSEP_CHAR);
|
||||
if (p) {
|
||||
p[1] = 0;
|
||||
} else {
|
||||
mask2[0] = '\0';
|
||||
}
|
||||
mask2 = talloc_asprintf_append(mask2,
|
||||
"%s\\*",
|
||||
fname);
|
||||
return mask2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the a copy of the ACL flags in ace modified according
|
||||
* to some inheritance rules.
|
||||
* a) SEC_ACE_FLAG_INHERITED_ACE is propagated to children
|
||||
* b) SEC_ACE_FLAG_INHERIT_ONLY is set on container children for OI (only)
|
||||
* c) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
|
||||
* stripped from flags to be propagated to non-container children
|
||||
* d) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
|
||||
* stripped from flags to be propagated if the NP flag
|
||||
* SEC_ACE_FLAG_NO_PROPAGATE_INHERIT is present
|
||||
*/
|
||||
|
||||
static uint8_t get_flags_to_propagate(bool is_container,
|
||||
struct security_ace *ace)
|
||||
{
|
||||
uint8_t newflags = ace->flags;
|
||||
/* OBJECT inheritance */
|
||||
bool acl_objinherit = (ace->flags &
|
||||
SEC_ACE_FLAG_OBJECT_INHERIT) == SEC_ACE_FLAG_OBJECT_INHERIT;
|
||||
/* CONTAINER inheritance */
|
||||
bool acl_cntrinherit = (ace->flags &
|
||||
SEC_ACE_FLAG_CONTAINER_INHERIT) ==
|
||||
SEC_ACE_FLAG_CONTAINER_INHERIT;
|
||||
/* PROHIBIT inheritance */
|
||||
bool prohibit_inheritance = ((ace->flags &
|
||||
SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) ==
|
||||
SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
|
||||
|
||||
/* Assume we are not propagating the ACE */
|
||||
|
||||
newflags &= ~SEC_ACE_FLAG_INHERITED_ACE;
|
||||
/* all children need to have the SEC_ACE_FLAG_INHERITED_ACE set */
|
||||
if (acl_cntrinherit || acl_objinherit) {
|
||||
/*
|
||||
* object inherit ( alone ) on a container needs
|
||||
* SEC_ACE_FLAG_INHERIT_ONLY
|
||||
*/
|
||||
if (is_container) {
|
||||
if (acl_objinherit && !acl_cntrinherit) {
|
||||
newflags |= SEC_ACE_FLAG_INHERIT_ONLY;
|
||||
}
|
||||
/*
|
||||
* this is tricky, the only time we would not
|
||||
* propagate the ace for a container is if
|
||||
* prohibit_inheritance is set and object inheritance
|
||||
* alone is set
|
||||
*/
|
||||
if ((prohibit_inheritance
|
||||
&& acl_objinherit
|
||||
&& !acl_cntrinherit) == false) {
|
||||
newflags |= SEC_ACE_FLAG_INHERITED_ACE;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* don't apply object/container inheritance flags to
|
||||
* non dirs
|
||||
*/
|
||||
newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
|
||||
| SEC_ACE_FLAG_CONTAINER_INHERIT
|
||||
| SEC_ACE_FLAG_INHERIT_ONLY);
|
||||
/*
|
||||
* only apply ace to file if object inherit
|
||||
*/
|
||||
if (acl_objinherit) {
|
||||
newflags |= SEC_ACE_FLAG_INHERITED_ACE;
|
||||
}
|
||||
}
|
||||
|
||||
/* if NP is specified strip NP and all OI/CI INHERIT flags */
|
||||
if (prohibit_inheritance) {
|
||||
newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
|
||||
| SEC_ACE_FLAG_CONTAINER_INHERIT
|
||||
| SEC_ACE_FLAG_INHERIT_ONLY
|
||||
| SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
|
||||
}
|
||||
}
|
||||
return newflags;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function builds a new acl for 'caclfile', first it removes any
|
||||
* existing inheritable ace(s) from the current acl of caclfile, secondly it
|
||||
* applies any inheritable acls of the parent of caclfile ( inheritable acls of
|
||||
* caclfile's parent are passed via acl_to_add member of cbstate )
|
||||
*
|
||||
*/
|
||||
static NTSTATUS propagate_inherited_aces(char *caclfile,
|
||||
struct cacl_callback_state *cbstate)
|
||||
{
|
||||
TALLOC_CTX *aclctx = NULL;
|
||||
NTSTATUS status;
|
||||
int result;
|
||||
int fileattr;
|
||||
struct security_descriptor *old = NULL;
|
||||
bool is_container = false;
|
||||
struct security_acl *acl_to_add = cbstate->acl_to_add;
|
||||
struct security_acl *acl_to_remove = NULL;
|
||||
uint32_t i, j;
|
||||
|
||||
aclctx = talloc_new(NULL);
|
||||
if (aclctx == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
old = get_secdesc_with_ctx(aclctx, cbstate->cli, caclfile);
|
||||
|
||||
if (!old) {
|
||||
status = NT_STATUS_UNSUCCESSFUL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* inhibit propagation? */
|
||||
if ((old->type & SEC_DESC_DACL_PROTECTED) ==
|
||||
SEC_DESC_DACL_PROTECTED){
|
||||
status = NT_STATUS_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fileattr = get_fileinfo(cbstate->cli, caclfile);
|
||||
is_container = (fileattr & FILE_ATTRIBUTE_DIRECTORY);
|
||||
|
||||
/* find acl(s) that are inherited */
|
||||
for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
|
||||
|
||||
if (old->dacl->aces[j].flags & SEC_ACE_FLAG_INHERITED_ACE) {
|
||||
if (!add_ace_with_ctx(aclctx, &acl_to_remove,
|
||||
&old->dacl->aces[j])) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* remove any acl(s) that are inherited */
|
||||
if (acl_to_remove) {
|
||||
for (i = 0; i < acl_to_remove->num_aces; i++) {
|
||||
struct security_ace ace = acl_to_remove->aces[i];
|
||||
for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
|
||||
|
||||
if (security_ace_equal(&ace,
|
||||
&old->dacl->aces[j])) {
|
||||
uint32_t k;
|
||||
for (k = j; k < old->dacl->num_aces-1;
|
||||
k++) {
|
||||
old->dacl->aces[k] =
|
||||
old->dacl->aces[k+1];
|
||||
}
|
||||
old->dacl->num_aces--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* propagate any inheritable ace to be added */
|
||||
if (acl_to_add) {
|
||||
for (i = 0; i < acl_to_add->num_aces; i++) {
|
||||
struct security_ace ace = acl_to_add->aces[i];
|
||||
bool is_objectinherit = (ace.flags &
|
||||
SEC_ACE_FLAG_OBJECT_INHERIT) ==
|
||||
SEC_ACE_FLAG_OBJECT_INHERIT;
|
||||
bool is_inherited;
|
||||
/* don't propagate flags to a file unless OI */
|
||||
if (!is_objectinherit && !is_container) {
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* adjust flags according to inheritance
|
||||
* rules
|
||||
*/
|
||||
ace.flags = get_flags_to_propagate(is_container, &ace);
|
||||
is_inherited = (ace.flags &
|
||||
SEC_ACE_FLAG_INHERITED_ACE) ==
|
||||
SEC_ACE_FLAG_INHERITED_ACE;
|
||||
/* don't propagate non inherited flags */
|
||||
if (!is_inherited) {
|
||||
continue;
|
||||
}
|
||||
if (!add_ace_with_ctx(aclctx, &old->dacl, &ace)) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = cacl_set_from_sd(cbstate->cli, caclfile,
|
||||
old,
|
||||
SMB_ACL_SET, cbstate->numeric);
|
||||
if (result != EXIT_OK) {
|
||||
status = NT_STATUS_UNSUCCESSFUL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = NT_STATUS_OK;
|
||||
out:
|
||||
TALLOC_FREE(aclctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if 'ace' contains SEC_ACE_FLAG_OBJECT_INHERIT or
|
||||
* SEC_ACE_FLAG_CONTAINER_INHERIT
|
||||
*/
|
||||
static bool is_inheritable_ace(struct security_ace *ace)
|
||||
{
|
||||
uint8_t flags = ace->flags;
|
||||
if (flags & (SEC_ACE_FLAG_OBJECT_INHERIT
|
||||
| SEC_ACE_FLAG_CONTAINER_INHERIT)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This method does some basic sanity checking with respect to automatic
|
||||
* inheritance. e.g. it checks if it is possible to do a set, it detects illegal
|
||||
* attempts to set inherited permissions directly. Additionally this method
|
||||
* does some basic initialisation for instance it parses the ACL passed on the
|
||||
* command line.
|
||||
*/
|
||||
static NTSTATUS prepare_inheritance_propagation(TALLOC_CTX *ctx, char *filename,
|
||||
struct cacl_callback_state *cbstate)
|
||||
{
|
||||
NTSTATUS result;
|
||||
char *the_acl = cbstate->the_acl;
|
||||
struct cli_state *cli = cbstate->cli;
|
||||
enum acl_mode mode = cbstate->mode;
|
||||
struct security_descriptor *sd = NULL;
|
||||
struct security_descriptor *old = NULL;
|
||||
uint32_t j;
|
||||
bool propagate = false;
|
||||
|
||||
old = get_secdesc_with_ctx(ctx, cli, filename);
|
||||
if (old == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* parse acl passed on the command line */
|
||||
if (sddl) {
|
||||
cbstate->aclsd = sddl_decode(ctx, the_acl,
|
||||
get_global_sam_sid());
|
||||
} else {
|
||||
cbstate->aclsd = sec_desc_parse(ctx, cli, the_acl);
|
||||
}
|
||||
|
||||
if (!cbstate->aclsd) {
|
||||
result = NT_STATUS_UNSUCCESSFUL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sd = cbstate->aclsd;
|
||||
|
||||
/* set operation if inheritance is enabled doesn't make sense */
|
||||
if (mode == SMB_ACL_SET && ((old->type & SEC_DESC_DACL_PROTECTED) !=
|
||||
SEC_DESC_DACL_PROTECTED)){
|
||||
d_printf("Inheritance enabled at %s, can't apply set operation\n",filename);
|
||||
result = NT_STATUS_UNSUCCESSFUL;
|
||||
goto out;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* search command line acl for any illegal SEC_ACE_FLAG_INHERITED_ACE
|
||||
* flags that are set
|
||||
*/
|
||||
for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
|
||||
struct security_ace *ace = &sd->dacl->aces[j];
|
||||
if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE) {
|
||||
d_printf("Illegal paramater %s\n", the_acl);
|
||||
result = NT_STATUS_UNSUCCESSFUL;
|
||||
goto out;
|
||||
}
|
||||
if (!propagate) {
|
||||
if (is_inheritable_ace(ace)) {
|
||||
propagate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = NT_STATUS_OK;
|
||||
out:
|
||||
cbstate->acl_no_propagate = !propagate;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method builds inheritable ace(s) from filename (which should be
|
||||
* a container) that need propagating to children in order to provide
|
||||
* automatic inheritance. Those inheritable ace(s) are stored in
|
||||
* acl_to_add member of cbstate for later processing
|
||||
* (see propagate_inherited_aces)
|
||||
*/
|
||||
static NTSTATUS get_inheritable_aces(TALLOC_CTX *ctx, char *filename,
|
||||
struct cacl_callback_state *cbstate)
|
||||
{
|
||||
NTSTATUS result;
|
||||
struct cli_state *cli = NULL;
|
||||
struct security_descriptor *sd = NULL;
|
||||
struct security_acl *acl_to_add = NULL;
|
||||
uint32_t j;
|
||||
|
||||
cli = cbstate->cli;
|
||||
sd = get_secdesc_with_ctx(ctx, cli, filename);
|
||||
|
||||
if (sd == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if any inheritance related flags are used, if not then
|
||||
* nothing to do. At the same time populate acls for inheritance
|
||||
* related ace(s) that need to be added to or deleted from children as
|
||||
* a result of inheritance propagation.
|
||||
*/
|
||||
|
||||
for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
|
||||
struct security_ace *ace = &sd->dacl->aces[j];
|
||||
if (is_inheritable_ace(ace)) {
|
||||
bool added = add_ace_with_ctx(ctx, &acl_to_add, ace);
|
||||
if (!added) {
|
||||
result = NT_STATUS_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
cbstate->acl_to_add = acl_to_add;
|
||||
result = NT_STATUS_OK;
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback handler to handle child elements processed by cli_list, we attempt
|
||||
* to propagate inheritable ace(s) to each child via the function
|
||||
* propagate_inherited_aces. Children that are themselves directories are passed
|
||||
* to cli_list again ( to decend the directory structure )
|
||||
*/
|
||||
static NTSTATUS cacl_set_cb(const char *mntpoint, struct file_info *f,
|
||||
const char *mask, void *state)
|
||||
{
|
||||
struct cacl_callback_state *cbstate =
|
||||
(struct cacl_callback_state *)state;
|
||||
struct cli_state *cli = NULL;
|
||||
struct user_auth_info *auth_info = NULL;
|
||||
|
||||
TALLOC_CTX *dirctx = NULL;
|
||||
NTSTATUS status;
|
||||
struct cli_state *targetcli = NULL;
|
||||
|
||||
char *dir = NULL;
|
||||
char *dir_end = NULL;
|
||||
char *mask2 = NULL;
|
||||
char *targetpath = NULL;
|
||||
char *caclfile = NULL;
|
||||
|
||||
dirctx = talloc_new(NULL);
|
||||
if (!dirctx) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cli = cbstate->cli;
|
||||
auth_info = cbstate->auth_info;
|
||||
|
||||
/* Work out the directory. */
|
||||
dir = talloc_strdup(dirctx, mask);
|
||||
if (!dir) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dir_end = strrchr(dir, DIRSEP_CHAR);
|
||||
if (dir_end != NULL) {
|
||||
*dir_end = '\0';
|
||||
}
|
||||
|
||||
if (!f->name || !f->name[0]) {
|
||||
d_printf("Empty dir name returned. Possible server misconfiguration.\n");
|
||||
status = NT_STATUS_UNSUCCESSFUL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
struct cacl_callback_state dir_cbstate;
|
||||
uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
|
||||
| FILE_ATTRIBUTE_SYSTEM
|
||||
| FILE_ATTRIBUTE_HIDDEN;
|
||||
dir_end = NULL;
|
||||
|
||||
/* ignore special '.' & '..' */
|
||||
if (!f->name || strequal(f->name, ".") ||
|
||||
strequal(f->name, "..")) {
|
||||
status = NT_STATUS_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mask2 = build_dirname(dirctx, mask, dir, f->name);
|
||||
if (mask2 == NULL) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check for dfs */
|
||||
status = cli_resolve_path(dirctx, "", auth_info, cli,
|
||||
mask2, &targetcli, &targetpath);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare path to caclfile, remove any existing wildcard
|
||||
* chars and convert path separators.
|
||||
*/
|
||||
|
||||
caclfile = talloc_strdup(dirctx, targetpath);
|
||||
if (!caclfile) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
dir_end = strrchr(caclfile, '*');
|
||||
if (dir_end != NULL) {
|
||||
*dir_end = '\0';
|
||||
}
|
||||
|
||||
string_replace(caclfile, '/', '\\');
|
||||
/*
|
||||
* make directory specific copy of cbstate here
|
||||
* (for this directory level) to be available as
|
||||
* the parent cbstate for the children of this directory.
|
||||
* Note: cbstate is overwritten for the current file being
|
||||
* processed.
|
||||
*/
|
||||
dir_cbstate = *cbstate;
|
||||
dir_cbstate.cli = targetcli;
|
||||
|
||||
/*
|
||||
* propagate any inherited ace from our parent
|
||||
*/
|
||||
status = propagate_inherited_aces(caclfile, &dir_cbstate);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* get inheritable ace(s) for this dir/container
|
||||
* that will be propagated to its children
|
||||
*/
|
||||
status = get_inheritable_aces(dirctx, caclfile,
|
||||
&dir_cbstate);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* ensure cacl_set_cb gets called for children
|
||||
* of this directory (targetpath)
|
||||
*/
|
||||
status = cli_list(targetcli, targetpath,
|
||||
attribute, cacl_set_cb,
|
||||
(void *)&dir_cbstate);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* build full path to caclfile and replace '/' with '\' so
|
||||
* other utility functions can deal with it
|
||||
*/
|
||||
|
||||
targetpath = talloc_asprintf(dirctx, "%s/%s", dir, f->name);
|
||||
if (!targetpath) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
string_replace(targetpath, '/', '\\');
|
||||
|
||||
/* attempt to propagate any inherited ace to file caclfile */
|
||||
status = propagate_inherited_aces(targetpath, cbstate);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
status = NT_STATUS_OK;
|
||||
out:
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
d_printf("error %s: processing %s\n",
|
||||
nt_errstr(status),
|
||||
targetpath);
|
||||
}
|
||||
TALLOC_FREE(dirctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Wrapper around cl_list to decend the directory tree pointed to by 'filename',
|
||||
* helper callback function 'cacl_set_cb' handles the child elements processed
|
||||
* by cli_list.
|
||||
*/
|
||||
static int inheritance_cacl_set(char *filename,
|
||||
struct cacl_callback_state *cbstate)
|
||||
{
|
||||
int result;
|
||||
NTSTATUS ntstatus;
|
||||
int fileattr;
|
||||
char *mask = NULL;
|
||||
struct cli_state *cli = cbstate->cli;
|
||||
TALLOC_CTX *ctx = NULL;
|
||||
bool isdirectory = false;
|
||||
uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM
|
||||
| FILE_ATTRIBUTE_HIDDEN;
|
||||
ctx = talloc_init("inherit_set");
|
||||
if (ctx == NULL) {
|
||||
d_printf("out of memory\n");
|
||||
result = EXIT_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* ensure we have a filename that starts with '\' */
|
||||
if (!filename || *filename != DIRSEP_CHAR) {
|
||||
/* illegal or no filename */
|
||||
result = EXIT_FAILED;
|
||||
d_printf("illegal or missing name '%s'\n", filename);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
fileattr = get_fileinfo(cli, filename);
|
||||
isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
|
||||
== FILE_ATTRIBUTE_DIRECTORY;
|
||||
|
||||
/*
|
||||
* if we've got as far as here then we have already evaluated
|
||||
* the args.
|
||||
*/
|
||||
if (test_args) {
|
||||
result = EXIT_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mask = NULL;
|
||||
/* make sure we have a trailing '\*' for directory */
|
||||
if (!isdirectory) {
|
||||
mask = talloc_strdup(ctx, filename);
|
||||
} else if (strlen(filename) > 1) {
|
||||
/*
|
||||
* if the passed file name doesn't have a trailing '\'
|
||||
* append it.
|
||||
*/
|
||||
char *name_end = strrchr(filename, DIRSEP_CHAR);
|
||||
if (name_end != filename + strlen(filename) + 1) {
|
||||
mask = talloc_asprintf(ctx, "%s\\*", filename);
|
||||
} else {
|
||||
mask = talloc_strdup(ctx, filename);
|
||||
}
|
||||
} else {
|
||||
/* filename is a single '\', just append '*' */
|
||||
mask = talloc_asprintf_append(mask, "%s*", filename);
|
||||
}
|
||||
|
||||
if (!mask) {
|
||||
result = EXIT_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare for automatic propagation of the acl passed on the
|
||||
* cmdline.
|
||||
*/
|
||||
ntstatus = prepare_inheritance_propagation(ctx, filename,
|
||||
cbstate);
|
||||
if (!NT_STATUS_IS_OK(ntstatus)) {
|
||||
d_printf("error: %s processing %s\n",
|
||||
nt_errstr(ntstatus), filename);
|
||||
result = EXIT_FAILED;
|
||||
goto out;
|
||||
}
|
||||
result = cacl_set_from_sd(cli, filename, cbstate->aclsd,
|
||||
cbstate->mode, cbstate->numeric);
|
||||
|
||||
/*
|
||||
* strictly speaking it could be considered an error if a file was
|
||||
* specificied with '--propagate-inheritance'. However we really want
|
||||
* to eventually get rid of '--propagate-inheritance' so we will be
|
||||
* more forgiving here and instead just exit early.
|
||||
*/
|
||||
if (!isdirectory || (result != EXIT_OK)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check if there is actually any need to propagate */
|
||||
if (cbstate->acl_no_propagate) {
|
||||
goto out;
|
||||
}
|
||||
/* get inheritable attributes this parent container (e.g. filename) */
|
||||
ntstatus = get_inheritable_aces(ctx, filename, cbstate);
|
||||
if (NT_STATUS_IS_OK(ntstatus)) {
|
||||
/* process children */
|
||||
ntstatus = cli_list(cli, mask, attribute,
|
||||
cacl_set_cb,
|
||||
(void *)cbstate);
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(ntstatus)) {
|
||||
d_printf("error: %s processing %s\n",
|
||||
nt_errstr(ntstatus), filename);
|
||||
result = EXIT_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
TALLOC_FREE(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
main program
|
||||
****************************************************************************/
|
||||
@ -873,6 +1563,14 @@ int main(int argc, char *argv[])
|
||||
.val = 'I',
|
||||
.descrip = "Inherit allow|remove|copy",
|
||||
},
|
||||
{
|
||||
.longName = "propagate-inheritance",
|
||||
.shortName = 0,
|
||||
.argInfo = POPT_ARG_NONE,
|
||||
.arg = &inheritance,
|
||||
.val = 1,
|
||||
.descrip = "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify",
|
||||
},
|
||||
{
|
||||
.longName = "numeric",
|
||||
.shortName = 0,
|
||||
@ -1008,8 +1706,11 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (inheritance && !the_acl) {
|
||||
poptPrintUsage(pc, stderr, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Make connection to server */
|
||||
if(!poptPeekArg(pc)) {
|
||||
poptPrintUsage(pc, stderr, 0);
|
||||
return -1;
|
||||
@ -1049,6 +1750,7 @@ int main(int argc, char *argv[])
|
||||
*share = 0;
|
||||
share++;
|
||||
|
||||
/* Make connection to server */
|
||||
if (!test_args) {
|
||||
cli = connect_one(popt_get_cmdline_auth_info(), server, share);
|
||||
if (!cli) {
|
||||
@ -1088,7 +1790,24 @@ int main(int argc, char *argv[])
|
||||
} else if (change_mode != REQUEST_NONE) {
|
||||
result = owner_set(targetcli, change_mode, targetfile, owner_username);
|
||||
} else if (the_acl) {
|
||||
result = cacl_set(targetcli, targetfile, the_acl, mode, numeric);
|
||||
if (inheritance) {
|
||||
struct cacl_callback_state cbstate;
|
||||
cbstate.auth_info = popt_get_cmdline_auth_info();
|
||||
cbstate.cli = targetcli;
|
||||
cbstate.aclsd = NULL;
|
||||
cbstate.acl_to_add = NULL;
|
||||
cbstate.mode = mode;
|
||||
cbstate.the_acl = the_acl;
|
||||
cbstate.acl_no_propagate = false;
|
||||
cbstate.numeric = numeric;
|
||||
result = inheritance_cacl_set(targetfile, &cbstate);
|
||||
} else {
|
||||
result = cacl_set(targetcli,
|
||||
targetfile,
|
||||
the_acl,
|
||||
mode,
|
||||
numeric);
|
||||
}
|
||||
} else {
|
||||
result = cacl_dump(targetcli, targetfile, numeric);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user