summaryrefslogtreecommitdiff
path: root/source3/smbd/posix_acls.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/smbd/posix_acls.c')
-rw-r--r--source3/smbd/posix_acls.c319
1 files changed, 304 insertions, 15 deletions
diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c
index 0071f8786d..2bd0065d58 100644
--- a/source3/smbd/posix_acls.c
+++ b/source3/smbd/posix_acls.c
@@ -42,6 +42,22 @@ typedef struct canon_ace {
static void free_canon_ace_list( canon_ace *list_head );
/****************************************************************************
+ Function to duplicate a canon_ace entry.
+****************************************************************************/
+
+static canon_ace *dup_canon_ace( canon_ace *src_ace)
+{
+ canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace));
+
+ if (dst_ace == NULL)
+ return NULL;
+
+ *dst_ace = *src_ace;
+ dst_ace->prev = dst_ace->next = NULL;
+ return dst_ace;
+}
+
+/****************************************************************************
Function to create owner and group SIDs from a SMB_STRUCT_STAT.
****************************************************************************/
@@ -254,11 +270,120 @@ static BOOL merge_aces( canon_ace *list_head, canon_ace *p_ace)
}
/****************************************************************************
+ Create a default mode for a directory default ACE.
+****************************************************************************/
+
+static mode_t get_default_ace_mode(files_struct *fsp, int type)
+{
+ mode_t force_mode = lp_force_dir_security_mode(SNUM(fsp->conn));
+ mode_t mode = 0;
+
+ switch(type) {
+ case S_IRUSR:
+ mode |= (force_mode & S_IRUSR) ? S_IRUSR : 0;
+ mode |= (force_mode & S_IWUSR) ? S_IWUSR : 0;
+ mode |= (force_mode & S_IXUSR) ? S_IXUSR : 0;
+ break;
+ case S_IRGRP:
+ mode |= (force_mode & S_IRGRP) ? S_IRUSR : 0;
+ mode |= (force_mode & S_IWGRP) ? S_IWUSR : 0;
+ mode |= (force_mode & S_IXGRP) ? S_IXUSR : 0;
+ break;
+ case S_IROTH:
+ mode |= (force_mode & S_IROTH) ? S_IRUSR : 0;
+ mode |= (force_mode & S_IWOTH) ? S_IWUSR : 0;
+ mode |= (force_mode & S_IXOTH) ? S_IXUSR : 0;
+ break;
+ }
+
+ return mode;
+}
+
+/****************************************************************************
+ A well formed POSIX file or default ACL has at least 3 entries, a
+ SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
+****************************************************************************/
+
+static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
+ files_struct *fsp,
+ DOM_SID *pfile_owner_sid,
+ DOM_SID *pfile_grp_sid,
+ SMB_STRUCT_STAT *pst,
+ BOOL default_acl)
+{
+ extern DOM_SID global_sid_World;
+ canon_ace *pace;
+ BOOL got_user = False;
+ BOOL got_grp = False;
+ BOOL got_other = False;
+
+ for (pace = *pp_ace; pace; pace = pace->next) {
+ if (pace->type == SMB_ACL_USER_OBJ)
+ got_user = True;
+ else if (pace->type == SMB_ACL_GROUP_OBJ)
+ got_grp = True;
+ else if (pace->type == SMB_ACL_OTHER)
+ got_other = True;
+ }
+
+ if (!got_user) {
+ if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pace);
+ pace->type = SMB_ACL_USER_OBJ;
+ pace->owner_type = UID_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;
+
+ DLIST_ADD(*pp_ace, pace);
+ }
+
+ if (!got_grp) {
+ if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pace);
+ pace->type = SMB_ACL_GROUP_OBJ;
+ pace->owner_type = GID_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;
+
+ DLIST_ADD(*pp_ace, pace);
+ }
+
+ if (!got_other) {
+ if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
+ DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pace);
+ pace->type = SMB_ACL_OTHER;
+ pace->owner_type = WORLD_ACE;
+ pace->unix_ug.world = -1;
+ pace->sid = global_sid_World;
+ pace->perms = default_acl ? get_default_ace_mode(fsp, S_IROTH): 0;
+
+ DLIST_ADD(*pp_ace, pace);
+ }
+
+ return True;
+}
+
+/****************************************************************************
Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
succeeding.
****************************************************************************/
static BOOL unpack_canon_ace(files_struct *fsp,
+ SMB_STRUCT_STAT *pst,
DOM_SID *pfile_owner_sid,
DOM_SID *pfile_grp_sid,
canon_ace **ppfile_ace, canon_ace **ppdir_ace,
@@ -383,7 +508,8 @@ static BOOL unpack_canon_ace(files_struct *fsp,
current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : SMB_ACL_GROUP;
}
- if (fsp->is_directory && (psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
+ if (fsp->is_directory) {
+
/*
* We can only add to the default POSIX ACE list if the ACE is
* designed to be inherited by both files and directories.
@@ -391,14 +517,45 @@ 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);
- } else {
- DEBUG(0,("unpack_canon_ace: unable to use a non-generic default ACE.\n"));
- free(current_ace);
+
+ /*
+ * If this is not an inherit only ACE we need to add a duplicate
+ * to the file acl.
+ */
+
+ if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
+ canon_ace *dup_ace = dup_canon_ace(current_ace);
+
+ if (!dup_ace) {
+ DEBUG(0,("unpack_canon_ace: malloc fail !\n"));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return False;
+ }
+
+ current_ace = dup_ace;
+ } else {
+ current_ace = NULL;
+ }
}
- } else {
+ }
+
+ /*
+ * Only add to the file ACL if not inherit only.
+ */
+
+ if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
DLIST_ADD(file_ace, current_ace);
all_aces_are_inherit_only = False;
+ current_ace = NULL;
}
+
+ /*
+ * Free if ACE was not addedd.
+ */
+
+ if (current_ace)
+ free(current_ace);
}
if (fsp->is_directory && all_aces_are_inherit_only) {
@@ -431,6 +588,36 @@ static BOOL unpack_canon_ace(files_struct *fsp,
goto again_dir;
}
+ /*
+ * A well formed POSIX file or default ACL has at least 3 entries, a
+ * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
+ * and optionally a mask entry. Ensure this is the case.
+ */
+
+ if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, False)) {
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return False;
+ }
+
+ if (!ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return False;
+ }
+
+ if( DEBUGLVL( 10 )) {
+ dbgtext("unpack_canon_ace: File ACL:\n");
+ for (i = 0, current_ace = file_ace; current_ace; current_ace = current_ace->next, i++ ) {
+ print_canon_ace( current_ace, i);
+ }
+
+ dbgtext("unpack_canon_ace: Directory ACL:\n");
+ for (i = 0, current_ace = dir_ace; current_ace; current_ace = current_ace->next, i++ ) {
+ print_canon_ace( current_ace, i);
+ }
+ }
+
*ppfile_ace = file_ace;
*ppdir_ace = dir_ace;
return True;
@@ -914,7 +1101,7 @@ static canon_ace *canonicalise_acl( SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf)
Attempt to apply an ACL to a file or directory.
****************************************************************************/
-static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace)
+static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
{
BOOL ret = False;
SMB_ACL_T the_acl = sys_acl_init((int)count_canon_ace_list(the_ace) + 1);
@@ -933,6 +1120,7 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
default_ace ? "default" : "file", strerror(errno) ));
#endif
+ *pacl_set_support = False;
return False;
}
@@ -951,6 +1139,13 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
}
/*
+ * Ok - we now know the ACL calls should be working, don't
+ * allow fallback to chmod.
+ */
+
+ *pacl_set_support = True;
+
+ /*
* Initialise the entry from the canon_ace.
*/
@@ -1041,7 +1236,9 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
*/
if (sys_acl_valid(the_acl) == -1) {
- DEBUG(0,("set_canon_ace_list: ACL is invalid for set (%s).\n", strerror(errno) ));
+ DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
+ the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
+ strerror(errno) ));
goto done;
}
@@ -1051,7 +1248,8 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau
if(default_ace || fsp->is_directory || fsp->fd == -1) {
if (sys_acl_set_file(fsp->fsp_name, the_acl_type, the_acl) == -1) {
- DEBUG(0,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
+ DEBUG(0,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
+ the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
fsp->fsp_name, strerror(errno) ));
goto done;
}
@@ -1150,7 +1348,7 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
num_acls = count_canon_ace_list(file_ace);
if (fsp->is_directory) {
- if (dir_ace)
+ if (dir_acl)
dir_ace = canonicalise_acl( dir_acl, &sbuf);
else
dir_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
@@ -1302,7 +1500,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
- acl_perms = unpack_canon_ace( fsp, &file_owner_sid, &file_grp_sid,
+ acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
&file_ace_list, &dir_ace_list, security_info_sent, psd);
posix_perms = unpack_posix_permissions( fsp, &sbuf, &perms, security_info_sent, psd, acl_perms);
@@ -1324,17 +1522,31 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
BOOL acl_set_support = False;
+ BOOL ret = False;
/*
* Try using the POSIX ACL set first. All back to chmod if
* we have no ACL support on this filesystem.
*/
- if (acl_perms && file_ace_list && set_canon_ace_list(fsp, file_ace_list, False))
- acl_set_support = True;
+ if (acl_perms && file_ace_list) {
+ ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
+ if (acl_set_support && ret == False) {
+ DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ }
- if (acl_perms && acl_set_support && fsp->is_directory && dir_ace_list)
- set_canon_ace_list(fsp, dir_ace_list, True);
+ if (acl_perms && acl_set_support && fsp->is_directory && dir_ace_list) {
+ if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
+ DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return False;
+ }
+ }
/*
* If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
@@ -1347,7 +1559,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
file_ace_list = NULL;
dir_ace_list = NULL;
- DEBUG(3,("call_nt_transact_set_security_desc: chmod %s. perms = 0%o.\n",
+ DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
fsp->fsp_name, (unsigned int)perms ));
if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), perms) == -1) {
@@ -1363,4 +1575,81 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
return True;
}
+
+/****************************************************************************
+ Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
+ and set the mask to rwx. Needed to preserve complex ACLs set by NT.
+****************************************************************************/
+
+static int chmod_acl_internals( SMB_ACL_T posix_acl, mode_t mode)
+{
+ int entry_id = SMB_ACL_FIRST_ENTRY;
+ SMB_ACL_ENTRY_T entry;
+ int num_entries = 0;
+
+#if 1
+ return -1;
+#else
+ while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
+ SMB_ACL_TAG_T tagtype;
+ SMB_ACL_PERMSET_T permset;
+
+ if (sys_acl_get_tag_type(entry, &tagtype) == -1)
+ return -1;
+
+ if (sys_acl_get_permset(entry, &permset) == -1)
+ return -1;
+
+ num_entries++;
+
+ switch(tagtype) {
+ case SMB_ACL_USER_OBJ:
+ break;
+ case SMB_ACL_USER:
+ break;
+ case SMB_ACL_GROUP_OBJ:
+ break;
+ case SMB_ACL_GROUP:
+ break;
+ case SMB_ACL_MASK:
+ break;
+ case SMB_ACL_OTHER:
+ break;
+ }
+
+ }
+#endif
+}
+
+/****************************************************************************
+ Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
+ and set the mask to rwx. Needed to preserve complex ACLs set by NT.
+ Note that name is in UNIX character set.
+****************************************************************************/
+
+int chmod_acl(char *name, mode_t mode)
+{
+ SMB_ACL_T posix_acl = NULL;
+
+ if ((posix_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS)) == NULL)
+ return -1;
+
+ return chmod_acl_internals(posix_acl, mode);
+}
+
+/****************************************************************************
+ Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
+ and set the mask to rwx. Needed to preserve complex ACLs set by NT.
+****************************************************************************/
+
+int fchmod_acl(int fd, mode_t mode)
+{
+ SMB_ACL_T posix_acl = NULL;
+
+ if ((posix_acl = sys_acl_get_fd(fd)) == NULL)
+ return -1;
+
+ return chmod_acl_internals(posix_acl, mode);
+}
+
#undef OLD_NTDOMAIN