gid_to_sid( pgroup_sid, psbuf->st_ex_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
if the permissions become zero, delete the deny if the permissions are non zero.
****************************************************************************/
-static void merge_aces( canon_ace **pp_list_head )
+static void merge_aces( canon_ace **pp_list_head, bool dir_acl)
{
canon_ace *l_head = *pp_list_head;
canon_ace *curr_ace_outer;
curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
+ bool can_merge = false;
curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
- if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
- (curr_ace->attr == curr_ace_outer->attr)) {
+ /* For file ACLs we can merge if the SIDs and ALLOW/DENY
+ * types are the same. For directory acls we must also
+ * ensure the POSIX ACL types are the same. */
+ if (!dir_acl) {
+ can_merge = (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+ (curr_ace->attr == curr_ace_outer->attr));
+ } else {
+ can_merge = (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+ (curr_ace->type == curr_ace_outer->type) &&
+ (curr_ace->attr == curr_ace_outer->attr));
+ }
+
+ if (can_merge) {
if( DEBUGLVL( 10 )) {
dbgtext("merge_aces: Merging ACE's\n");
print_canon_ace( curr_ace_outer, 0);
/* Merge two allow or two deny ACE's. */
+ /* Theoretically we shouldn't merge a dir ACE if
+ * one ACE has the CI flag set, and the other
+ * ACE has the OI flag set, but this is rare
+ * enough we can ignore it. */
+
curr_ace_outer->perms |= curr_ace->perms;
+ curr_ace_outer->ace_flags |= curr_ace->ace_flags;
DLIST_REMOVE(l_head, curr_ace);
SAFE_FREE(curr_ace);
curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
* we've put on the ACL, we know the deny must be the first one.
*/
- if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
+ if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
(curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
if( DEBUGLVL( 10 )) {
not get. Deny entries are implicit on get with ace->perms = 0.
****************************************************************************/
-static uint32_t map_canon_ace_perms(int snum,
+uint32_t map_canon_ace_perms(int snum,
enum security_ace_type *pacl_type,
mode_t perms,
bool directory_ace)
}
}
+ if ((perms & S_IWUSR) && lp_dos_filemode(snum)) {
+ nt_mask |= (SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER|DELETE_ACCESS);
+ }
+
DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
(unsigned int)perms, (unsigned int)nt_mask ));
DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
}
+/****************************************************************************
+ If an ACE entry is SMB_ACL_USER_OBJ and not CREATOR_OWNER, map to SMB_ACL_USER.
+ If an ACE entry is SMB_ACL_GROUP_OBJ and not CREATOR_GROUP, map to SMB_ACL_GROUP
+****************************************************************************/
+
+static bool dup_owning_ace(canon_ace *dir_ace, canon_ace *ace)
+{
+ /* dir ace must be followings.
+ SMB_ACL_USER_OBJ : trustee(CREATOR_OWNER) -> Posix ACL d:u::perm
+ SMB_ACL_USER : not trustee -> Posix ACL u:user:perm
+ SMB_ACL_USER_OBJ : trustee -> convert to SMB_ACL_USER : trustee
+ Posix ACL u:trustee:perm
+
+ SMB_ACL_GROUP_OBJ: trustee(CREATOR_GROUP) -> Posix ACL d:g::perm
+ SMB_ACL_GROUP : not trustee -> Posix ACL g:group:perm
+ SMB_ACL_GROUP_OBJ: trustee -> convert to SMB_ACL_GROUP : trustee
+ Posix ACL g:trustee:perm
+ */
+
+ if (ace->type == SMB_ACL_USER_OBJ &&
+ !(sid_equal(&ace->trustee, &global_sid_Creator_Owner))) {
+ canon_ace *dup_ace = dup_canon_ace(ace);
+
+ if (dup_ace == NULL) {
+ return false;
+ }
+ dup_ace->type = SMB_ACL_USER;
+ DLIST_ADD_END(dir_ace, dup_ace, canon_ace *);
+ }
+
+ if (ace->type == SMB_ACL_GROUP_OBJ &&
+ !(sid_equal(&ace->trustee, &global_sid_Creator_Group))) {
+ canon_ace *dup_ace = dup_canon_ace(ace);
+
+ if (dup_ace == NULL) {
+ return false;
+ }
+ dup_ace->type = SMB_ACL_GROUP;
+ DLIST_ADD_END(dir_ace, dup_ace, canon_ace *);
+ }
+
+ return true;
+}
+
/****************************************************************************
Unpack a SEC_DESC into two canonical ace lists.
****************************************************************************/
static bool create_canon_ace_lists(files_struct *fsp,
- SMB_STRUCT_STAT *pst,
+ const SMB_STRUCT_STAT *pst,
DOM_SID *pfile_owner_sid,
DOM_SID *pfile_grp_sid,
canon_ace **ppfile_ace,
/*
* The Creator Owner entry only specifies inheritable permissions,
* never access permissions. WinNT doesn't always set the ACE to
- *INHERIT_ONLY, though.
+ * INHERIT_ONLY, though.
*/
- if (nt4_compatible_acls())
- psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+
} else if (sid_equal(¤t_ace->trustee, &global_sid_Creator_Group)) {
current_ace->owner_type = GID_ACE;
current_ace->unix_ug.gid = pst->st_ex_gid;
/*
* The Creator Group entry only specifies inheritable permissions,
* never access permissions. WinNT doesn't always set the ACE to
- *INHERIT_ONLY, though.
+ * INHERIT_ONLY, though.
*/
- if (nt4_compatible_acls())
- psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
} else if (sid_to_uid( ¤t_ace->trustee, ¤t_ace->unix_ug.uid)) {
current_ace->owner_type = UID_ACE;
continue;
}
+ if (lp_force_unknown_acl_user(SNUM(fsp->conn))) {
+ DEBUG(10, ("create_canon_ace_lists: ignoring "
+ "unknown or foreign SID %s\n",
+ sid_string_dbg(&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 "
print_canon_ace( current_ace, 0);
}
+ /*
+ * We have a lossy mapping: directory ACE entries
+ * CREATOR_OWNER ------\
+ * (map to) +---> SMB_ACL_USER_OBJ
+ * owning sid ------/
+ *
+ * CREATOR_GROUP ------\
+ * (map to) +---> SMB_ACL_GROUP_OBJ
+ * primary group sid --/
+ *
+ * on set. And on read of a directory ACL
+ *
+ * SMB_ACL_USER_OBJ ----> CREATOR_OWNER
+ * SMB_ACL_GROUP_OBJ ---> CREATOR_GROUP.
+ *
+ * Deal with this on set by duplicating
+ * owning sid and primary group sid ACE
+ * entries into the directory ACL.
+ * Fix from Tsukasa Hamano <hamano@osstech.co.jp>.
+ */
+
+ if (!dup_owning_ace(dir_ace, current_ace)) {
+ DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return false;
+ }
+
/*
* If this is not an inherit only ACE we need to add a duplicate
* to the file acl.
* pointer is now owned by the dir_ace list.
*/
current_ace = dup_ace;
+ /* We've essentially split this ace into two,
+ * and added the ace with inheritance request
+ * bits to the directory ACL. Drop those bits for
+ * the ACE we're adding to the file list. */
+ current_ace->ace_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|
+ SEC_ACE_FLAG_CONTAINER_INHERIT|
+ SEC_ACE_FLAG_INHERIT_ONLY);
} else {
/*
* We must not free current_ace here as its
****************************************************************************/
static bool unpack_canon_ace(files_struct *fsp,
- SMB_STRUCT_STAT *pst,
+ const SMB_STRUCT_STAT *pst,
DOM_SID *pfile_owner_sid,
DOM_SID *pfile_grp_sid,
canon_ace **ppfile_ace,
uint32 security_info_sent,
const SEC_DESC *psd)
{
+ SMB_STRUCT_STAT st;
canon_ace *file_ace = NULL;
canon_ace *dir_ace = NULL;
*/
print_canon_ace_list( "file ace - before merge", file_ace);
- merge_aces( &file_ace );
+ merge_aces( &file_ace, false);
print_canon_ace_list( "dir ace - before merge", dir_ace);
- merge_aces( &dir_ace );
+ merge_aces( &dir_ace, true);
/*
* NT ACLs are order dependent. Go through the acl lists and
print_canon_ace_list( "file ace - before valid", file_ace);
+ st = *pst;
+
/*
* A default 3 element mode entry for a file should be r-- --- ---.
* A default 3 element mode entry for a directory should be rwx --- ---.
*/
- pst->st_ex_mode = create_default_mode(fsp, False);
+ st.st_ex_mode = create_default_mode(fsp, False);
- if (!ensure_canon_entry_valid(&file_ace, fsp->conn->params, fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+ if (!ensure_canon_entry_valid(&file_ace, fsp->conn->params,
+ fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
* it's a directory.
*/
- pst->st_ex_mode = create_default_mode(fsp, True);
+ st.st_ex_mode = create_default_mode(fsp, True);
- if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp->conn->params, fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+ if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp->conn->params,
+ fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
continue;
}
- /*
- * A SMB_ACL_USER entry for the owner is shadowed by the
- * SMB_ACL_USER_OBJ entry and Windows also cannot represent
- * that entry, so we ignore it. We also don't create such
- * 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_ex_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;
Check if the current user group list contains a given group.
****************************************************************************/
-static bool current_user_in_group(gid_t gid)
+bool current_user_in_group(gid_t gid)
{
int i;
mode_t mask_perms = 0;
/* Use the psbuf that was passed in. */
- fsp->fsp_name->st = *psbuf;
+ if (psbuf != &fsp->fsp_name->st) {
+ fsp->fsp_name->st = *psbuf;
+ }
#if defined(POSIX_ACL_NEEDS_MASK)
/* HP-UX always wants to have a mask (called "class" there). */
SMB_ACL_T posix_acl = NULL;
SMB_ACL_T def_acl = NULL;
struct pai_val *pal;
- struct smb_filename *smb_fname = NULL;
- NTSTATUS status;
+ struct smb_filename smb_fname;
int ret;
*ppdesc = NULL;
DEBUG(10,("posix_get_nt_acl: called for file %s\n", name ));
- status = create_synthetic_smb_fname(talloc_tos(), name, NULL, NULL,
- &smb_fname);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
+ ZERO_STRUCT(smb_fname);
+ smb_fname.base_name = discard_const_p(char, name);
/* Get the stat struct for the owner info. */
if (lp_posix_pathnames()) {
- ret = SMB_VFS_LSTAT(conn, smb_fname);
+ ret = SMB_VFS_LSTAT(conn, &smb_fname);
} else {
- ret = SMB_VFS_STAT(conn, smb_fname);
+ ret = SMB_VFS_STAT(conn, &smb_fname);
}
if (ret == -1) {
- status = map_nt_error_from_unix(errno);
- goto out;
+ return map_nt_error_from_unix(errno);
}
/* Get the ACL from the path. */
posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_ACCESS);
/* If it's a directory get the default POSIX ACL. */
- if(S_ISDIR(smb_fname->st.st_ex_mode)) {
+ if(S_ISDIR(smb_fname.st.st_ex_mode)) {
def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_DEFAULT);
def_acl = free_empty_sys_acl(conn, def_acl);
}
pal = load_inherited_info(conn, name);
- status = posix_get_nt_acl_common(conn, name, &smb_fname->st, pal,
- posix_acl, def_acl, security_info,
- ppdesc);
- out:
- TALLOC_FREE(smb_fname);
- return status;
+ return posix_get_nt_acl_common(conn, name, &smb_fname.st, pal,
+ posix_acl, def_acl, security_info,
+ ppdesc);
}
/****************************************************************************
return -1;
}
- if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname, &fsp))) {
+ if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname, &fsp))) {
return -1;
}
become_root();
/* Keep the current file gid the same. */
- ret = SMB_VFS_FCHOWN(fsp, uid, (gid_t)-1);
+ if (fsp->fh->fd == -1) {
+ if (lp_posix_pathnames()) {
+ ret = SMB_VFS_LCHOWN(conn, smb_fname->base_name, uid,
+ (gid_t)-1);
+ } else {
+ ret = SMB_VFS_CHOWN(conn, smb_fname->base_name, uid,
+ (gid_t)-1);
+ }
+ } else {
+ ret = SMB_VFS_FCHOWN(fsp, uid, (gid_t)-1);
+ }
unbecome_root();
- close_file_fchmod(NULL, fsp);
+ close_file(NULL, fsp, NORMAL_CLOSE);
return ret;
}
This should be the only external function needed for the UNIX style set ACL.
****************************************************************************/
-NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC *psd)
+NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC *psd_orig)
{
connection_struct *conn = fsp->conn;
uid_t user = (uid_t)-1;
bool set_acl_as_root = false;
bool acl_set_support = false;
bool ret = false;
+ SEC_DESC *psd = NULL;
DEBUG(10,("set_nt_acl: called for file %s\n",
fsp_str_dbg(fsp)));
return NT_STATUS_MEDIA_WRITE_PROTECTED;
}
+ if (!psd_orig) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ psd = dup_sec_desc(talloc_tos(), psd_orig);
+ if (!psd) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
/*
* Get the current state of the file.
*/
* Unpack the user/group/world id's.
*/
+ /* POSIX can't cope with missing owner/group. */
+ if ((security_info_sent & SECINFO_OWNER) && (psd->owner_sid == NULL)) {
+ security_info_sent &= ~SECINFO_OWNER;
+ }
+ if ((security_info_sent & SECINFO_GROUP) && (psd->group_sid == NULL)) {
+ security_info_sent &= ~SECINFO_GROUP;
+ }
+
status = unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd);
if (!NT_STATUS_IS_OK(status)) {
return status;
create_file_sids(&fsp->fsp_name->st, &file_owner_sid, &file_grp_sid);
+ if((security_info_sent & SECINFO_DACL) &&
+ (psd->type & SEC_DESC_DACL_PRESENT) &&
+ (psd->dacl == NULL)) {
+ SEC_ACE ace[3];
+
+ /* We can't have NULL DACL in POSIX.
+ Use owner/group/Everyone -> full access. */
+
+ init_sec_ace(&ace[0],
+ &file_owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ GENERIC_ALL_ACCESS,
+ 0);
+ init_sec_ace(&ace[1],
+ &file_grp_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ GENERIC_ALL_ACCESS,
+ 0);
+ init_sec_ace(&ace[2],
+ &global_sid_World,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ GENERIC_ALL_ACCESS,
+ 0);
+ psd->dacl = make_sec_acl(talloc_tos(),
+ NT4_ACL_REVISION,
+ 3,
+ ace);
+ if (psd->dacl == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ security_acl_map_generic(psd->dacl, &file_generic_mapping);
+ }
+
acl_perms = unpack_canon_ace(fsp, &fsp->fsp_name->st, &file_owner_sid,
&file_grp_sid, &file_ace_list,
&dir_ace_list, security_info_sent, psd);
free_canon_ace_list(file_ace_list);
free_canon_ace_list(dir_ace_list);
+ /* Ensure the stat struct in the fsp is correct. */
+ status = vfs_stat_fsp(fsp);
+
return NT_STATUS_OK;
}
return ret_sd;
}
+
+/* Stolen shamelessly from pvfs_default_acl() in source4 :-). */
+
+NTSTATUS make_default_filesystem_acl(TALLOC_CTX *ctx,
+ const char *name,
+ SMB_STRUCT_STAT *psbuf,
+ SEC_DESC **ppdesc)
+{
+ struct dom_sid owner_sid, group_sid;
+ size_t size = 0;
+ SEC_ACE aces[4];
+ uint32_t access_mask = 0;
+ mode_t mode = psbuf->st_ex_mode;
+ SEC_ACL *new_dacl = NULL;
+ int idx = 0;
+
+ DEBUG(10,("make_default_filesystem_acl: file %s mode = 0%o\n",
+ name, (int)mode ));
+
+ uid_to_sid(&owner_sid, psbuf->st_ex_uid);
+ gid_to_sid(&group_sid, psbuf->st_ex_gid);
+
+ /*
+ We provide up to 4 ACEs
+ - Owner
+ - Group
+ - Everyone
+ - NT System
+ */
+
+ if (mode & S_IRUSR) {
+ if (mode & S_IWUSR) {
+ access_mask |= SEC_RIGHTS_FILE_ALL;
+ } else {
+ access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ }
+ if (mode & S_IWUSR) {
+ access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
+ }
+
+ init_sec_ace(&aces[idx],
+ &owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ access_mask,
+ 0);
+ idx++;
+
+ access_mask = 0;
+ if (mode & S_IRGRP) {
+ access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ if (mode & S_IWGRP) {
+ /* note that delete is not granted - this matches posix behaviour */
+ access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (access_mask) {
+ init_sec_ace(&aces[idx],
+ &group_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ access_mask,
+ 0);
+ idx++;
+ }
+
+ access_mask = 0;
+ if (mode & S_IROTH) {
+ access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ if (mode & S_IWOTH) {
+ access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (access_mask) {
+ init_sec_ace(&aces[idx],
+ &global_sid_World,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ access_mask,
+ 0);
+ idx++;
+ }
+
+ init_sec_ace(&aces[idx],
+ &global_sid_System,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_RIGHTS_FILE_ALL,
+ 0);
+ idx++;
+
+ new_dacl = make_sec_acl(ctx,
+ NT4_ACL_REVISION,
+ idx,
+ aces);
+
+ if (!new_dacl) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *ppdesc = make_sec_desc(ctx,
+ SECURITY_DESCRIPTOR_REVISION_1,
+ SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT,
+ &owner_sid,
+ &group_sid,
+ NULL,
+ new_dacl,
+ &size);
+ if (!*ppdesc) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}