summaryrefslogtreecommitdiff
path: root/source3
diff options
context:
space:
mode:
Diffstat (limited to 'source3')
-rw-r--r--source3/include/dlinklist.h15
-rw-r--r--source3/smbd/posix_acls.c242
2 files changed, 190 insertions, 67 deletions
diff --git a/source3/include/dlinklist.h b/source3/include/dlinklist.h
index 447948b521..d510aad028 100644
--- a/source3/include/dlinklist.h
+++ b/source3/include/dlinklist.h
@@ -56,3 +56,18 @@
DLIST_REMOVE(list, p) \
DLIST_ADD(list, p) \
}
+
+/* demote an element to the top of the list, needs a tmp pointer */
+#define DLIST_DEMOTE(list, p, tmp) \
+{ \
+ DLIST_REMOVE(list, p) \
+ if (!(list)) { \
+ (list) = (p); \
+ (p)->next = (p)->prev = NULL; \
+ } else { \
+ for ((tmp) = (list); (tmp)->next; (tmp) = (tmp)->next) ; \
+ (tmp)->next = (p); \
+ (p)->next = NULL; \
+ (p)->prev = (tmp); \
+ } \
+}
diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c
index b9d6c7e32f..f73f122f43 100644
--- a/source3/smbd/posix_acls.c
+++ b/source3/smbd/posix_acls.c
@@ -22,6 +22,7 @@
#include "includes.h"
enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
+enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
typedef union posix_id {
uid_t uid;
@@ -35,6 +36,7 @@ typedef struct canon_ace {
mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
DOM_SID sid;
enum ace_owner owner_type;
+ enum ace_attribute attr;
posix_id unix_ug;
} canon_ace;
@@ -76,7 +78,7 @@ static void print_canon_ace(canon_ace *ace, int num)
{
fstring str;
- dbgtext( "canon_ace index %d.", num );
+ dbgtext( "canon_ace index %d. Type = %s", num, ace->attr == ALLOW_ACE ? "allow" : "deny" );
dbgtext( "SID = %s ", sid_to_string( str, &ace->sid));
if (ace->owner_type == UID_ACE) {
struct passwd *pass = sys_getpwuid(ace->unix_ug.uid);
@@ -111,6 +113,8 @@ static void print_canon_ace(canon_ace *ace, int num)
/****************************************************************************
Map canon_ace perms to permission bits NT.
+ The attr element is not used here - we only process deny entries on set,
+ not get. Deny entries are implicit on get with ace->perms = 0.
****************************************************************************/
static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
@@ -258,7 +262,8 @@ static BOOL merge_aces( canon_ace *list_head, canon_ace *p_ace)
if (curr_ace == p_ace)
continue;
- if (curr_ace->type == p_ace->type && sid_equal(&curr_ace->sid, &p_ace->sid)) {
+ if ((curr_ace->type == p_ace->type) && (curr_ace->attr == p_ace->attr) &&
+ sid_equal(&curr_ace->sid, &p_ace->sid)) {
if( DEBUGLVL( 10 )) {
dbgtext("Merging ACE's\n");
print_canon_ace( p_ace, 0);
@@ -343,6 +348,7 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
pace->unix_ug.uid = pst->st_uid;
pace->sid = *pfile_owner_sid;
pace->perms = default_acl ? get_default_ace_mode(fsp, S_IRUSR): 0;
+ pace->attr = ALLOW_ACE;
DLIST_ADD(*pp_ace, pace);
}
@@ -359,6 +365,7 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
pace->unix_ug.uid = pst->st_gid;
pace->sid = *pfile_grp_sid;
pace->perms = default_acl ? get_default_ace_mode(fsp, S_IRGRP): 0;
+ pace->attr = ALLOW_ACE;
DLIST_ADD(*pp_ace, pace);
}
@@ -375,6 +382,7 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
pace->unix_ug.world = -1;
pace->sid = global_sid_World;
pace->perms = default_acl ? get_default_ace_mode(fsp, S_IROTH): 0;
+ pace->attr = ALLOW_ACE;
DLIST_ADD(*pp_ace, pace);
}
@@ -496,10 +504,13 @@ static BOOL unpack_canon_ace(files_struct *fsp,
* S_I(R|W|X)USR bits.
*/
- if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
+ if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
- else
+ current_ace->attr = ALLOW_ACE;
+ } else {
current_ace->perms = 0;
+ current_ace->attr = DENY_ACE;
+ }
/*
* Now note what kind of a POSIX ACL this should map to.
@@ -531,8 +542,15 @@ static BOOL unpack_canon_ace(files_struct *fsp,
*/
if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+
DLIST_ADD(dir_ace, current_ace);
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("unpack_canon_ace: adding dir ACL:\n");
+ print_canon_ace( current_ace, 0);
+ }
+
/*
* If this is not an inherit only ACE we need to add a duplicate
* to the file acl.
@@ -906,17 +924,111 @@ static void free_canon_ace_list( canon_ace *list_head )
}
/******************************************************************************
+ When returning permissions, try and fit NT display
+ semantics if possible. Note the the canon_entries here must have been malloced.
+ The list format should be - first entry = owner, followed by group and other user
+ entries, last entry = other.
+********************************************************************************/
+
+static void arrange_posix_perms( char *filename, canon_ace *list_head)
+{
+ extern DOM_SID global_sid_World;
+ canon_ace *owner_ace = NULL;
+ canon_ace *other_ace = NULL;
+ canon_ace *ace = NULL;
+ mode_t owner_perms = 0;
+ mode_t group_perms = 0;
+ mode_t other_perms = 0;
+
+ for (ace = list_head; ace; ace = ace->next) {
+ if (ace->type == SMB_ACL_USER_OBJ)
+ owner_ace = ace;
+ else if (ace->type == SMB_ACL_OTHER) {
+ /* Last ace - this is "other" */
+ other_ace = ace;
+ } else {
+ /* Get the union of all the group and supplementary user perms. */
+ group_perms |= ace->perms;
+ }
+ }
+
+ if (!owner_ace || !other_ace) {
+ DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
+ filename ));
+ return;
+ }
+
+ /*
+ * The POSIX algorithm applies to owner first, and other last,
+ * so ensure they are arranged in this order.
+ */
+
+ if (owner_ace) {
+ DLIST_PROMOTE(list_head, owner_ace);
+ }
+
+ if (other_ace) {
+ DLIST_DEMOTE(list_head, other_ace, ace);
+ }
+
+ owner_perms = owner_ace->perms;
+ other_perms = other_ace->perms;
+
+ /*
+ * We have to be clever here. NT4.x won't display anything other
+ * Than an "Everyone, No access" DENY acl. Truncate blank perms
+ * from the end, but we can't truncate blank permissions from
+ * anywhere except the end, as they have an effect on allowing access
+ * under POSIX.
+ */
+
+ if ((owner_perms || group_perms) && !other_perms) {
+ DLIST_REMOVE(list_head, other_ace);
+ safe_free(other_ace);
+ }
+
+ if (owner_perms && !group_perms && !other_perms) {
+ /* Free everything except the list head. */
+ free_canon_ace_list(owner_ace->next);
+ owner_ace->next = NULL;
+ }
+
+ if (!owner_perms && !group_perms && !other_perms) {
+ /*
+ * Special case - no one has any access.
+ * Return a 1 element ACL - other has "no access".
+ */
+
+ if (owner_ace->next) {
+ free_canon_ace_list(owner_ace->next);
+ owner_ace->next = NULL;
+ }
+
+ owner_ace->type = SMB_ACL_OTHER;
+ owner_ace->sid = global_sid_World;
+ owner_ace->unix_ug.world = -1;
+ owner_ace->owner_type = WORLD_ACE;
+ owner_ace->attr = DENY_ACE;
+ owner_ace->perms = 0;
+ }
+}
+
+/******************************************************************************
Fall back to the generic 3 element UNIX permissions.
********************************************************************************/
static canon_ace *unix_canonicalise_acl(files_struct *fsp, SMB_STRUCT_STAT *psbuf,
- DOM_SID *powner, DOM_SID *pgroup)
+ DOM_SID *powner, DOM_SID *pgroup, BOOL default_acl)
{
extern DOM_SID global_sid_World;
canon_ace *list_head = NULL;
canon_ace *owner_ace = NULL;
canon_ace *group_ace = NULL;
canon_ace *other_ace = NULL;
+ mode_t mode;
+
+ if (default_acl)
+ return NULL;
/*
* Create 3 linked list entries.
@@ -939,58 +1051,36 @@ static canon_ace *unix_canonicalise_acl(files_struct *fsp, SMB_STRUCT_STAT *psbu
owner_ace->sid = *powner;
owner_ace->unix_ug.uid = psbuf->st_uid;
owner_ace->owner_type = UID_ACE;
+ owner_ace->attr = ALLOW_ACE;
group_ace->type = SMB_ACL_GROUP_OBJ;
group_ace->sid = *pgroup;
group_ace->unix_ug.gid = psbuf->st_gid;
group_ace->owner_type = GID_ACE;
+ group_ace->attr = ALLOW_ACE;
other_ace->type = SMB_ACL_OTHER;
other_ace->sid = global_sid_World;
other_ace->unix_ug.world = -1;
other_ace->owner_type = WORLD_ACE;
+ other_ace->attr = ALLOW_ACE;
- if (!fsp->is_directory) {
- owner_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
- group_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
- other_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
- } else {
- mode_t mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
-
- owner_ace->perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
- group_ace->perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
- other_ace->perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
- }
+ mode = psbuf->st_mode;
- if (other_ace->perms) {
- DLIST_ADD(list_head, other_ace);
- } else
- safe_free(other_ace);
- if (group_ace->perms) {
- DLIST_ADD(list_head, group_ace);
- } else
- safe_free(group_ace);
- if (owner_ace->perms) {
- DLIST_ADD(list_head, owner_ace);
- } else
- safe_free(owner_ace);
+ owner_ace->perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
+ owner_ace->attr = owner_ace->perms ? ALLOW_ACE : DENY_ACE;
+
+ group_ace->perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
+ group_ace->attr = group_ace->perms ? ALLOW_ACE : DENY_ACE;
- if (list_head == NULL) {
- /*
- * Return an "Everyone" NO ACCESS ace.
- */
+ other_ace->perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
+ other_ace->attr = other_ace->perms ? ALLOW_ACE : DENY_ACE;
- if ((other_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
- goto fail;
+ DLIST_ADD(list_head, other_ace);
+ DLIST_ADD(list_head, group_ace);
+ DLIST_ADD(list_head, owner_ace);
- other_ace->type = SMB_ACL_OTHER;
- other_ace->sid = global_sid_World;
- other_ace->unix_ug.world = -1;
- other_ace->owner_type = WORLD_ACE;
- other_ace->perms = (mode_t)0;
-
- DLIST_ADD(list_head, other_ace);
- }
+ arrange_posix_perms(fsp->fsp_name,list_head );
return list_head;
@@ -1004,19 +1094,26 @@ static canon_ace *unix_canonicalise_acl(files_struct *fsp, SMB_STRUCT_STAT *psbu
}
/****************************************************************************
- Create a linked list of canonical ACE entries. This is sorted so that DENY
- entries are at the front of the list, as NT requires.
+ Create a linked list of canonical ACE entries.
****************************************************************************/
-static canon_ace *canonicalise_acl( SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf)
+static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
+ DOM_SID *powner, DOM_SID *pgroup, BOOL default_acl)
{
extern DOM_SID global_sid_World;
mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
canon_ace *list_head = NULL;
canon_ace *ace = NULL;
canon_ace *next_ace = NULL;
+ canon_ace *owner_ace = NULL;
+ canon_ace *group_ace = NULL;
+ canon_ace *other_ace = NULL;
int entry_id = SMB_ACL_FIRST_ENTRY;
SMB_ACL_ENTRY_T entry;
+ size_t ace_count;
+
+ if (posix_acl == NULL)
+ return unix_canonicalise_acl( fsp, psbuf, powner, pgroup, default_acl);
while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
SMB_ACL_TAG_T tagtype;
@@ -1098,30 +1195,44 @@ static canon_ace *canonicalise_acl( SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf)
ZERO_STRUCTP(ace);
ace->type = tagtype;
ace->perms = convert_permset_to_mode_t(permset);
+ ace->attr = ace->perms ? ALLOW_ACE : DENY_ACE;
ace->sid = sid;
ace->unix_ug = unix_ug;
ace->owner_type = owner_type;
+ /*
+ * Remember the user/group/other ACE entries.
+ */
+
+ if (tagtype == SMB_ACL_USER_OBJ)
+ owner_ace = ace;
+ else if (tagtype == SMB_ACL_GROUP_OBJ)
+ group_ace = ace;
+ else if (tagtype == SMB_ACL_OTHER)
+ other_ace = ace;
+
DLIST_ADD(list_head, ace);
}
/*
* Now go through the list, masking the permissions with the
- * acl_mask. If the permissions are 0 it should be listed
- * first.
+ * acl_mask.
*/
- for ( ace = list_head; ace; ace = next_ace) {
+ for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
next_ace = ace->next;
/* Masks are only applied to entries other than USER_OBJ and OTHER. */
if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
ace->perms &= acl_mask;
- if (ace->perms == 0)
+ if (ace->perms == 0) {
DLIST_PROMOTE(list_head, ace);
+ }
}
+ arrange_posix_perms(fsp->fsp_name,list_head );
+
if( DEBUGLVL( 10 ) ) {
char *acl_text = sys_acl_to_text( posix_acl, NULL);
@@ -1381,32 +1492,29 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
create_file_sids(&sbuf, &owner_sid, &group_sid);
/* Create the canon_ace lists. */
- if (posix_acl)
- file_ace = canonicalise_acl( posix_acl, &sbuf);
- else
- file_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
-
+ file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, False);
num_acls = count_canon_ace_list(file_ace);
- if (fsp->is_directory) {
- if (dir_acl)
- dir_ace = canonicalise_acl( dir_acl, &sbuf);
- else
- dir_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
+ /* We must have *some* ACLS. */
- num_dir_acls = count_canon_ace_list(dir_ace);
+ if (num_acls == 0) {
+ DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
+ return 0;
}
- if ((num_acls + num_dir_acls) != 0) {
- /* Allocate the ace list. */
- if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
- DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
- goto done;
- }
+ if (fsp->is_directory) {
+ dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid, True);
+ num_dir_acls = count_canon_ace_list(dir_ace);
+ }
- memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
+ /* Allocate the ace list. */
+ if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
+ DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
+ goto done;
}
+ memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
+
/*
* Create the NT ACE list from the canonical ace lists.
*/