diff options
Diffstat (limited to 'source3/smbd/posix_acls.c')
-rw-r--r-- | source3/smbd/posix_acls.c | 319 |
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 |