X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source%2Fsmbd%2Fposix_acls.c;h=6422badf635e9ec582e105b7a21466b7792d9e10;hb=7b582af2107bed3b864bb408b5c9bcce4b8e4c72;hp=25e8fd802bd688502b6b18a621797a64f228d566;hpb=a205c56a75c93c82796fd68687e8c0db26459073;p=samba.git diff --git a/source/smbd/posix_acls.c b/source/smbd/posix_acls.c index 25e8fd802bd..6422badf635 100644 --- a/source/smbd/posix_acls.c +++ b/source/smbd/posix_acls.c @@ -6,7 +6,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -15,12 +15,17 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program. If not, see . */ #include "includes.h" +extern struct current_user current_user; +extern const struct generic_mapping file_generic_mapping; + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_ACLS + /**************************************************************************** Data structures representing the internal ACE format. ****************************************************************************/ @@ -41,8 +46,8 @@ typedef struct canon_ace { DOM_SID trustee; enum ace_owner owner_type; enum ace_attribute attr; - posix_id unix_ug; - BOOL inherited; + posix_id unix_ug; + bool inherited; } canon_ace; #define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR) @@ -74,11 +79,11 @@ typedef struct canon_ace { struct pai_entry { struct pai_entry *next, *prev; enum ace_owner owner_type; - posix_id unix_ug; + posix_id unix_ug; }; - + struct pai_val { - BOOL protected; + bool pai_protected; unsigned int num_entries; struct pai_entry *entry_list; unsigned int num_def_entries; @@ -143,7 +148,7 @@ static unsigned int num_inherited_entries(canon_ace *ace_list) Create the on-disk format. Caller must free. ************************************************************************/ -static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, BOOL protected, size_t *store_size) +static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, bool pai_protected, size_t *store_size) { char *pai_buf = NULL; canon_ace *ace_list = NULL; @@ -163,7 +168,7 @@ static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, B *store_size = PAI_ENTRIES_BASE + ((num_entries + num_def_entries)*PAI_ENTRY_LENGTH); - pai_buf = malloc(*store_size); + pai_buf = (char *)SMB_MALLOC(*store_size); if (!pai_buf) { return NULL; } @@ -171,13 +176,13 @@ static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, B /* Set up the header. */ memset(pai_buf, '\0', PAI_ENTRIES_BASE); SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_VERSION); - SCVAL(pai_buf,PAI_FLAG_OFFSET,(protected ? PAI_ACL_FLAG_PROTECTED : 0)); + SCVAL(pai_buf,PAI_FLAG_OFFSET,(pai_protected ? PAI_ACL_FLAG_PROTECTED : 0)); SSVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET,num_entries); SSVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries); entry_offset = pai_buf + PAI_ENTRIES_BASE; - for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) { + for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) { if (ace_list->inherited) { uint8 type_val = (unsigned char)ace_list->owner_type; uint32 entry_val = get_entry_val(ace_list); @@ -188,7 +193,7 @@ static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, B } } - for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) { + for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) { if (ace_list->inherited) { uint8 type_val = (unsigned char)ace_list->owner_type; uint32 entry_val = get_entry_val(ace_list); @@ -207,7 +212,7 @@ static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, B ************************************************************************/ static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_list, - canon_ace *dir_ace_list, BOOL protected) + canon_ace *dir_ace_list, bool pai_protected) { int ret; size_t store_size; @@ -221,19 +226,19 @@ static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_ * none of the entries in it are marked as inherited. */ - if (!protected && num_inherited_entries(file_ace_list) == 0 && num_inherited_entries(dir_ace_list) == 0) { + if (!pai_protected && num_inherited_entries(file_ace_list) == 0 && num_inherited_entries(dir_ace_list) == 0) { /* Instead just remove the attribute if it exists. */ - if (fsp->fd != -1) - SMB_VFS_FREMOVEXATTR(fsp, fsp->fd, SAMBA_POSIX_INHERITANCE_EA_NAME); + if (fsp->fh->fd != -1) + SMB_VFS_FREMOVEXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME); else SMB_VFS_REMOVEXATTR(fsp->conn, fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME); return; } - pai_buf = create_pai_buf(file_ace_list, dir_ace_list, protected, &store_size); + pai_buf = create_pai_buf(file_ace_list, dir_ace_list, pai_protected, &store_size); - if (fsp->fd != -1) - ret = SMB_VFS_FSETXATTR(fsp, fsp->fd, SAMBA_POSIX_INHERITANCE_EA_NAME, + if (fsp->fh->fd != -1) + ret = SMB_VFS_FSETXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME, pai_buf, store_size, 0); else ret = SMB_VFS_SETXATTR(fsp->conn,fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME, @@ -241,8 +246,8 @@ static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_ SAFE_FREE(pai_buf); - DEBUG(10,("store_inheritance_attribute:%s for file %s\n", protected ? " (protected)" : "", fsp->fsp_name)); - if (ret == -1 && errno != ENOSYS) + DEBUG(10,("store_inheritance_attribute:%s for file %s\n", pai_protected ? " (protected)" : "", fsp->fsp_name)); + if (ret == -1 && !no_acl_syscall_error(errno)) DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) )); } @@ -270,18 +275,18 @@ static void free_inherited_info(struct pai_val *pal) Was this ACL protected ? ************************************************************************/ -static BOOL get_protected_flag(struct pai_val *pal) +static bool get_protected_flag(struct pai_val *pal) { if (!pal) return False; - return pal->protected; + return pal->pai_protected; } /************************************************************************ Was this ACE inherited ? ************************************************************************/ -static BOOL get_inherited_flag(struct pai_val *pal, canon_ace *ace_entry, BOOL default_ace) +static bool get_inherited_flag(struct pai_val *pal, canon_ace *ace_entry, bool default_ace) { struct pai_entry *paie; @@ -301,7 +306,7 @@ static BOOL get_inherited_flag(struct pai_val *pal, canon_ace *ace_entry, BOOL d Ensure an attribute just read is valid. ************************************************************************/ -static BOOL check_pai_ok(char *pai_buf, size_t pai_buf_data_size) +static bool check_pai_ok(char *pai_buf, size_t pai_buf_data_size) { uint16 num_entries; uint16 num_def_entries; @@ -340,13 +345,13 @@ static struct pai_val *create_pai_val(char *buf, size_t size) if (!check_pai_ok(buf, size)) return NULL; - paiv = malloc(sizeof(struct pai_val)); + paiv = SMB_MALLOC_P(struct pai_val); if (!paiv) return NULL; memset(paiv, '\0', sizeof(struct pai_val)); - paiv->protected = (CVAL(buf,PAI_FLAG_OFFSET) == PAI_ACL_FLAG_PROTECTED); + paiv->pai_protected = (CVAL(buf,PAI_FLAG_OFFSET) == PAI_ACL_FLAG_PROTECTED); paiv->num_entries = SVAL(buf,PAI_NUM_ENTRIES_OFFSET); paiv->num_def_entries = SVAL(buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET); @@ -354,12 +359,12 @@ static struct pai_val *create_pai_val(char *buf, size_t size) entry_offset = buf + PAI_ENTRIES_BASE; DEBUG(10,("create_pai_val:%s num_entries = %u, num_def_entries = %u\n", - paiv->protected ? " (protected)" : "", paiv->num_entries, paiv->num_def_entries )); + paiv->pai_protected ? " (pai_protected)" : "", paiv->num_entries, paiv->num_def_entries )); for (i = 0; i < paiv->num_entries; i++) { struct pai_entry *paie; - paie = malloc(sizeof(struct pai_entry)); + paie = SMB_MALLOC_P(struct pai_entry); if (!paie) { free_inherited_info(paiv); return NULL; @@ -390,7 +395,7 @@ static struct pai_val *create_pai_val(char *buf, size_t size) for (i = 0; i < paiv->num_def_entries; i++) { struct pai_entry *paie; - paie = malloc(sizeof(struct pai_entry)); + paie = SMB_MALLOC_P(struct pai_entry); if (!paie) { free_inherited_info(paiv); return NULL; @@ -435,12 +440,12 @@ static struct pai_val *load_inherited_info(files_struct *fsp) if (!lp_map_acl_inherit(SNUM(fsp->conn))) return NULL; - if ((pai_buf = malloc(pai_buf_size)) == NULL) + if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) return NULL; do { - if (fsp->fd != -1) - ret = SMB_VFS_FGETXATTR(fsp, fsp->fd, SAMBA_POSIX_INHERITANCE_EA_NAME, + if (fsp->fh->fd != -1) + ret = SMB_VFS_FGETXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME, pai_buf, pai_buf_size); else ret = SMB_VFS_GETXATTR(fsp->conn,fsp->fsp_name,SAMBA_POSIX_INHERITANCE_EA_NAME, @@ -453,7 +458,10 @@ static struct pai_val *load_inherited_info(files_struct *fsp) /* Buffer too small - enlarge it. */ pai_buf_size *= 2; SAFE_FREE(pai_buf); - if ((pai_buf = malloc(pai_buf_size)) == NULL) + if (pai_buf_size > 1024*1024) { + return NULL; /* Limit malloc to 1mb. */ + } + if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) return NULL; } } while (ret == -1); @@ -475,7 +483,7 @@ static struct pai_val *load_inherited_info(files_struct *fsp) paiv = create_pai_val(pai_buf, ret); - if (paiv && paiv->protected) + if (paiv && paiv->pai_protected) DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fsp->fsp_name)); SAFE_FREE(pai_buf); @@ -507,10 +515,12 @@ static size_t count_canon_ace_list( canon_ace *list_head ) static void free_canon_ace_list( canon_ace *list_head ) { - while (list_head) { - canon_ace *old_head = list_head; - DLIST_REMOVE(list_head, list_head); - SAFE_FREE(old_head); + canon_ace *list, *next; + + for (list = list_head; list; list = next) { + next = list->next; + DLIST_REMOVE(list_head, list); + SAFE_FREE(list); } } @@ -520,7 +530,7 @@ static void free_canon_ace_list( canon_ace *list_head ) static canon_ace *dup_canon_ace( canon_ace *src_ace) { - canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace)); + canon_ace *dst_ace = SMB_MALLOC_P(canon_ace); if (dst_ace == NULL) return NULL; @@ -564,6 +574,9 @@ static void print_canon_ace(canon_ace *pace, int num) case SMB_ACL_OTHER: dbgtext( "SMB_ACL_OTHER "); break; + default: + dbgtext( "MASK " ); + break; } if (pace->inherited) dbgtext( "(inherited) "); @@ -644,16 +657,38 @@ static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_AC } return 0; } + /**************************************************************************** Function to create owner and group SIDs from a SMB_STRUCT_STAT. ****************************************************************************/ -static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid) +static void create_file_sids(const SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid) { uid_to_sid( powner_sid, psbuf->st_uid ); gid_to_sid( pgroup_sid, psbuf->st_gid ); } +/**************************************************************************** + Is the identity in two ACEs equal ? Check both SID and uid/gid. +****************************************************************************/ + +static bool identity_in_ace_equal(canon_ace *ace1, canon_ace *ace2) +{ + if (sid_equal(&ace1->trustee, &ace2->trustee)) { + return True; + } + if (ace1->owner_type == ace2->owner_type) { + if (ace1->owner_type == UID_ACE && + ace1->unix_ug.uid == ace2->unix_ug.uid) { + return True; + } else if (ace1->owner_type == GID_ACE && + ace1->unix_ug.gid == ace2->unix_ug.gid) { + return True; + } + } + return False; +} + /**************************************************************************** Merge aces with a common sid - if both are allow or deny, OR the permissions together and delete the second one. If the first is deny, mask the permissions off and delete the allow @@ -681,7 +716,7 @@ static void merge_aces( canon_ace **pp_list_head ) curr_ace_next = curr_ace->next; /* Save the link in case of delete. */ - if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) && + if (identity_in_ace_equal(curr_ace, curr_ace_outer) && (curr_ace->attr == curr_ace_outer->attr)) { if( DEBUGLVL( 10 )) { @@ -721,7 +756,7 @@ static void merge_aces( canon_ace **pp_list_head ) * we've put on the ACL, we know the deny must be the first one. */ - if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) && + if (identity_in_ace_equal(curr_ace, curr_ace_outer) && (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) { if( DEBUGLVL( 10 )) { @@ -772,17 +807,17 @@ static void merge_aces( canon_ace **pp_list_head ) Check if we need to return NT4.x compatible ACL entries. ****************************************************************************/ -static BOOL nt4_compatible_acls(void) +static bool nt4_compatible_acls(void) { - const char *compat = lp_acl_compatibility(); + int compat = lp_acl_compatibility(); - if (*compat == '\0') { + if (compat == ACL_COMPAT_AUTO) { enum remote_arch_types ra_type = get_remote_arch(); /* Automatically adapt to client */ return (ra_type <= RA_WINNT); } else - return (strequal(compat, "winnt")); + return (compat == ACL_COMPAT_WINNT); } @@ -792,16 +827,23 @@ static BOOL nt4_compatible_acls(void) 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) +static SEC_ACCESS map_canon_ace_perms(int snum, + int *pacl_type, + mode_t perms, + bool directory_ace) { SEC_ACCESS sa; uint32 nt_mask = 0; *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED; - if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) { + if (lp_acl_map_full_control(snum) && ((perms & ALL_ACE_PERMS) == ALL_ACE_PERMS)) { + if (directory_ace) { + nt_mask = UNIX_DIRECTORY_ACCESS_RWX; + } else { nt_mask = UNIX_ACCESS_RWX; - } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) { + } + } else if ((perms & ALL_ACE_PERMS) == (mode_t)0) { /* * Windows NT refuses to display ACEs with no permissions in them (but * they are perfectly legal with Windows 2000). If the ACE has empty @@ -816,13 +858,19 @@ static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon else nt_mask = 0; } else { - nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 ); - nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 ); - nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 ); + if (directory_ace) { + nt_mask |= ((perms & S_IRUSR) ? UNIX_DIRECTORY_ACCESS_R : 0 ); + nt_mask |= ((perms & S_IWUSR) ? UNIX_DIRECTORY_ACCESS_W : 0 ); + nt_mask |= ((perms & S_IXUSR) ? UNIX_DIRECTORY_ACCESS_X : 0 ); + } else { + nt_mask |= ((perms & S_IRUSR) ? UNIX_ACCESS_R : 0 ); + nt_mask |= ((perms & S_IWUSR) ? UNIX_ACCESS_W : 0 ); + nt_mask |= ((perms & S_IXUSR) ? UNIX_ACCESS_X : 0 ); + } } DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n", - (unsigned int)ace->perms, (unsigned int)nt_mask )); + (unsigned int)perms, (unsigned int)nt_mask )); init_sec_access(&sa,nt_mask); return sa; @@ -836,36 +884,36 @@ static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES) #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE) -static mode_t map_nt_perms( SEC_ACCESS sec_access, int type) +static mode_t map_nt_perms( uint32 *mask, int type) { mode_t mode = 0; switch(type) { case S_IRUSR: - if(sec_access.mask & GENERIC_ALL_ACCESS) + if((*mask) & GENERIC_ALL_ACCESS) mode = S_IRUSR|S_IWUSR|S_IXUSR; else { - mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0; - mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0; - mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0; + mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0; + mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0; + mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0; } break; case S_IRGRP: - if(sec_access.mask & GENERIC_ALL_ACCESS) + if((*mask) & GENERIC_ALL_ACCESS) mode = S_IRGRP|S_IWGRP|S_IXGRP; else { - mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0; - mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0; - mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0; + mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0; + mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0; + mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0; } break; case S_IROTH: - if(sec_access.mask & GENERIC_ALL_ACCESS) + if((*mask) & GENERIC_ALL_ACCESS) mode = S_IROTH|S_IWOTH|S_IXOTH; else { - mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0; - mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0; - mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0; + mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0; + mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0; + mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0; } break; } @@ -877,7 +925,7 @@ static mode_t map_nt_perms( SEC_ACCESS sec_access, int type) Unpack a SEC_DESC into a UNIX owner and group. ****************************************************************************/ -static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd) +NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd) { DOM_SID owner_sid; DOM_SID grp_sid; @@ -887,7 +935,7 @@ static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, if(security_info_sent == 0) { DEBUG(0,("unpack_nt_owners: no security info sent !\n")); - return True; + return NT_STATUS_OK; } /* @@ -906,17 +954,20 @@ static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, if (security_info_sent & OWNER_SECURITY_INFORMATION) { sid_copy(&owner_sid, psd->owner_sid); - if (!NT_STATUS_IS_OK(sid_to_uid(&owner_sid, puser))) { -#if ACL_FORCE_UNMAPPABLE - /* this allows take ownership to work reasonably */ - extern struct current_user current_user; - *puser = current_user.uid; -#else - DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n", - sid_string_static(&owner_sid))); - return False; -#endif + if (!sid_to_uid(&owner_sid, puser)) { + if (lp_force_unknown_acl_user(snum)) { + /* this allows take ownership to work + * reasonably */ + *puser = current_user.ut.uid; + } else { + DEBUG(3,("unpack_nt_owners: unable to validate" + " owner sid for %s\n", + sid_string_static(&owner_sid))); + return NT_STATUS_INVALID_OWNER; + } } + DEBUG(3,("unpack_nt_owners: owner sid mapped to uid %u\n", + (unsigned int)*puser )); } /* @@ -925,29 +976,32 @@ static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, */ if (security_info_sent & GROUP_SECURITY_INFORMATION) { - sid_copy(&grp_sid, psd->grp_sid); - if (!NT_STATUS_IS_OK(sid_to_gid( &grp_sid, pgrp))) { -#if ACL_FORCE_UNMAPPABLE - /* this allows take group ownership to work reasonably */ - extern struct current_user current_user; - *pgrp = current_user.gid; -#else - DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n")); - return False; -#endif + sid_copy(&grp_sid, psd->group_sid); + if (!sid_to_gid( &grp_sid, pgrp)) { + if (lp_force_unknown_acl_user(snum)) { + /* this allows take group ownership to work + * reasonably */ + *pgrp = current_user.ut.gid; + } else { + DEBUG(3,("unpack_nt_owners: unable to validate" + " group sid.\n")); + return NT_STATUS_INVALID_OWNER; + } } - } + DEBUG(3,("unpack_nt_owners: group sid mapped to gid %u\n", + (unsigned int)*pgrp)); + } DEBUG(5,("unpack_nt_owners: owner_sids validated.\n")); - return True; + return NT_STATUS_OK; } /**************************************************************************** Ensure the enforced permissions for this share apply. ****************************************************************************/ -static void apply_default_perms(files_struct *fsp, canon_ace *pace, mode_t type) +static void apply_default_perms(const files_struct *fsp, canon_ace *pace, mode_t type) { int snum = SNUM(fsp->conn); mode_t and_bits = (mode_t)0; @@ -991,12 +1045,9 @@ static void apply_default_perms(files_struct *fsp, canon_ace *pace, mode_t type) expensive and will need optimisation. A *lot* of optimisation :-). JRA. ****************************************************************************/ -static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace ) +static bool uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace ) { - extern DOM_SID global_sid_World; - fstring u_name; - fstring g_name; - extern struct current_user current_user; + const char *u_name = NULL; /* "Everyone" always matches every uid. */ @@ -1005,18 +1056,15 @@ static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace ) /* Assume that the current user is in the current group (force group) */ - if (uid_ace->unix_ug.uid == current_user.uid && group_ace->unix_ug.gid == current_user.gid) + if (uid_ace->unix_ug.uid == current_user.ut.uid && group_ace->unix_ug.gid == current_user.ut.gid) return True; - fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid)); - fstrcpy(g_name, gidtoname(group_ace->unix_ug.gid)); - - /* - * Due to the winbind interfaces we need to do this via names, - * not uids/gids. - */ - - return user_in_group_list(u_name, g_name, NULL, 0); + /* u_name talloc'ed off tos. */ + u_name = uidtoname(uid_ace->unix_ug.uid); + if (!u_name) { + return False; + } + return user_in_group_sid(u_name, &group_ace->trustee); } /**************************************************************************** @@ -1029,20 +1077,18 @@ static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace ) type. ****************************************************************************/ -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 setting_acl) +static bool ensure_canon_entry_valid(canon_ace **pp_ace, + const files_struct *fsp, + const DOM_SID *pfile_owner_sid, + const DOM_SID *pfile_grp_sid, + const SMB_STRUCT_STAT *pst, + bool setting_acl) { - extern DOM_SID global_sid_World; canon_ace *pace; - BOOL got_user = False; - BOOL got_grp = False; - BOOL got_other = False; + bool got_user = False; + bool got_grp = False; + bool got_other = False; canon_ace *pace_other = NULL; - canon_ace *pace_group = NULL; for (pace = *pp_ace; pace; pace = pace->next) { if (pace->type == SMB_ACL_USER_OBJ) { @@ -1060,7 +1106,6 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace, if (setting_acl) apply_default_perms(fsp, pace, S_IRGRP); got_grp = True; - pace_group = pace; } else if (pace->type == SMB_ACL_OTHER) { @@ -1076,7 +1121,7 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace, } if (!got_user) { - if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) { + if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) { DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n")); return False; } @@ -1089,13 +1134,28 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace, pace->attr = ALLOW_ACE; if (setting_acl) { + /* See if the owning user is in any of the other groups in + the ACE. If so, OR in the permissions from that group. */ + + bool group_matched = False; + canon_ace *pace_iter; + + for (pace_iter = *pp_ace; pace_iter; pace_iter = pace_iter->next) { + if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) { + if (uid_entry_in_group(pace, pace_iter)) { + pace->perms |= pace_iter->perms; + group_matched = True; + } + } + } + /* If we only got an "everyone" perm, just use that. */ - if (!got_grp && got_other) - pace->perms = pace_other->perms; - else if (got_grp && uid_entry_in_group(pace, pace_group)) - pace->perms = pace_group->perms; - else - pace->perms = 0; + if (!group_matched) { + if (got_other) + pace->perms = pace_other->perms; + else + pace->perms = 0; + } apply_default_perms(fsp, pace, S_IRUSR); } else { @@ -1106,7 +1166,7 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace, } if (!got_grp) { - if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) { + if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) { DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n")); return False; } @@ -1132,7 +1192,7 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace, } if (!got_other) { - if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) { + if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) { DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n")); return False; } @@ -1163,7 +1223,7 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace, static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid) { - BOOL got_user_obj, got_group_obj; + bool got_user_obj, got_group_obj; canon_ace *current_ace; int i, entries; @@ -1204,23 +1264,18 @@ static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID Unpack a SEC_DESC into two canonical ace lists. ****************************************************************************/ -static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst, +static bool create_canon_ace_lists(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, SEC_ACL *dacl) { - extern DOM_SID global_sid_Creator_Owner; - extern DOM_SID global_sid_Creator_Group; - extern DOM_SID global_sid_World; - extern struct generic_mapping file_generic_mapping; - BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False); + bool all_aces_are_inherit_only = (fsp->is_directory ? True : False); canon_ace *file_ace = NULL; canon_ace *dir_ace = NULL; - canon_ace *tmp_ace = NULL; canon_ace *current_ace = NULL; - BOOL got_dir_allow = False; - BOOL got_file_allow = False; + bool got_dir_allow = False; + bool got_file_allow = False; int i, j; *ppfile_ace = NULL; @@ -1231,7 +1286,7 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst, */ for(i = 0; i < dacl->num_aces; i++) { - SEC_ACE *psa = &dacl->ace[i]; + SEC_ACE *psa = &dacl->aces[i]; if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) { DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n")); @@ -1250,12 +1305,12 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst, * Convert GENERIC bits to specific bits. */ - se_map_generic(&psa->info.mask, &file_generic_mapping); + se_map_generic(&psa->access_mask, &file_generic_mapping); - psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS); + psa->access_mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS); - if(psa->info.mask != UNIX_ACCESS_NONE) - psa->info.mask &= ~UNIX_ACCESS_NONE; + if(psa->access_mask != UNIX_ACCESS_NONE) + psa->access_mask &= ~UNIX_ACCESS_NONE; } } @@ -1268,12 +1323,12 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst, */ for(i = 0; i < dacl->num_aces; i++) { - SEC_ACE *psa1 = &dacl->ace[i]; + SEC_ACE *psa1 = &dacl->aces[i]; for (j = i + 1; j < dacl->num_aces; j++) { - SEC_ACE *psa2 = &dacl->ace[j]; + SEC_ACE *psa2 = &dacl->aces[j]; - if (psa1->info.mask != psa2->info.mask) + if (psa1->access_mask != psa2->access_mask) continue; if (!sid_equal(&psa1->trustee, &psa2->trustee)) @@ -1299,24 +1354,13 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst, } for(i = 0; i < dacl->num_aces; i++) { - SEC_ACE *psa = &dacl->ace[i]; - - /* - * Ignore non-mappable SIDs (NT Authority, BUILTIN etc). - */ - - if (non_mappable_sid(&psa->trustee)) { - fstring str; - DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n", - sid_to_string(str, &psa->trustee) )); - continue; - } + SEC_ACE *psa = &dacl->aces[i]; /* * Create a cannon_ace entry representing this NT DACL ACE. */ - if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) { + if ((current_ace = SMB_MALLOC_P(canon_ace)) == NULL) { free_canon_ace_list(file_ace); free_canon_ace_list(dir_ace); DEBUG(0,("create_canon_ace_lists: malloc fail.\n")); @@ -1363,15 +1407,26 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst, if (nt4_compatible_acls()) psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY; - } else if (NT_STATUS_IS_OK(sid_to_uid( ¤t_ace->trustee, ¤t_ace->unix_ug.uid))) { + } else if (sid_to_uid( ¤t_ace->trustee, ¤t_ace->unix_ug.uid)) { current_ace->owner_type = UID_ACE; current_ace->type = SMB_ACL_USER; - } else if (NT_STATUS_IS_OK(sid_to_gid( ¤t_ace->trustee, ¤t_ace->unix_ug.gid))) { + } else if (sid_to_gid( ¤t_ace->trustee, ¤t_ace->unix_ug.gid)) { current_ace->owner_type = GID_ACE; current_ace->type = SMB_ACL_GROUP; } else { fstring str; + /* + * Silently ignore map failures in non-mappable SIDs (NT Authority, BUILTIN etc). + */ + + if (non_mappable_sid(&psa->trustee)) { + DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n", + sid_to_string(str, &psa->trustee) )); + SAFE_FREE(current_ace); + continue; + } + free_canon_ace_list(file_ace); free_canon_ace_list(dir_ace); DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n", @@ -1385,7 +1440,7 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst, * S_I(R|W|X)USR bits. */ - current_ace->perms |= map_nt_perms( psa->info, S_IRUSR); + current_ace->perms |= map_nt_perms( &psa->access_mask, S_IRUSR); current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE; current_ace->inherited = ((psa->flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False); @@ -1405,7 +1460,7 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst, 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_END(dir_ace, current_ace, tmp_ace); + DLIST_ADD_END(dir_ace, current_ace, canon_ace *); /* * Note if this was an allow ace. We can't process @@ -1420,7 +1475,6 @@ static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst, Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name )); free_canon_ace_list(file_ace); free_canon_ace_list(dir_ace); - SAFE_FREE(current_ace); return False; } @@ -1463,8 +1517,8 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name )); * Only add to the file ACL if not inherit only. */ - if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) { - DLIST_ADD_END(file_ace, current_ace, tmp_ace); + if (current_ace && !(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) { + DLIST_ADD_END(file_ace, current_ace, canon_ace *); /* * Note if this was an allow ace. We can't process @@ -1479,7 +1533,6 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name )); Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name )); free_canon_ace_list(file_ace); free_canon_ace_list(dir_ace); - SAFE_FREE(current_ace); return False; } @@ -1521,8 +1574,12 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name )); * entries can be converted to *_OBJ. Usually we will already have these * entries in the Default ACL, and the Access ACL will not have them. */ - check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid); - check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid); + if (file_ace) { + check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid); + } + if (dir_ace) { + check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid); + } } *ppfile_ace = file_ace; @@ -1637,7 +1694,6 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name )); static void process_deny_list( canon_ace **pp_ace_list ) { - extern DOM_SID global_sid_World; canon_ace *ace_list = *pp_ace_list; canon_ace *curr_ace = NULL; canon_ace *curr_ace_next = NULL; @@ -1709,7 +1765,6 @@ static void process_deny_list( canon_ace **pp_ace_list ) for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) { mode_t new_perms = (mode_t)0; canon_ace *allow_ace_p; - canon_ace *tmp_ace; curr_ace_next = curr_ace->next; /* So we can't lose the link. */ @@ -1728,7 +1783,7 @@ static void process_deny_list( canon_ace **pp_ace_list ) curr_ace->attr = ALLOW_ACE; curr_ace->perms = (mode_t)0; - DLIST_DEMOTE(ace_list, curr_ace, tmp_ace); + DLIST_DEMOTE(ace_list, curr_ace, canon_ace *); continue; } @@ -1753,13 +1808,12 @@ static void process_deny_list( canon_ace **pp_ace_list ) curr_ace->attr = ALLOW_ACE; curr_ace->perms = (new_perms & ~curr_ace->perms); - DLIST_DEMOTE(ace_list, curr_ace, tmp_ace); + DLIST_DEMOTE(ace_list, curr_ace, canon_ace *); } /* Pass 3 above - deal with deny group entries. */ for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) { - canon_ace *tmp_ace; canon_ace *allow_ace_p; canon_ace *allow_everyone_p = NULL; @@ -1801,8 +1855,7 @@ static void process_deny_list( canon_ace **pp_ace_list ) curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms; else curr_ace->perms = (mode_t)0; - DLIST_DEMOTE(ace_list, curr_ace, tmp_ace); - + DLIST_DEMOTE(ace_list, curr_ace, canon_ace *); } /* Doing this fourth pass allows Windows semantics to be layered @@ -1852,12 +1905,15 @@ static void process_deny_list( canon_ace **pp_ace_list ) no user/group/world entries. ****************************************************************************/ -static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode) +static mode_t create_default_mode(files_struct *fsp, bool interitable_mode) { int snum = SNUM(fsp->conn); mode_t and_bits = (mode_t)0; mode_t or_bits = (mode_t)0; - mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR; + mode_t mode = interitable_mode + ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name, + NULL ) + : S_IRUSR; if (fsp->is_directory) mode |= (S_IWUSR|S_IXUSR); @@ -1883,7 +1939,7 @@ static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode) succeeding. ****************************************************************************/ -static BOOL unpack_canon_ace(files_struct *fsp, +static bool unpack_canon_ace(files_struct *fsp, SMB_STRUCT_STAT *pst, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid, @@ -2002,15 +2058,15 @@ static BOOL unpack_canon_ace(files_struct *fsp, then a simple POSIX permission of rw-r--r-- should really map to 5 entries, Entry 0: owner : deny all except read and write. - Entry 1: group : deny all except read. - Entry 2: owner : allow read and write. + Entry 1: owner : allow read and write. + Entry 2: group : deny all except read. Entry 3: group : allow read. Entry 4: Everyone : allow read. But NT cannot display this in their ACL editor ! ********************************************************************************/ -static void arrange_posix_perms( char *filename, canon_ace **pp_list_head) +static void arrange_posix_perms(const char *filename, canon_ace **pp_list_head) { canon_ace *list_head = *pp_list_head; canon_ace *owner_ace = NULL; @@ -2042,7 +2098,7 @@ static void arrange_posix_perms( char *filename, canon_ace **pp_list_head) } if (other_ace) { - DLIST_DEMOTE(list_head, other_ace, ace); + DLIST_DEMOTE(list_head, other_ace, canon_ace *); } /* We have probably changed the head of the list. */ @@ -2054,10 +2110,9 @@ static void arrange_posix_perms( char *filename, canon_ace **pp_list_head) Create a linked list of canonical ACE entries. ****************************************************************************/ -static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf, - DOM_SID *powner, DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type) +static canon_ace *canonicalise_acl( const files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf, + const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type) { - extern DOM_SID global_sid_World; connection_struct *conn = fsp->conn; mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR); canon_ace *list_head = NULL; @@ -2107,8 +2162,10 @@ static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_ * entries out of the blue when setting ACLs, so a get/set * cycle will drop them. */ - if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid) + if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid) { + SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype); continue; + } uid_to_sid( &sid, *puid); unix_ug.uid = *puid; owner_type = UID_ACE; @@ -2152,7 +2209,7 @@ static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_ * Add this entry to the list. */ - if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) + if ((ace = SMB_MALLOC_P(canon_ace)) == NULL) goto fail; ZERO_STRUCTP(ace); @@ -2209,22 +2266,56 @@ static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_ return NULL; } +/**************************************************************************** + Check if the current user group list contains a given group. +****************************************************************************/ + +static bool current_user_in_group(gid_t gid) +{ + int i; + + for (i = 0; i < current_user.ut.ngroups; i++) { + if (current_user.ut.groups[i] == gid) { + return True; + } + } + + return False; +} + +/**************************************************************************** + Should we override a deny ? Check deprecated 'acl group control' + and 'dos filemode' +****************************************************************************/ + +static bool acl_group_override(connection_struct *conn, gid_t prim_gid) +{ + if ( (errno == EACCES || errno == EPERM) + && (lp_acl_group_control(SNUM(conn)) || lp_dos_filemode(SNUM(conn))) + && current_user_in_group(prim_gid)) + { + return True; + } + + return False; +} + /**************************************************************************** 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, BOOL *pacl_set_support) +static bool set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, bool default_ace, gid_t prim_gid, bool *pacl_set_support) { connection_struct *conn = fsp->conn; - BOOL ret = False; + bool ret = False; SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, (int)count_canon_ace_list(the_ace) + 1); canon_ace *p_ace; int i; SMB_ACL_ENTRY_T mask_entry; - BOOL got_mask_entry = False; + bool got_mask_entry = False; SMB_ACL_PERMSET_T mask_permset; SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS); - BOOL needs_mask = False; + bool needs_mask = False; mode_t mask_perms = 0; #if defined(POSIX_ACL_NEEDS_MASK) @@ -2234,7 +2325,7 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau if (the_acl == NULL) { - if (errno != ENOSYS) { + if (!no_acl_syscall_error(errno)) { /* * Only print this error message if we have some kind of ACL * support that's not working. Otherwise we would always get this. @@ -2278,7 +2369,7 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) { DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n", i, strerror(errno) )); - goto done; + goto fail; } if (p_ace->type == SMB_ACL_MASK) { @@ -2304,7 +2395,7 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, p_ace->type) == -1) { DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n", i, strerror(errno) )); - goto done; + goto fail; } /* @@ -2316,7 +2407,7 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) { DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n", i, strerror(errno) )); - goto done; + goto fail; } } @@ -2327,13 +2418,13 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) { DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n", i, strerror(errno) )); - goto done; + goto fail; } if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) { DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n", (unsigned int)p_ace->perms, i, strerror(errno) )); - goto done; + goto fail; } /* @@ -2343,7 +2434,7 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) { DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n", i, strerror(errno) )); - goto done; + goto fail; } if( DEBUGLVL( 10 )) @@ -2354,90 +2445,104 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau if (needs_mask && !got_mask_entry) { if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &mask_entry) == -1) { DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) )); - goto done; + goto fail; } if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, mask_entry, SMB_ACL_MASK) == -1) { DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) )); - goto done; + goto fail; } if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, mask_entry, &mask_permset) == -1) { DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) )); - goto done; + goto fail; } if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) { DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) )); - goto done; + goto fail; } if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, mask_entry, mask_permset) == -1) { DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) )); - goto done; + goto fail; } } - /* - * Check if the ACL is valid. - */ - - if (SMB_VFS_SYS_ACL_VALID(conn, the_acl) == -1) { - 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; - } - /* * Finally apply it to the file or directory. */ - if(default_ace || fsp->is_directory || fsp->fd == -1) { + if(default_ace || fsp->is_directory || fsp->fh->fd == -1) { if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) { /* * Some systems allow all the above calls and only fail with no ACL support * when attempting to apply the acl. HPUX with HFS is an example of this. JRA. */ - if (errno == ENOSYS) + if (no_acl_syscall_error(errno)) { *pacl_set_support = False; + } -#ifdef ENOTSUP - if (errno == ENOTSUP) - *pacl_set_support = False; -#endif + if (acl_group_override(conn, prim_gid)) { + int sret; - DEBUG(2,("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; + DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n", + fsp->fsp_name )); + + become_root(); + sret = SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl); + unbecome_root(); + if (sret == 0) { + ret = True; + } + } + + if (ret == False) { + DEBUG(2,("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 fail; + } } } else { - if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fd, the_acl) == -1) { + if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, the_acl) == -1) { /* * Some systems allow all the above calls and only fail with no ACL support * when attempting to apply the acl. HPUX with HFS is an example of this. JRA. */ - if (errno == ENOSYS) + if (no_acl_syscall_error(errno)) { *pacl_set_support = False; + } -#ifdef ENOTSUP - if (errno == ENOTSUP) - *pacl_set_support = False; -#endif + if (acl_group_override(conn, prim_gid)) { + int sret; - DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n", - fsp->fsp_name, strerror(errno) )); - goto done; + DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n", + fsp->fsp_name )); + + become_root(); + sret = SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, the_acl); + unbecome_root(); + if (sret == 0) { + ret = True; + } + } + + if (ret == False) { + DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n", + fsp->fsp_name, strerror(errno) )); + goto fail; + } } } ret = True; - done: + fail: - if (the_acl != NULL) - SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl); + if (the_acl != NULL) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl); + } return ret; } @@ -2481,7 +2586,7 @@ SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T the_acl) #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 ) -static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms) +static bool convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms) { int snum = SNUM(fsp->conn); size_t ace_count = count_canon_ace_list(file_ace_list); @@ -2565,13 +2670,13 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces) for (j = i+1; j < num_aces; j++) { uint32 i_flags_ni = (nt_ace_list[i].flags & ~SEC_ACE_FLAG_INHERITED_ACE); uint32 j_flags_ni = (nt_ace_list[j].flags & ~SEC_ACE_FLAG_INHERITED_ACE); - BOOL i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False; - BOOL j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False; + bool i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False; + bool j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False; /* We know the lower number ACE's are file entries. */ if ((nt_ace_list[i].type == nt_ace_list[j].type) && (nt_ace_list[i].size == nt_ace_list[j].size) && - (nt_ace_list[i].info.mask == nt_ace_list[j].info.mask) && + (nt_ace_list[i].access_mask == nt_ace_list[j].access_mask) && sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) && (i_inh == j_inh) && (i_flags_ni == 0) && @@ -2584,7 +2689,7 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces) * the non-inherited ACE onto the inherited ACE. */ - if (nt_ace_list[i].info.mask == 0) { + if (nt_ace_list[i].access_mask == 0) { nt_ace_list[j].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT| (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0); if (num_aces - i - 1 > 0) @@ -2625,10 +2730,6 @@ static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces) size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) { - extern DOM_SID global_sid_Builtin_Administrators; - extern DOM_SID global_sid_Builtin_Users; - extern DOM_SID global_sid_Creator_Owner; - extern DOM_SID global_sid_Creator_Group; connection_struct *conn = fsp->conn; SMB_STRUCT_STAT sbuf; SEC_ACE *nt_ace_list = NULL; @@ -2637,10 +2738,10 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) size_t sd_size = 0; SEC_ACL *psa = NULL; size_t num_acls = 0; - size_t num_dir_acls = 0; + size_t num_def_acls = 0; size_t num_aces = 0; SMB_ACL_T posix_acl = NULL; - SMB_ACL_T dir_acl = NULL; + SMB_ACL_T def_acl = NULL; canon_ace *file_ace = NULL; canon_ace *dir_ace = NULL; size_t num_profile_acls = 0; @@ -2651,7 +2752,7 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name )); - if(fsp->is_directory || fsp->fd == -1) { + if(fsp->is_directory || fsp->fh->fd == -1) { /* Get the stat struct for the owner info. */ if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0) { @@ -2668,25 +2769,25 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) */ if(fsp->is_directory) { - dir_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT); - dir_acl = free_empty_sys_acl(conn, dir_acl); + def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT); + def_acl = free_empty_sys_acl(conn, def_acl); } } else { /* Get the stat struct for the owner info. */ - if(SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf) != 0) { + if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0) { return 0; } /* * Get the ACL from the fd. */ - posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fd); + posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd); } DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n", posix_acl ? "present" : "absent", - dir_acl ? "present" : "absent" )); + def_acl ? "present" : "absent" )); pal = load_inherited_info(fsp); @@ -2694,7 +2795,7 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) * Get the owner, group and world SIDs. */ - if (lp_profile_acls(SNUM(fsp->conn))) { + if (lp_profile_acls(SNUM(conn))) { /* For WXP SP1 the owner must be administrators. */ sid_copy(&owner_sid, &global_sid_Builtin_Administrators); sid_copy(&group_sid, &global_sid_Builtin_Users); @@ -2721,11 +2822,11 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) if (count_canon_ace_list(file_ace) == 0) { DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name )); - return 0; + goto done; } - if (fsp->is_directory && dir_acl) { - dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, + if (fsp->is_directory && def_acl) { + dir_ace = canonicalise_acl(fsp, def_acl, &sbuf, &global_sid_Creator_Owner, &global_sid_Creator_Group, pal, SMB_ACL_TYPE_DEFAULT ); } @@ -2789,54 +2890,74 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) } num_acls = count_canon_ace_list(file_ace); - num_dir_acls = count_canon_ace_list(dir_ace); + num_def_acls = count_canon_ace_list(dir_ace); /* Allocate the ace list. */ - if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_profile_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) { + if ((nt_ace_list = SMB_MALLOC_ARRAY(SEC_ACE,num_acls + num_profile_acls + num_def_acls)) == 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) ); - + memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(SEC_ACE) ); + /* * Create the NT ACE list from the canonical ace lists. */ - + ace = file_ace; for (i = 0; i < num_acls; i++, ace = ace->next) { SEC_ACCESS acc; - acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace ); - init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0); + acc = map_canon_ace_perms(SNUM(conn), + &nt_acl_type, + ace->perms, + fsp->is_directory); + init_sec_ace(&nt_ace_list[num_aces++], + &ace->trustee, + nt_acl_type, + acc, + ace->inherited ? + SEC_ACE_FLAG_INHERITED_ACE : 0); } - /* The User must have access to a profile share - even if we can't map the SID. */ - if (lp_profile_acls(SNUM(fsp->conn))) { + /* The User must have access to a profile share - even + * if we can't map the SID. */ + if (lp_profile_acls(SNUM(conn))) { SEC_ACCESS acc; init_sec_access(&acc,FILE_GENERIC_ALL); - init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, + init_sec_ace(&nt_ace_list[num_aces++], + &global_sid_Builtin_Users, + SEC_ACE_TYPE_ACCESS_ALLOWED, acc, 0); } ace = dir_ace; - for (i = 0; i < num_dir_acls; i++, ace = ace->next) { + for (i = 0; i < num_def_acls; i++, ace = ace->next) { SEC_ACCESS acc; - - acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace ); - init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, - SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT| - SEC_ACE_FLAG_INHERIT_ONLY| - (ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0)); + + acc = map_canon_ace_perms(SNUM(conn), + &nt_acl_type, + ace->perms, + fsp->is_directory); + init_sec_ace(&nt_ace_list[num_aces++], + &ace->trustee, + nt_acl_type, + acc, + SEC_ACE_FLAG_OBJECT_INHERIT| + SEC_ACE_FLAG_CONTAINER_INHERIT| + SEC_ACE_FLAG_INHERIT_ONLY| + (ace->inherited ? + SEC_ACE_FLAG_INHERITED_ACE : 0)); } - /* The User must have access to a profile share - even if we can't map the SID. */ - if (lp_profile_acls(SNUM(fsp->conn))) { + /* The User must have access to a profile share - even + * if we can't map the SID. */ + if (lp_profile_acls(SNUM(conn))) { SEC_ACCESS acc; - + init_sec_access(&acc,FILE_GENERIC_ALL); init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT| @@ -2854,14 +2975,14 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) } if (num_aces) { - if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) { + if((psa = make_sec_acl( talloc_tos(), NT4_ACL_REVISION, num_aces, nt_ace_list)) == NULL) { DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n")); goto done; } } } /* security_info & DACL_SECURITY_INFORMATION */ - psd = make_standard_sec_desc( main_loop_talloc_get(), + psd = make_standard_sec_desc( talloc_tos(), (security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL, (security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL, psa, @@ -2870,31 +2991,37 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) if(!psd) { DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n")); sd_size = 0; - } else { - /* - * Windows 2000: The DACL_PROTECTED flag in the security - * descriptor marks the ACL as non-inheriting, i.e., no - * ACEs from higher level directories propagate to this - * ACL. In the POSIX ACL model permissions are only - * inherited at file create time, so ACLs never contain - * any ACEs that are inherited dynamically. The DACL_PROTECTED - * flag doesn't seem to bother Windows NT. - */ - if (get_protected_flag(pal)) - psd->type |= SE_DESC_DACL_PROTECTED; + goto done; + } + + /* + * Windows 2000: The DACL_PROTECTED flag in the security + * descriptor marks the ACL as non-inheriting, i.e., no + * ACEs from higher level directories propagate to this + * ACL. In the POSIX ACL model permissions are only + * inherited at file create time, so ACLs never contain + * any ACEs that are inherited dynamically. The DACL_PROTECTED + * flag doesn't seem to bother Windows NT. + * Always set this if map acl inherit is turned off. + */ + if (get_protected_flag(pal) || !lp_map_acl_inherit(SNUM(conn))) { + psd->type |= SE_DESC_DACL_PROTECTED; } - if (psd->dacl) - dacl_sort_into_canonical_order(psd->dacl->ace, (unsigned int)psd->dacl->num_aces); + if (psd->dacl) { + dacl_sort_into_canonical_order(psd->dacl->aces, (unsigned int)psd->dacl->num_aces); + } *ppdesc = psd; done: - if (posix_acl) + if (posix_acl) { SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl); - if (dir_acl) - SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl); + } + if (def_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl); + } free_canon_ace_list(file_ace); free_canon_ace_list(dir_ace); free_inherited_info(pal); @@ -2907,41 +3034,72 @@ size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc) Try to chown a file. We will be able to chown it under the following conditions. 1) If we have root privileges, then it will just work. - 2) If we have write permission to the file and dos_filemodes is set + 2) If we have SeTakeOwnershipPrivilege we can change the user to the current user. + 3) If we have SeRestorePrivilege we can change the user to any other user. + 4) If we have write permission to the file and dos_filemodes is set then allow chown to the currently authenticated user. ****************************************************************************/ -static int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid) +int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid) { int ret; - extern struct current_user current_user; files_struct *fsp; SMB_STRUCT_STAT st; + if(!CAN_WRITE(conn)) { + return -1; + } + + /* Case (1). */ /* try the direct way first */ ret = SMB_VFS_CHOWN(conn, fname, uid, gid); if (ret == 0) return 0; - if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn))) + /* Case (2) / (3) */ + if (lp_enable_privileges()) { + + bool has_take_ownership_priv = user_has_privileges(current_user.nt_user_token, + &se_take_ownership); + bool has_restore_priv = user_has_privileges(current_user.nt_user_token, + &se_restore); + + /* Case (2) */ + if ( ( has_take_ownership_priv && ( uid == current_user.ut.uid ) ) || + /* Case (3) */ + ( has_restore_priv ) ) { + + become_root(); + /* Keep the current file gid the same - take ownership doesn't imply group change. */ + ret = SMB_VFS_CHOWN(conn, fname, uid, (gid_t)-1); + unbecome_root(); + return ret; + } + } + + /* Case (4). */ + if (!lp_dos_filemode(SNUM(conn))) { + errno = EPERM; return -1; + } - if (SMB_VFS_STAT(conn,fname,&st)) + if (SMB_VFS_STAT(conn,fname,&st)) { return -1; + } - fsp = open_file_fchmod(conn,fname,&st); - if (!fsp) + if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,&st,&fsp))) { return -1; + } /* only allow chown to the current user. This is more secure, and also copes with the case where the SID in a take ownership ACL is a local SID on the users workstation */ - uid = current_user.uid; + uid = current_user.ut.uid; become_root(); /* Keep the current file gid the same. */ - ret = SMB_VFS_FCHOWN(fsp, fsp->fd, uid, (gid_t)-1); + ret = SMB_VFS_FCHOWN(fsp, fsp->fh->fd, uid, (gid_t)-1); unbecome_root(); close_file_fchmod(fsp); @@ -2949,74 +3107,255 @@ static int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_ return ret; } +static NTSTATUS append_ugw_ace(files_struct *fsp, + SMB_STRUCT_STAT *psbuf, + mode_t unx_mode, + int ugw, + SEC_ACE *se) +{ + mode_t perms; + SEC_ACCESS acc; + int nt_acl_type; + DOM_SID trustee; + + switch (ugw) { + case S_IRUSR: + perms = unix_perms_to_acl_perms(unx_mode, + S_IRUSR, + S_IWUSR, + S_IXUSR); + uid_to_sid(&trustee, psbuf->st_uid ); + break; + case S_IRGRP: + perms = unix_perms_to_acl_perms(unx_mode, + S_IRGRP, + S_IWGRP, + S_IXGRP); + gid_to_sid(&trustee, psbuf->st_gid ); + break; + case S_IROTH: + perms = unix_perms_to_acl_perms(unx_mode, + S_IROTH, + S_IWOTH, + S_IXOTH); + sid_copy(&trustee, &global_sid_World); + break; + default: + return NT_STATUS_INVALID_PARAMETER; + } + acc = map_canon_ace_perms(SNUM(fsp->conn), + &nt_acl_type, + perms, + fsp->is_directory); + + init_sec_ace(se, + &trustee, + nt_acl_type, + acc, + 0); + return NT_STATUS_OK; +} + +/**************************************************************************** + If this is an +****************************************************************************/ + +static NTSTATUS append_parent_acl(files_struct *fsp, + SMB_STRUCT_STAT *psbuf, + SEC_DESC *psd, + SEC_DESC **pp_new_sd) +{ + SEC_DESC *parent_sd = NULL; + files_struct *parent_fsp = NULL; + TALLOC_CTX *mem_ctx = talloc_parent(psd); + char *parent_name = NULL; + SEC_ACE *new_ace = NULL; + unsigned int num_aces = psd->dacl->num_aces; + SMB_STRUCT_STAT sbuf; + NTSTATUS status; + int info; + size_t sd_size; + unsigned int i, j; + mode_t unx_mode; + + ZERO_STRUCT(sbuf); + + if (mem_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (!parent_dirname_talloc(mem_ctx, + fsp->fsp_name, + &parent_name, + NULL)) { + return NT_STATUS_NO_MEMORY; + } + + /* Create a default mode for u/g/w. */ + unx_mode = unix_mode(fsp->conn, + aARCH | (fsp->is_directory ? aDIR : 0), + fsp->fsp_name, + parent_name); + + status = open_directory(fsp->conn, + NULL, + parent_name, + &sbuf, + FILE_READ_ATTRIBUTES, /* Just a stat open */ + FILE_SHARE_NONE, /* Ignored for stat opens */ + FILE_OPEN, + 0, + INTERNAL_OPEN_ONLY, + &info, + &parent_fsp); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + sd_size = SMB_VFS_GET_NT_ACL(parent_fsp, parent_fsp->fsp_name, + DACL_SECURITY_INFORMATION, &parent_sd ); + + close_file(parent_fsp, NORMAL_CLOSE); + + if (!sd_size) { + return NT_STATUS_ACCESS_DENIED; + } + + /* + * Make room for potentially all the ACLs from + * the parent, plus the user/group/other triple. + */ + + num_aces += parent_sd->dacl->num_aces + 3; + + if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, SEC_ACE, + num_aces)) == NULL) { + return NT_STATUS_NO_MEMORY; + } + + DEBUG(10,("append_parent_acl: parent ACL has %u entries. New " + "ACL has %u entries\n", + parent_sd->dacl->num_aces, num_aces )); + + /* Start by copying in all the given ACE entries. */ + for (i = 0; i < psd->dacl->num_aces; i++) { + sec_ace_copy(&new_ace[i], &psd->dacl->aces[i]); + } + + /* + * Note that we're ignoring "inherit permissions" here + * as that really only applies to newly created files. JRA. + */ + + /* + * Append u/g/w. + */ + + status = append_ugw_ace(fsp, psbuf, unx_mode, S_IRUSR, &new_ace[i++]); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = append_ugw_ace(fsp, psbuf, unx_mode, S_IRGRP, &new_ace[i++]); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = append_ugw_ace(fsp, psbuf, unx_mode, S_IROTH, &new_ace[i++]); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* Finally append any inherited ACEs. */ + for (j = 0; j < parent_sd->dacl->num_aces; j++) { + SEC_ACE *se = &parent_sd->dacl->aces[j]; + uint32 i_flags = se->flags & (SEC_ACE_FLAG_OBJECT_INHERIT| + SEC_ACE_FLAG_CONTAINER_INHERIT| + SEC_ACE_FLAG_INHERIT_ONLY); + + if (fsp->is_directory) { + if (i_flags == SEC_ACE_FLAG_OBJECT_INHERIT) { + /* Should only apply to a file - ignore. */ + continue; + } + } else { + if ((i_flags & (SEC_ACE_FLAG_OBJECT_INHERIT| + SEC_ACE_FLAG_INHERIT_ONLY)) != + SEC_ACE_FLAG_OBJECT_INHERIT) { + /* Should not apply to a file - ignore. */ + continue; + } + } + sec_ace_copy(&new_ace[i], se); + if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) { + new_ace[i].flags &= ~(SEC_ACE_FLAG_VALID_INHERIT); + } + new_ace[i].flags |= SEC_ACE_FLAG_INHERITED_ACE; + i++; + } + + parent_sd->dacl->aces = new_ace; + parent_sd->dacl->num_aces = i; + + *pp_new_sd = parent_sd; + return status; +} + /**************************************************************************** Reply to set a security descriptor on an fsp. security_info_sent is the description of the following NT ACL. This should be the only external function needed for the UNIX style set ACL. ****************************************************************************/ -BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) +NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) { connection_struct *conn = fsp->conn; uid_t user = (uid_t)-1; gid_t grp = (gid_t)-1; - SMB_STRUCT_STAT sbuf; + SMB_STRUCT_STAT sbuf; DOM_SID file_owner_sid; DOM_SID file_grp_sid; canon_ace *file_ace_list = NULL; canon_ace *dir_ace_list = NULL; - BOOL acl_perms = False; + bool acl_perms = False; mode_t orig_mode = (mode_t)0; - uid_t orig_uid; - gid_t orig_gid; - BOOL need_chown = False; - extern struct current_user current_user; + NTSTATUS status; DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name )); if (!CAN_WRITE(conn)) { DEBUG(10,("set acl rejected on read-only share\n")); - return False; + return NT_STATUS_MEDIA_WRITE_PROTECTED; } /* * Get the current state of the file. */ - if(fsp->is_directory || fsp->fd == -1) { + if(fsp->is_directory || fsp->fh->fd == -1) { if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0) - return False; + return map_nt_error_from_unix(errno); } else { - if(SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf) != 0) - return False; + if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0) + return map_nt_error_from_unix(errno); } /* Save the original elements we check against. */ orig_mode = sbuf.st_mode; - orig_uid = sbuf.st_uid; - orig_gid = sbuf.st_gid; /* * Unpack the user/group/world id's. */ - if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd)) - return False; + status = unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd); + if (!NT_STATUS_IS_OK(status)) { + return status; + } /* * Do we need to chown ? */ - if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp))) - need_chown = True; - - /* - * Chown before setting ACL only if we don't change the user, or - * if we change to the current user, but not if we want to give away - * the file. - */ - - if (need_chown && (user == (uid_t)-1 || user == current_user.uid)) { + if (((user != (uid_t)-1) && (sbuf.st_uid != user)) || (( grp != (gid_t)-1) && (sbuf.st_gid != grp))) { DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n", fsp->fsp_name, (unsigned int)user, (unsigned int)grp )); @@ -3024,7 +3363,10 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) { DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n", fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) )); - return False; + if (errno == EPERM) { + return NT_STATUS_INVALID_OWNER; + } + return map_nt_error_from_unix(errno); } /* @@ -3034,34 +3376,41 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) if(fsp->is_directory) { if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) { - return False; + return map_nt_error_from_unix(errno); } } else { int ret; - - if(fsp->fd == -1) + + if(fsp->fh->fd == -1) ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf); else - ret = SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf); - + ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf); + if(ret != 0) - return False; + return map_nt_error_from_unix(errno); } /* Save the original elements we check against. */ orig_mode = sbuf.st_mode; - orig_uid = sbuf.st_uid; - orig_gid = sbuf.st_gid; - - /* We did it, don't try again */ - need_chown = False; } create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid); + if ((security_info_sent & DACL_SECURITY_INFORMATION) && + psd->dacl != NULL && + (psd->type & (SE_DESC_DACL_AUTO_INHERITED| + SE_DESC_DACL_AUTO_INHERIT_REQ))== + (SE_DESC_DACL_AUTO_INHERITED| + SE_DESC_DACL_AUTO_INHERIT_REQ) ) { + status = append_parent_acl(fsp, &sbuf, psd, &psd); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid, - &file_ace_list, &dir_ace_list, security_info_sent, psd); + &file_ace_list, &dir_ace_list, security_info_sent, psd); /* Ignore W2K traverse DACL set. */ if (file_ace_list || dir_ace_list) { @@ -3070,7 +3419,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) DEBUG(3,("set_nt_acl: cannot set permissions\n")); free_canon_ace_list(file_ace_list); free_canon_ace_list(dir_ace_list); - return False; + return NT_STATUS_ACCESS_DENIED; } /* @@ -3079,8 +3428,8 @@ 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; + bool acl_set_support = False; + bool ret = False; /* * Try using the POSIX ACL set first. Fall back to chmod if @@ -3088,22 +3437,22 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) */ if (acl_perms && file_ace_list) { - ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support); + ret = set_canon_ace_list(fsp, file_ace_list, False, sbuf.st_gid, &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; + return map_nt_error_from_unix(errno); } } if (acl_perms && acl_set_support && fsp->is_directory) { if (dir_ace_list) { - if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) { + if (!set_canon_ace_list(fsp, dir_ace_list, True, sbuf.st_gid, &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; + return map_nt_error_from_unix(errno); } } else { @@ -3112,17 +3461,32 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) */ if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name) == -1) { - DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno))); - free_canon_ace_list(file_ace_list); - free_canon_ace_list(dir_ace_list); - return False; + int sret = -1; + + if (acl_group_override(conn, sbuf.st_gid)) { + DEBUG(5,("set_nt_acl: acl group control on and " + "current user in file %s primary group. Override delete_def_acl\n", + fsp->fsp_name )); + + become_root(); + sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name); + unbecome_root(); + } + + if (sret == -1) { + DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno))); + free_canon_ace_list(file_ace_list); + free_canon_ace_list(dir_ace_list); + return map_nt_error_from_unix(errno); + } } } } - if (acl_set_support) + if (acl_set_support) { store_inheritance_attributes(fsp, file_ace_list, dir_ace_list, (psd->type & SE_DESC_DACL_PROTECTED) ? True : False); + } /* * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod. @@ -3136,7 +3500,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) free_canon_ace_list(dir_ace_list); DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n", fsp->fsp_name )); - return False; + return NT_STATUS_ACCESS_DENIED; } if (orig_mode != posix_perms) { @@ -3145,11 +3509,24 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) fsp->fsp_name, (unsigned int)posix_perms )); if(SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms) == -1) { - DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n", + int sret = -1; + if (acl_group_override(conn, sbuf.st_gid)) { + DEBUG(5,("set_nt_acl: acl group control on and " + "current user in file %s primary group. Override chmod\n", + fsp->fsp_name )); + + become_root(); + sret = SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms); + unbecome_root(); + } + + if (sret == -1) { + DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n", fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) )); - free_canon_ace_list(file_ace_list); - free_canon_ace_list(dir_ace_list); - return False; + free_canon_ace_list(file_ace_list); + free_canon_ace_list(dir_ace_list); + return map_nt_error_from_unix(errno); + } } } } @@ -3159,20 +3536,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) free_canon_ace_list(dir_ace_list); } - /* Any chown pending? */ - if (need_chown) { - - DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n", - fsp->fsp_name, (unsigned int)user, (unsigned int)grp )); - - if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) { - DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n", - fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) )); - return False; - } - } - - return True; + return NT_STATUS_OK; } /**************************************************************************** @@ -3186,6 +3550,7 @@ int get_acl_group_bits( connection_struct *conn, const char *fname, mode_t *mode int entry_id = SMB_ACL_FIRST_ENTRY; SMB_ACL_ENTRY_T entry; SMB_ACL_T posix_acl; + int result = -1; posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS); if (posix_acl == (SMB_ACL_T)NULL) @@ -3200,21 +3565,23 @@ int get_acl_group_bits( connection_struct *conn, const char *fname, mode_t *mode entry_id = SMB_ACL_NEXT_ENTRY; if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) ==-1) - return -1; + break; if (tagtype == SMB_ACL_GROUP_OBJ) { if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) { - return -1; + break; } else { *mode &= ~(S_IRGRP|S_IWGRP|S_IXGRP); *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRGRP : 0); *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWGRP : 0); *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXGRP : 0); - return 0;; + result = 0; + break; } } } - return -1; + SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl); + return result; } /**************************************************************************** @@ -3323,19 +3690,17 @@ int chmod_acl(connection_struct *conn, const char *name, mode_t mode) } /**************************************************************************** - If "inherit permissions" is set and the parent directory has no default - ACL but it does have an Access ACL, inherit this Access ACL to file name. + If the parent directory has no default ACL but it does have an Access ACL, + inherit this Access ACL to file name. ****************************************************************************/ -int inherit_access_acl(connection_struct *conn, const char *name, mode_t mode) +int inherit_access_acl(connection_struct *conn, const char *inherit_from_dir, + const char *name, mode_t mode) { - pstring dirname; - pstrcpy(dirname, parent_dirname(name)); - - if (!lp_inherit_perms(SNUM(conn)) || directory_has_default_acl(conn, dirname)) + if (directory_has_default_acl(conn, inherit_from_dir)) return 0; - return copy_access_acl(conn, dirname, name, mode); + return copy_access_acl(conn, inherit_from_dir, name, mode); } /**************************************************************************** @@ -3367,16 +3732,899 @@ int fchmod_acl(files_struct *fsp, int fd, mode_t mode) Check for an existing default POSIX ACL on a directory. ****************************************************************************/ -BOOL directory_has_default_acl(connection_struct *conn, const char *fname) +bool directory_has_default_acl(connection_struct *conn, const char *fname) { - SMB_ACL_T dir_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT); - BOOL has_acl = False; - SMB_ACL_ENTRY_T entry; + SMB_ACL_T def_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT); + bool has_acl = False; + SMB_ACL_ENTRY_T entry; - if (dir_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1)) - has_acl = True; + if (def_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, def_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1)) { + has_acl = True; + } - if (dir_acl) - SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl); + if (def_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl); + } return has_acl; } + +/**************************************************************************** + Map from wire type to permset. +****************************************************************************/ + +static bool unix_ex_wire_to_permset(connection_struct *conn, unsigned char wire_perm, SMB_ACL_PERMSET_T *p_permset) +{ + if (wire_perm & ~(SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE)) { + return False; + } + + if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1) { + return False; + } + + if (wire_perm & SMB_POSIX_ACL_READ) { + if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1) { + return False; + } + } + if (wire_perm & SMB_POSIX_ACL_WRITE) { + if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1) { + return False; + } + } + if (wire_perm & SMB_POSIX_ACL_EXECUTE) { + if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1) { + return False; + } + } + return True; +} + +/**************************************************************************** + Map from wire type to tagtype. +****************************************************************************/ + +static bool unix_ex_wire_to_tagtype(unsigned char wire_tt, SMB_ACL_TAG_T *p_tt) +{ + switch (wire_tt) { + case SMB_POSIX_ACL_USER_OBJ: + *p_tt = SMB_ACL_USER_OBJ; + break; + case SMB_POSIX_ACL_USER: + *p_tt = SMB_ACL_USER; + break; + case SMB_POSIX_ACL_GROUP_OBJ: + *p_tt = SMB_ACL_GROUP_OBJ; + break; + case SMB_POSIX_ACL_GROUP: + *p_tt = SMB_ACL_GROUP; + break; + case SMB_POSIX_ACL_MASK: + *p_tt = SMB_ACL_MASK; + break; + case SMB_POSIX_ACL_OTHER: + *p_tt = SMB_ACL_OTHER; + break; + default: + return False; + } + return True; +} + +/**************************************************************************** + Create a new POSIX acl from wire permissions. + FIXME ! How does the share mask/mode fit into this.... ? +****************************************************************************/ + +static SMB_ACL_T create_posix_acl_from_wire(connection_struct *conn, uint16 num_acls, const char *pdata) +{ + unsigned int i; + SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, num_acls); + + if (the_acl == NULL) { + return NULL; + } + + for (i = 0; i < num_acls; i++) { + SMB_ACL_ENTRY_T the_entry; + SMB_ACL_PERMSET_T the_permset; + SMB_ACL_TAG_T tag_type; + + if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) { + DEBUG(0,("create_posix_acl_from_wire: Failed to create entry %u. (%s)\n", + i, strerror(errno) )); + goto fail; + } + + if (!unix_ex_wire_to_tagtype(CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), &tag_type)) { + DEBUG(0,("create_posix_acl_from_wire: invalid wire tagtype %u on entry %u.\n", + CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), i )); + goto fail; + } + + if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, tag_type) == -1) { + DEBUG(0,("create_posix_acl_from_wire: Failed to set tagtype on entry %u. (%s)\n", + i, strerror(errno) )); + goto fail; + } + + /* Get the permset pointer from the new ACL entry. */ + if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) { + DEBUG(0,("create_posix_acl_from_wire: Failed to get permset on entry %u. (%s)\n", + i, strerror(errno) )); + goto fail; + } + + /* Map from wire to permissions. */ + if (!unix_ex_wire_to_permset(conn, CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+1), &the_permset)) { + DEBUG(0,("create_posix_acl_from_wire: invalid permset %u on entry %u.\n", + CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE) + 1), i )); + goto fail; + } + + /* Now apply to the new ACL entry. */ + if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) { + DEBUG(0,("create_posix_acl_from_wire: Failed to add permset on entry %u. (%s)\n", + i, strerror(errno) )); + goto fail; + } + + if (tag_type == SMB_ACL_USER) { + uint32 uidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2); + uid_t uid = (uid_t)uidval; + if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&uid) == -1) { + DEBUG(0,("create_posix_acl_from_wire: Failed to set uid %u on entry %u. (%s)\n", + (unsigned int)uid, i, strerror(errno) )); + goto fail; + } + } + + if (tag_type == SMB_ACL_GROUP) { + uint32 gidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2); + gid_t gid = (uid_t)gidval; + if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&gid) == -1) { + DEBUG(0,("create_posix_acl_from_wire: Failed to set gid %u on entry %u. (%s)\n", + (unsigned int)gid, i, strerror(errno) )); + goto fail; + } + } + } + + return the_acl; + + fail: + + if (the_acl != NULL) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl); + } + return NULL; +} + +/**************************************************************************** + Calls from UNIX extensions - Default POSIX ACL set. + If num_def_acls == 0 and not a directory just return. If it is a directory + and num_def_acls == 0 then remove the default acl. Else set the default acl + on the directory. +****************************************************************************/ + +bool set_unix_posix_default_acl(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, + uint16 num_def_acls, const char *pdata) +{ + SMB_ACL_T def_acl = NULL; + + if (num_def_acls && !S_ISDIR(psbuf->st_mode)) { + DEBUG(5,("set_unix_posix_default_acl: Can't set default ACL on non-directory file %s\n", fname )); + errno = EISDIR; + return False; + } + + if (!num_def_acls) { + /* Remove the default ACL. */ + if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fname) == -1) { + DEBUG(5,("set_unix_posix_default_acl: acl_delete_def_file failed on directory %s (%s)\n", + fname, strerror(errno) )); + return False; + } + return True; + } + + if ((def_acl = create_posix_acl_from_wire(conn, num_def_acls, pdata)) == NULL) { + return False; + } + + if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_DEFAULT, def_acl) == -1) { + DEBUG(5,("set_unix_posix_default_acl: acl_set_file failed on directory %s (%s)\n", + fname, strerror(errno) )); + SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl); + return False; + } + + DEBUG(10,("set_unix_posix_default_acl: set default acl for file %s\n", fname )); + SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl); + return True; +} + +/**************************************************************************** + Remove an ACL from a file. As we don't have acl_delete_entry() available + we must read the current acl and copy all entries except MASK, USER and GROUP + to a new acl, then set that. This (at least on Linux) causes any ACL to be + removed. + FIXME ! How does the share mask/mode fit into this.... ? +****************************************************************************/ + +static bool remove_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname) +{ + SMB_ACL_T file_acl = NULL; + int entry_id = SMB_ACL_FIRST_ENTRY; + SMB_ACL_ENTRY_T entry; + bool ret = False; + /* Create a new ACL with only 3 entries, u/g/w. */ + SMB_ACL_T new_file_acl = SMB_VFS_SYS_ACL_INIT(conn, 3); + SMB_ACL_ENTRY_T user_ent = NULL; + SMB_ACL_ENTRY_T group_ent = NULL; + SMB_ACL_ENTRY_T other_ent = NULL; + + if (new_file_acl == NULL) { + DEBUG(5,("remove_posix_acl: failed to init new ACL with 3 entries for file %s.\n", fname)); + return False; + } + + /* Now create the u/g/w entries. */ + if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &user_ent) == -1) { + DEBUG(5,("remove_posix_acl: Failed to create user entry for file %s. (%s)\n", + fname, strerror(errno) )); + goto done; + } + if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, user_ent, SMB_ACL_USER_OBJ) == -1) { + DEBUG(5,("remove_posix_acl: Failed to set user entry for file %s. (%s)\n", + fname, strerror(errno) )); + goto done; + } + + if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &group_ent) == -1) { + DEBUG(5,("remove_posix_acl: Failed to create group entry for file %s. (%s)\n", + fname, strerror(errno) )); + goto done; + } + if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, group_ent, SMB_ACL_GROUP_OBJ) == -1) { + DEBUG(5,("remove_posix_acl: Failed to set group entry for file %s. (%s)\n", + fname, strerror(errno) )); + goto done; + } + + if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &other_ent) == -1) { + DEBUG(5,("remove_posix_acl: Failed to create other entry for file %s. (%s)\n", + fname, strerror(errno) )); + goto done; + } + if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, other_ent, SMB_ACL_OTHER) == -1) { + DEBUG(5,("remove_posix_acl: Failed to set other entry for file %s. (%s)\n", + fname, strerror(errno) )); + goto done; + } + + /* Get the current file ACL. */ + if (fsp && fsp->fh->fd != -1) { + file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd); + } else { + file_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_ACCESS); + } + + if (file_acl == NULL) { + /* This is only returned if an error occurred. Even for a file with + no acl a u/g/w acl should be returned. */ + DEBUG(5,("remove_posix_acl: failed to get ACL from file %s (%s).\n", + fname, strerror(errno) )); + goto done; + } + + while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, file_acl, entry_id, &entry) == 1) { + SMB_ACL_TAG_T tagtype; + SMB_ACL_PERMSET_T permset; + + /* get_next... */ + if (entry_id == SMB_ACL_FIRST_ENTRY) + entry_id = SMB_ACL_NEXT_ENTRY; + + if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) { + DEBUG(5,("remove_posix_acl: failed to get tagtype from ACL on file %s (%s).\n", + fname, strerror(errno) )); + goto done; + } + + if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) { + DEBUG(5,("remove_posix_acl: failed to get permset from ACL on file %s (%s).\n", + fname, strerror(errno) )); + goto done; + } + + if (tagtype == SMB_ACL_USER_OBJ) { + if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, user_ent, permset) == -1) { + DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n", + fname, strerror(errno) )); + } + } else if (tagtype == SMB_ACL_GROUP_OBJ) { + if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, group_ent, permset) == -1) { + DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n", + fname, strerror(errno) )); + } + } else if (tagtype == SMB_ACL_OTHER) { + if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, other_ent, permset) == -1) { + DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n", + fname, strerror(errno) )); + } + } + } + + /* Set the new empty file ACL. */ + if (fsp && fsp->fh->fd != -1) { + if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, new_file_acl) == -1) { + DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n", + fname, strerror(errno) )); + goto done; + } + } else { + if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, new_file_acl) == -1) { + DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n", + fname, strerror(errno) )); + goto done; + } + } + + ret = True; + + done: + + if (file_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl); + } + if (new_file_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, new_file_acl); + } + return ret; +} + +/**************************************************************************** + Calls from UNIX extensions - POSIX ACL set. + If num_def_acls == 0 then read/modify/write acl after removing all entries + except SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER. +****************************************************************************/ + +bool set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname, uint16 num_acls, const char *pdata) +{ + SMB_ACL_T file_acl = NULL; + + if (!num_acls) { + /* Remove the ACL from the file. */ + return remove_posix_acl(conn, fsp, fname); + } + + if ((file_acl = create_posix_acl_from_wire(conn, num_acls, pdata)) == NULL) { + return False; + } + + if (fsp && fsp->fh->fd != -1) { + /* The preferred way - use an open fd. */ + if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, file_acl) == -1) { + DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n", + fname, strerror(errno) )); + SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl); + return False; + } + } else { + if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, file_acl) == -1) { + DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n", + fname, strerror(errno) )); + SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl); + return False; + } + } + + DEBUG(10,("set_unix_posix_acl: set acl for file %s\n", fname )); + SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl); + return True; +} + +/**************************************************************************** + Check for POSIX group ACLs. If none use stat entry. + Return -1 if no match, 0 if match and denied, 1 if match and allowed. +****************************************************************************/ + +static int check_posix_acl_group_access(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, uint32 access_mask) +{ + SMB_ACL_T posix_acl = NULL; + int entry_id = SMB_ACL_FIRST_ENTRY; + SMB_ACL_ENTRY_T entry; + int i; + bool seen_mask = False; + bool seen_owning_group = False; + int ret = -1; + gid_t cu_gid; + + DEBUG(10,("check_posix_acl_group_access: requesting 0x%x on file %s\n", + (unsigned int)access_mask, fname )); + + if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS)) == NULL) { + goto check_stat; + } + + /* First ensure the group mask allows group access. */ + /* Also check any user entries (these take preference over group). */ + + while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) { + SMB_ACL_TAG_T tagtype; + SMB_ACL_PERMSET_T permset; + int have_write = -1; + int have_read = -1; + + /* get_next... */ + if (entry_id == SMB_ACL_FIRST_ENTRY) + entry_id = SMB_ACL_NEXT_ENTRY; + + if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) { + goto check_stat; + } + + if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) { + goto check_stat; + } + + have_read = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ); + if (have_read == -1) { + goto check_stat; + } + + have_write = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE); + if (have_write == -1) { + goto check_stat; + } + + /* + * Solaris returns 2 for this if write is available. + * canonicalize to 0 or 1. + */ + have_write = (have_write ? 1 : 0); + have_read = (have_read ? 1 : 0); + + switch(tagtype) { + case SMB_ACL_MASK: + seen_mask = True; + switch (access_mask) { + case FILE_READ_DATA: + if (!have_read) { + ret = -1; + DEBUG(10,("check_posix_acl_group_access: file %s " + "refusing read due to mask.\n", fname)); + goto done; + } + break; + case FILE_WRITE_DATA: + if (!have_write) { + ret = -1; + DEBUG(10,("check_posix_acl_group_access: file %s " + "refusing write due to mask.\n", fname)); + goto done; + } + break; + default: /* FILE_READ_DATA|FILE_WRITE_DATA */ + if (!have_write || !have_read) { + ret = -1; + DEBUG(10,("check_posix_acl_group_access: file %s " + "refusing read/write due to mask.\n", fname)); + goto done; + } + break; + } + break; + case SMB_ACL_USER: + { + /* Check against current_user.ut.uid. */ + uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry); + if (puid == NULL) { + goto check_stat; + } + if (current_user.ut.uid == *puid) { + /* We have a uid match but we must ensure we have seen the acl mask. */ + switch (access_mask) { + case FILE_READ_DATA: + ret = have_read; + break; + case FILE_WRITE_DATA: + ret = have_write; + break; + default: /* FILE_READ_DATA|FILE_WRITE_DATA */ + ret = (have_write & have_read); + break; + } + DEBUG(10,("check_posix_acl_group_access: file %s " + "match on user %u -> %s.\n", + fname, (unsigned int)*puid, + ret ? "can access" : "cannot access")); + if (seen_mask) { + goto done; + } + } + break; + } + default: + continue; + } + } + + /* If ret is anything other than -1 we matched on a user entry. */ + if (ret != -1) { + goto done; + } + + /* Next check all group entries. */ + entry_id = SMB_ACL_FIRST_ENTRY; + while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) { + SMB_ACL_TAG_T tagtype; + SMB_ACL_PERMSET_T permset; + int have_write = -1; + int have_read = -1; + + /* get_next... */ + if (entry_id == SMB_ACL_FIRST_ENTRY) + entry_id = SMB_ACL_NEXT_ENTRY; + + if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) { + goto check_stat; + } + + if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) { + goto check_stat; + } + + have_read = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ); + if (have_read == -1) { + goto check_stat; + } + + have_write = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE); + if (have_write == -1) { + goto check_stat; + } + + /* + * Solaris returns 2 for this if write is available. + * canonicalize to 0 or 1. + */ + have_write = (have_write ? 1 : 0); + have_read = (have_read ? 1 : 0); + + switch(tagtype) { + case SMB_ACL_GROUP: + case SMB_ACL_GROUP_OBJ: + { + gid_t *pgid = NULL; + + if (tagtype == SMB_ACL_GROUP) { + pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry); + } else { + seen_owning_group = True; + pgid = &psbuf->st_gid; + } + if (pgid == NULL) { + goto check_stat; + } + + /* + * Does it match the current effective group + * or supplementary groups ? + */ + for (cu_gid = get_current_user_gid_first(&i); cu_gid != (gid_t)-1; + cu_gid = get_current_user_gid_next(&i)) { + if (cu_gid == *pgid) { + switch (access_mask) { + case FILE_READ_DATA: + ret = have_read; + break; + case FILE_WRITE_DATA: + ret = have_write; + break; + default: /* FILE_READ_DATA|FILE_WRITE_DATA */ + ret = (have_write & have_read); + break; + } + + DEBUG(10,("check_posix_acl_group_access: file %s " + "match on group %u -> can access.\n", + fname, (unsigned int)cu_gid )); + + /* If we don't have access permission this entry doesn't + terminate the enumeration of the entries. */ + if (ret) { + goto done; + } + /* But does terminate the group iteration. */ + break; + } + } + break; + } + default: + continue; + } + } + + /* If ret is -1 here we didn't match on the user entry or + supplemental group entries. */ + + DEBUG(10,("check_posix_acl_group_access: ret = %d before check_stat:\n", ret)); + + check_stat: + + /* + * We only check the S_I[RW]GRP permissions if we haven't already + * seen an owning group SMB_ACL_GROUP_OBJ ace entry. If there is an + * SMB_ACL_GROUP_OBJ ace entry then the group bits in st_gid are + * the same as the SMB_ACL_MASK bits, not the SMB_ACL_GROUP_OBJ + * bits. Thanks to Marc Cousin for pointing + * this out. JRA. + */ + + if (!seen_owning_group) { + /* Do we match on the owning group entry ? */ + /* + * Does it match the current effective group + * or supplementary groups ? + */ + for (cu_gid = get_current_user_gid_first(&i); cu_gid != (gid_t)-1; + cu_gid = get_current_user_gid_next(&i)) { + if (cu_gid == psbuf->st_gid) { + switch (access_mask) { + case FILE_READ_DATA: + ret = (psbuf->st_mode & S_IRGRP) ? 1 : 0; + break; + case FILE_WRITE_DATA: + ret = (psbuf->st_mode & S_IWGRP) ? 1 : 0; + break; + default: /* FILE_READ_DATA|FILE_WRITE_DATA */ + if ((psbuf->st_mode & (S_IWGRP|S_IRGRP)) == (S_IWGRP|S_IRGRP)) { + ret = 1; + } else { + ret = 0; + } + break; + } + DEBUG(10,("check_posix_acl_group_access: file %s " + "match on owning group %u -> %s.\n", + fname, (unsigned int)psbuf->st_gid, + ret ? "can access" : "cannot access")); + break; + } + } + + if (cu_gid == (gid_t)-1) { + DEBUG(10,("check_posix_acl_group_access: file %s " + "failed to match on user or group in token (ret = %d).\n", + fname, ret )); + } + } + + done: + + if (posix_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl); + } + + DEBUG(10,("check_posix_acl_group_access: file %s returning (ret = %d).\n", fname, ret )); + return ret; +} + +/**************************************************************************** + Actually emulate the in-kernel access checking for delete access. We need + this to successfully return ACCESS_DENIED on a file open for delete access. +****************************************************************************/ + +bool can_delete_file_in_directory(connection_struct *conn, const char *fname) +{ + SMB_STRUCT_STAT sbuf; + TALLOC_CTX *ctx = talloc_tos(); + char *dname = NULL; + int ret; + + if (!CAN_WRITE(conn)) { + return False; + } + + /* Get the parent directory permission mask and owners. */ + if (!parent_dirname_talloc(ctx, + fname, + &dname, + NULL)) { + return False; + } + if(SMB_VFS_STAT(conn, dname, &sbuf) != 0) { + return False; + } + if (!S_ISDIR(sbuf.st_mode)) { + return False; + } + if (current_user.ut.uid == 0 || conn->admin_user) { + /* I'm sorry sir, I didn't know you were root... */ + return True; + } + + /* Check primary owner write access. */ + if (current_user.ut.uid == sbuf.st_uid) { + return (sbuf.st_mode & S_IWUSR) ? True : False; + } + +#ifdef S_ISVTX + /* sticky bit means delete only by owner or root. */ + if (sbuf.st_mode & S_ISVTX) { + SMB_STRUCT_STAT sbuf_file; + if(SMB_VFS_STAT(conn, fname, &sbuf_file) != 0) { + if (errno == ENOENT) { + /* If the file doesn't already exist then + * yes we'll be able to delete it. */ + return True; + } + return False; + } + /* + * Patch from SATOH Fumiyasu + * for bug #3348. Don't assume owning sticky bit + * directory means write access allowed. + */ + if (current_user.ut.uid != sbuf_file.st_uid) { + return False; + } + } +#endif + + /* Check group or explicit user acl entry write access. */ + ret = check_posix_acl_group_access(conn, dname, &sbuf, FILE_WRITE_DATA); + if (ret == 0 || ret == 1) { + return ret ? True : False; + } + + /* Finally check other write access. */ + return (sbuf.st_mode & S_IWOTH) ? True : False; +} + +/**************************************************************************** + Actually emulate the in-kernel access checking for read/write access. We need + this to successfully check for ability to write for dos filetimes. + Note this doesn't take into account share write permissions. +****************************************************************************/ + +bool can_access_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, uint32 access_mask) +{ + int ret; + + if (!(access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))) { + return False; + } + access_mask &= (FILE_READ_DATA|FILE_WRITE_DATA); + + DEBUG(10,("can_access_file: requesting 0x%x on file %s\n", + (unsigned int)access_mask, fname )); + + if (current_user.ut.uid == 0 || conn->admin_user) { + /* I'm sorry sir, I didn't know you were root... */ + return True; + } + + if (!VALID_STAT(*psbuf)) { + /* Get the file permission mask and owners. */ + if(SMB_VFS_STAT(conn, fname, psbuf) != 0) { + return False; + } + } + + /* Check primary owner access. */ + if (current_user.ut.uid == psbuf->st_uid) { + switch (access_mask) { + case FILE_READ_DATA: + return (psbuf->st_mode & S_IRUSR) ? True : False; + + case FILE_WRITE_DATA: + return (psbuf->st_mode & S_IWUSR) ? True : False; + + default: /* FILE_READ_DATA|FILE_WRITE_DATA */ + + if ((psbuf->st_mode & (S_IWUSR|S_IRUSR)) == (S_IWUSR|S_IRUSR)) { + return True; + } else { + return False; + } + } + } + + /* Check group or explicit user acl entry access. */ + ret = check_posix_acl_group_access(conn, fname, psbuf, access_mask); + if (ret == 0 || ret == 1) { + return ret ? True : False; + } + + /* Finally check other access. */ + switch (access_mask) { + case FILE_READ_DATA: + return (psbuf->st_mode & S_IROTH) ? True : False; + + case FILE_WRITE_DATA: + return (psbuf->st_mode & S_IWOTH) ? True : False; + + default: /* FILE_READ_DATA|FILE_WRITE_DATA */ + + if ((psbuf->st_mode & (S_IWOTH|S_IROTH)) == (S_IWOTH|S_IROTH)) { + return True; + } + } + return False; +} + +/**************************************************************************** + Userspace check for write access. + Note this doesn't take into account share write permissions. +****************************************************************************/ + +bool can_write_to_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf) +{ + return can_access_file(conn, fname, psbuf, FILE_WRITE_DATA); +} + +/******************************************************************** + Pull the NT ACL from a file on disk or the OpenEventlog() access + check. Caller is responsible for freeing the returned security + descriptor via TALLOC_FREE(). This is designed for dealing with + user space access checks in smbd outside of the VFS. For example, + checking access rights in OpenEventlog(). + + Assume we are dealing with files (for now) +********************************************************************/ + +SEC_DESC *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname) +{ + SEC_DESC *psd, *ret_sd; + connection_struct conn; + files_struct finfo; + struct fd_handle fh; + + ZERO_STRUCT( conn ); + + if ( !(conn.mem_ctx = talloc_init( "novfs_get_nt_acl" )) ) { + DEBUG(0,("get_nt_acl_no_snum: talloc() failed!\n")); + return NULL; + } + + if (!(conn.params = TALLOC_P(conn.mem_ctx, struct share_params))) { + DEBUG(0,("get_nt_acl_no_snum: talloc() failed!\n")); + TALLOC_FREE(conn.mem_ctx); + return NULL; + } + + conn.params->service = -1; + + set_conn_connectpath(&conn, "/"); + + if (!smbd_vfs_init(&conn)) { + DEBUG(0,("get_nt_acl_no_snum: Unable to create a fake connection struct!\n")); + conn_free_internal( &conn ); + return NULL; + } + + ZERO_STRUCT( finfo ); + ZERO_STRUCT( fh ); + + finfo.fnum = -1; + finfo.conn = &conn; + finfo.fh = &fh; + finfo.fh->fd = -1; + finfo.fsp_name = CONST_DISCARD(char *,fname); + + if (get_nt_acl( &finfo, DACL_SECURITY_INFORMATION, &psd ) == 0) { + DEBUG(0,("get_nt_acl_no_snum: get_nt_acl returned zero.\n")); + conn_free_internal( &conn ); + return NULL; + } + + ret_sd = dup_sec_desc( ctx, psd ); + + conn_free_internal( &conn ); + + return ret_sd; +}