diff options
Diffstat (limited to 'source3/lib/util_unixsd.c')
-rw-r--r-- | source3/lib/util_unixsd.c | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/source3/lib/util_unixsd.c b/source3/lib/util_unixsd.c new file mode 100644 index 0000000000..1bfc56a2da --- /dev/null +++ b/source3/lib/util_unixsd.c @@ -0,0 +1,679 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + SMB NT Security Descriptor / Unix permission conversion. + Copyright (C) Jeremy Allison 1994-2000 + + 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 + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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. +*/ + +#include "includes.h" +#include "rpc_parse.h" +#include "sids.h" + +#ifndef WITH_SURS +extern DOM_SID global_sid_World; +#define global_sid_everyone &global_sid_World +#endif + + +/**************************************************************************** + Map unix perms to NT. +****************************************************************************/ + +static SEC_ACCESS map_unix_perms(int *pacl_type, mode_t perm, int r_mask, + int w_mask, int x_mask, BOOL is_directory) +{ + SEC_ACCESS sa; + uint32 nt_mask = 0; + + *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED; + + if ((perm & (r_mask | w_mask | x_mask)) == (r_mask | w_mask | x_mask)) + { + nt_mask = UNIX_ACCESS_RWX; + } + else if ((perm & (r_mask | w_mask | x_mask)) == 0) + { + nt_mask = UNIX_ACCESS_NONE; + } + else + { + nt_mask |= (perm & r_mask) ? UNIX_ACCESS_R : 0; + if (is_directory) + nt_mask |= (perm & w_mask) ? UNIX_ACCESS_W : 0; + else + nt_mask |= (perm & w_mask) ? UNIX_ACCESS_W : 0; + nt_mask |= (perm & x_mask) ? UNIX_ACCESS_X : 0; + } + make_sec_access(&sa, nt_mask); + return sa; +} + +/**************************************************************************** + Function to create owner and group SIDs from a SMB_STRUCT_STAT. +****************************************************************************/ + +static BOOL create_file_sids(const SMB_STRUCT_STAT * psbuf, + DOM_SID *powner_sid, DOM_SID *pgroup_sid) +{ + extern DOM_SID global_sam_sid; + + sid_copy(powner_sid, &global_sam_sid); + sid_copy(pgroup_sid, &global_sam_sid); + sid_append_rid(powner_sid, pdb_uid_to_user_rid(psbuf->st_uid)); + sid_append_rid(pgroup_sid, pdb_gid_to_group_rid(psbuf->st_gid)); + return True; +} + +#ifdef WITH_SURS +static BOOL create_file_sids(const SMB_STRUCT_STAT * psbuf, + DOM_SID *powner_sid, DOM_SID *pgroup_sid) +{ + SURS_POSIX_ID id; + + ZERO_STRUCTP(powner_sid); + ZERO_STRUCTP(pgroup_sid); + DEBUG(0, ("TODO: create_file_sids: not ok " + "to assume gid is NT group\n")); + + id.type = SURS_POSIX_UID; + id.id = (uint32)psbuf->st_uid; + + if (!surs_unixid_to_sam_sid(&id, powner_sid, False)) + { + DEBUG(3, ("create_file_sids: map uid %d failed\n", + (int)psbuf->st_uid)); + return False; + } + + id.type = SURS_POSIX_GID; + id.id = (uint32)psbuf->st_gid; + + if (!surs_unixid_to_sam_sid(&id, pgroup_sid, False)) + { + DEBUG(3, ("create_file_sids: map gid %d failed\n", + (int)psbuf->st_gid)); + return False; + } + return True; +} +#endif + +/**************************************************************************** + Reply to query a security descriptor from an fsp. If it succeeds it allocates + the space for the return elements and returns True. +****************************************************************************/ + +size_t convertperms_unix_to_sd(const SMB_STRUCT_STAT * sbuf, + BOOL is_directory, mode_t mode, + SEC_DESC ** ppdesc) +{ + SEC_ACE *ace_list = NULL; + DOM_SID owner_sid; + DOM_SID group_sid; + size_t sec_desc_size; + SEC_ACL *psa = NULL; + SEC_ACCESS owner_access; + int owner_acl_type; + SEC_ACCESS group_access; + int grp_acl_type; + SEC_ACCESS other_access; + int other_acl_type; + int num_acls = 0; + + (*ppdesc) = NULL; + + if (!lp_nt_acl_support()) + { + sid_copy(&owner_sid, global_sid_everyone); + sid_copy(&group_sid, global_sid_everyone); + } + else + { + if (!create_file_sids(sbuf, &owner_sid, &group_sid)) + { + DEBUG(3, ("create_file_sids: uid or gid " + "not mapped to SIDS\n")); + return 0; + } + + /* + * Create the generic 3 element UNIX acl. + */ + + owner_access = map_unix_perms(&owner_acl_type, sbuf->st_mode, + S_IRUSR, S_IWUSR, S_IXUSR, + is_directory); + group_access = map_unix_perms(&grp_acl_type, sbuf->st_mode, + S_IRGRP, S_IWGRP, S_IXGRP, + is_directory); + other_access = map_unix_perms(&other_acl_type, sbuf->st_mode, + S_IROTH, S_IWOTH, S_IXOTH, + is_directory); + + if (owner_access.mask) + { + ace_list = g_renew(SEC_ACE, ace_list, num_acls + 1); + if (ace_list == NULL) + { + return 0; + } + make_sec_ace(&ace_list[num_acls++], &owner_sid, + owner_acl_type, owner_access, 0); + } + + if (group_access.mask) + { + ace_list = g_renew(SEC_ACE, ace_list, num_acls + 1); + if (ace_list == NULL) + { + return 0; + } + + make_sec_ace(&ace_list[num_acls++], &group_sid, + grp_acl_type, group_access, 0); + } + + if (other_access.mask) + { + ace_list = g_renew(SEC_ACE, ace_list, num_acls + 1); + if (ace_list == NULL) + { + return 0; + } + + make_sec_ace(&ace_list[num_acls++], + global_sid_everyone, other_acl_type, + other_access, 0); + } + + if (is_directory) + { + /* + * For directory ACLs we also add in the + * inherited permissions ACE entries. These + * are the permissions a file would get when + * being created in the directory. + */ + + owner_access = map_unix_perms(&owner_acl_type, mode, + S_IRUSR, S_IWUSR, + S_IXUSR, is_directory); + group_access = map_unix_perms(&grp_acl_type, + mode, S_IRGRP, + S_IWGRP, S_IXGRP, + is_directory); + other_access = map_unix_perms(&other_acl_type, + mode, S_IROTH, + S_IWOTH, S_IXOTH, + is_directory); + + if (owner_access.mask) + { + ace_list = g_renew(SEC_ACE, ace_list, + num_acls + 1); + if (ace_list == NULL) + { + return 0; + } + + make_sec_ace(&ace_list[num_acls++], + &owner_sid, owner_acl_type, + owner_access, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_INHERIT_ONLY); + } + + if (group_access.mask) + { + ace_list = g_renew(SEC_ACE, ace_list, + num_acls + 1); + if (ace_list == NULL) + { + return 0; + } + + make_sec_ace(&ace_list[num_acls++], + &group_sid, grp_acl_type, + group_access, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_INHERIT_ONLY); + } + + if (other_access.mask) + { + ace_list = g_renew(SEC_ACE, ace_list, + num_acls + 1); + if (ace_list == NULL) + { + return 0; + } + + make_sec_ace(&ace_list[num_acls++], + global_sid_everyone, + other_acl_type, other_access, + SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_INHERIT_ONLY); + } + } + + if (num_acls) + { + psa = g_new(SEC_ACL, 1); + if (psa == NULL) + { + safe_free(ace_list); + } + if (!make_sec_acl(psa, 2, num_acls, ace_list)) + { + DEBUG(0, ("get_nt_acl: Unable to malloc " + "space for acl.\n")); + safe_free(ace_list); + safe_free(psa); + return 0; + } + } + } + + (*ppdesc) = g_new(SEC_DESC, 1); + + if ((*ppdesc) == NULL) + { + DEBUG(0, ("get_nt_acl: Unable to malloc space " + "for security descriptor.\n")); + sec_desc_size = 0; + free_sec_acl(psa); + safe_free(psa); + return 0; + } + + sec_desc_size = make_sec_desc((*ppdesc), 1, + SEC_DESC_SELF_RELATIVE | + SEC_DESC_DACL_PRESENT, + sid_dup(&owner_sid), + sid_dup(&group_sid), NULL, psa); + + return sec_desc_size; +} + +/**************************************************************************** + Map NT perms to UNIX. +****************************************************************************/ + +#define FILE_SPECIFIC_READ_BITS \ + (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES) +#define FILE_SPECIFIC_WRITE_BITS \ + (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES) +#define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE) + +#define PRINT_SPECIFIC_READ_BITS (PRINTER_READ) +#define PRINT_SPECIFIC_WRITE_BITS (PRINTER_READ) +#define PRINT_SPECIFIC_EXECUTE_BITS (PRINTER_ALL_ACCESS) + +static mode_t map_nt_perms(SEC_ACCESS sec_access, int type) +{ + uint32 write_bits; + uint32 read_bits; + uint32 execute_bits; + mode_t mode = 0; + + write_bits = FILE_SPECIFIC_WRITE_BITS; + read_bits = FILE_SPECIFIC_READ_BITS; + execute_bits = FILE_SPECIFIC_EXECUTE_BITS; + + switch (type) + { + case S_IRUSR: + if (sec_access.mask & GENERIC_ALL_ACCESS) + mode = S_IRUSR | S_IWUSR | S_IXUSR; + else + { + mode |= + (sec_access.mask & + (GENERIC_READ_ACCESS | + read_bits)) ? S_IRUSR : 0; + mode |= + (sec_access.mask & + (GENERIC_WRITE_ACCESS | + write_bits)) ? S_IWUSR : 0; + mode |= + (sec_access.mask & + (GENERIC_EXECUTE_ACCESS | + execute_bits)) ? S_IXUSR : 0; + } + break; + case S_IRGRP: + if (sec_access.mask & GENERIC_ALL_ACCESS) + mode = S_IRGRP | S_IWGRP | S_IXGRP; + else + { + mode |= + (sec_access.mask & + (GENERIC_READ_ACCESS | + read_bits)) ? S_IRGRP : 0; + mode |= + (sec_access.mask & + (GENERIC_WRITE_ACCESS | + write_bits)) ? S_IWGRP : 0; + mode |= + (sec_access.mask & + (GENERIC_EXECUTE_ACCESS | + execute_bits)) ? S_IXGRP : 0; + } + break; + case S_IROTH: + if (sec_access.mask & GENERIC_ALL_ACCESS) + mode = S_IROTH | S_IWOTH | S_IXOTH; + else + { + mode |= + (sec_access.mask & + (GENERIC_READ_ACCESS | + read_bits)) ? S_IROTH : 0; + mode |= + (sec_access.mask & + (GENERIC_WRITE_ACCESS | + write_bits)) ? S_IWOTH : 0; + mode |= + (sec_access.mask & + (GENERIC_EXECUTE_ACCESS | + execute_bits)) ? S_IXOTH : 0; + } + break; + } + + return mode; +} + +#ifndef WITH_SURS +/**************************************************************************** + Validate a SID. +****************************************************************************/ +static BOOL validate_unix_sid(DOM_SID *psid, uint32 *prid, DOM_SID *sd_sid) +{ + extern DOM_SID global_sam_sid; + DOM_SID sid; + + if (!sd_sid) + { + DEBUG(5, ("validate_unix_sid: sid missing.\n")); + return False; + } + + sid_copy(psid, sd_sid); + sid_copy(&sid, sd_sid); + + if (!sid_split_rid(&sid, prid)) + { + DEBUG(5, ("validate_unix_sid: cannot get RID from sid.\n")); + return False; + } + + if (!sid_equal(&sid, &global_sam_sid)) + { + DEBUG(5, ("validate_unix_sid: sid is not ours.\n")); + return False; + } + + return True; +} +#endif + +/**************************************************************************** + Unpack a SEC_DESC into a owner, group and set of UNIX permissions. +****************************************************************************/ + +BOOL convertperms_sd_to_unix(SMB_STRUCT_STAT * psbuf, uid_t * puser, + gid_t * pgrp, mode_t * pmode, + uint32 security_info_sent, SEC_DESC * psd, + BOOL is_directory) +{ + DOM_SID file_owner_sid; + DOM_SID file_grp_sid; + SEC_ACL *dacl = psd->dacl; + BOOL all_aces_are_inherit_only = (is_directory ? True : False); + int i; +#ifdef WITH_SURS + SURS_POSIX_ID id; +#else + DOM_SID owner_sid; + DOM_SID grp_sid; + uint32 owner_rid; + uint32 grp_rid; +#endif + + *pmode = 0; + *puser = (uid_t) - 1; + *pgrp = (gid_t) - 1; + + if (security_info_sent == 0) + { + DEBUG(0, ("unpack_nt_permissions: " + "no security info sent !\n")); + return False; + } + + /* + * Windows 2000 sends the owner and group SIDs as the logged in + * user, not the connected user. But it still sends the file + * owner SIDs on an ACL set. So we need to check for the file + * owner and group SIDs as well as the owner SIDs. JRA. + */ + + if (!create_file_sids(psbuf, &file_owner_sid, &file_grp_sid)) + { + DEBUG(3, ("create_file_sids: uid or gid " + "not mapped to SIDS\n")); + return 0; + } + + /* + * Don't immediately fail if the owner sid cannot be validated. + * This may be a group chown only set. + */ + + DEBUG(0, ("TODO: LsaLookupSids to find type of owner_sid\n")); + +#ifdef WITH_SURS + if (security_info_sent & OWNER_SECURITY_INFORMATION && + surs_sam_sid_to_unixid(psd->owner_sid, &id, False) && + id.type == SURS_POSIX_UID) + { + *puser = (uid_t) id.id; + } + +#else + if (!validate_unix_sid(&owner_sid, &owner_rid, psd->owner_sid)) + DEBUG(3, + ("unpack_nt_permissions: unable to validate owner sid.\n")); + else if (security_info_sent & OWNER_SECURITY_INFORMATION) + *puser = pdb_user_rid_to_uid(owner_rid); + + if (security_info_sent & OWNER_SECURITY_INFORMATION) + { + *puser = pdb_user_rid_to_uid(owner_rid); + } + +#endif + /* + * Don't immediately fail if the group sid cannot be validated. + * This may be an owner chown only set. + */ + +#ifdef WITH_SURS + if (security_info_sent & GROUP_SECURITY_INFORMATION && + surs_sam_sid_to_unixid(psd->grp_sid, &id, False) && + (id.type == SURS_POSIX_GID)) + { + *pgrp = (gid_t) id.id; + } +#else + if (!validate_unix_sid(&grp_sid, &grp_rid, psd->grp_sid)) + DEBUG(3, + ("unpack_nt_permissions: unable to validate group sid.\n")); + else if (security_info_sent & GROUP_SECURITY_INFORMATION) + *pgrp = pdb_user_rid_to_gid(grp_rid); + +#endif + /* + * If no DACL then this is a chown only security descriptor. + */ + + if (!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl) + { + *pmode = 0; + return True; + } + + /* + * Now go through the DACL and ensure that + * any owner/group sids match. + */ + + for (i = 0; i < dacl->num_aces; i++) + { + DOM_SID ace_sid; + SEC_ACE *psa = &dacl->ace[i]; + + if ((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && + (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) + { + DEBUG(3, ("unpack_nt_permissions: " + "unable to set anything but an " + "ALLOW or DENY ACE.\n")); + return False; + } + + /* + * Ignore or remove bits we don't care about on a directory ACE. + */ + + if (is_directory) + { + if (psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) + { + DEBUG(3, ("unpack_nt_permissions: " + "ignoring inherit only ACE.\n")); + continue; + } + + /* + * At least one of the ACE entries wasn't inherit only. + * Flag this so we know the returned mode is valid. + */ + + all_aces_are_inherit_only = False; + } + + /* + * Windows 2000 sets these flags even on *file* ACE's. + * This is wrong but we can ignore them for now. + * Revisit this when we go to POSIX ACLs on directories. + */ + + psa->flags &= + ~(SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT); + + if (psa->flags != 0) + { + DEBUG(1, + ("unpack_nt_permissions: unable to set ACE flags (%x).\n", + (unsigned int)psa->flags)); + return False; + } + + /* + * The security mask may be UNIX_ACCESS_NONE which + * should map into no permissions (we overload the + * WRITE_OWNER bit for this) or it should be one of + * the ALL/EXECUTE/READ/WRITE bits. Arrange for this + * to be so. Any other bits override the + * UNIX_ACCESS_NONE bit. + */ + + psa->info.mask &= + (GENERIC_ALL_ACCESS | GENERIC_EXECUTE_ACCESS | + GENERIC_WRITE_ACCESS | GENERIC_READ_ACCESS | + UNIX_ACCESS_NONE | FILE_ALL_ATTRIBUTES); + + if (psa->info.mask != UNIX_ACCESS_NONE) + psa->info.mask &= ~UNIX_ACCESS_NONE; + + sid_copy(&ace_sid, &psa->sid); + + if (sid_equal(&ace_sid, &file_owner_sid)) + { + /* + * Map the desired permissions into owner perms. + */ + + if (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) + *pmode |= map_nt_perms(psa->info, S_IRUSR); + else + *pmode &= ~(map_nt_perms(psa->info, S_IRUSR)); + + } + else if (sid_equal(&ace_sid, &file_grp_sid)) + { + /* + * Map the desired permissions into group perms. + */ + + if (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) + *pmode |= map_nt_perms(psa->info, S_IRGRP); + else + *pmode &= ~(map_nt_perms(psa->info, S_IRGRP)); + + } + else if (sid_equal(&ace_sid, global_sid_everyone)) + { + /* + * Map the desired permissions into other perms. + */ + + if (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) + *pmode |= map_nt_perms(psa->info, S_IROTH); + else + *pmode &= ~(map_nt_perms(psa->info, S_IROTH)); + + } + else + { + DEBUG(0, ("unpack_nt_permissions: " + "unknown SID used in ACL.\n")); + return False; + } + } + + if (is_directory && all_aces_are_inherit_only) + { + /* + * Windows 2000 is doing one of these weird 'inherit acl' + * traverses to conserve NTFS ACL resources. Just pretend + * there was no DACL sent. JRA. + */ + + DEBUG(10, ("unpack_nt_permissions: " + "Win2k inherit acl traverse. " + "Ignoring DACL.\n")); + free_sec_acl(psd->dacl); + safe_free(psd->dacl); + psd->dacl = NULL; + } + + return True; +} |