mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-01-29 18:40:25 +08:00
bc444e5aa4
- New, unified implementation of POSIX permission and ACL handling. The new ACLs now store the POSIX ACL MASK/CLASS_OBJ permission mask, and they allow to inherit the S_ISGID bit. ACL inheritance now really works as desired, in a limited, but theoretically equivalent fashion even for non-Cygwin processes. To accommodate Windows default ACLs, the new code ignores SYSTEM and Administrators group permissions when computing the MASK/CLASS_OBJ permission mask on old ACLs, and it doesn't deny access to SYSTEM and Administrators group based on the value of MASK/CLASS_OBJ when creating the new ACLs. The new code now handles the S_ISGID bit on directories as on Linux: Setting S_ISGID on a directory causes new files and subdirs created within to inherit its group, rather than the primary group of the user who created the file. This only works for files and directories created by Cygwin processes. 2015-05-29 Corinna Vinschen <corinna@vinschen.de> Reapply POSIX ACL changes. * utils.xml (setfacl): Show new option output. (getfacl): Show new option output. * sec_acl.cc (get_posix_access): Check for Cygwin "standard" ACL. Apply umask, if so. Align comments. * security.cc (set_created_file_access): Fix permission masking by incoming requested file mode. * sec_acl.cc (set_posix_access): Apply mask only in terms of execute bit for SYSTEM and Admins group. * sec_acl.cc (set_posix_access): Don't create DENY ACEs for USER and GROUP entries if they are the same as USER_OBJ or GROUP_OBJ. * fhandler.h (fhandler_pty_slave::facl): Add prototype. * fhandler_tty.cc (fhandler_pty_slave::facl): New method. (fhandler_pty_slave::fchown): Fix uid/gid handling. * sec_acl.cc (set_posix_access): Drop superfluous class_idx variable. Simplify and move around code in a few places. To improve ACL readability, add r/w permissions to Admins ACE appended to pty ACL. Add comment to explain Windows ACE Mask filtering being in the way of creating a real CLASS_OBJ. (get_posix_access): Fake CLASS_OBJ for ptys. Explain why. * security.cc (get_object_attribute): Add S_IFCHR flag to attributes when calling get_posix_access. * sec_acl.cc (set_posix_access): Move merging group perms into owner perms in case of owner == group after mask has been computed. Take mask into account when doing so to avoid unnecessary ACCESS_DENIED_ACE. * sec_acl.cc (get_posix_access): Only set saw_group_obj flag if we saw the ACCESS_ALLOWED_ACE. * fhandler_disk_file.cc (fhandler_disk_file::fchmod): Deliberatly set GROUP_OBJ and CLASS_OBJ perms to new group perms. Add comment to explain why. * security.cc (set_created_file_access): Ditto. * sec_acl.cc (set_posix_access): Replace previous patch. Return EINVAL if uid and/or guid is invalid and not backed by an actual Windows account. * sec_acl.cc (set_posix_access): Workaround owner/group SIDs being NULL. * sec_acl.cc (set_posix_access): Handle files with owner == group. Rephrase switch statement checking against unfiltered a_type value. (get_posix_access): Handle files with owner == group. * sec_acl.cc (get_posix_access): Don't use GROUP_OBJ access to fix up CLASS_OBJ mask on old-style ACLs. Fix a comment. * sec_acl.cc (set_posix_access): Always make sure Admins have WRITE_DAC and WRITE_OWNER permissions. * security.h (create_object_sd_from_attribute): Drop handle parameter from prototype. * security.cc (create_object_sd_from_attribute): Drop handle parameter. Just create the standard POSIXy security descriptor. (set_object_attribute): Accommodate dropped paramter in call to create_object_sd_from_attribute. * fhandler_tty.cc: Ditto, throughout. * fhandler_disk_file.cc (fhandler_disk_file::fchmod): Fix typo in mask computation. * fhandler.cc (fhandler_base::open_with_arch): Call open with mode not umasked. (fhandler_base::open): Explicitely umask mode on NFS here. Call new set_created_file_access rather than set_file_attribute. * fhandler_disk_file.cc (fhandler_disk_file::fchmod): Reimplement setting permissions on filesystems supporting ACLs using the new set_posix_access call. (fhandler_disk_file::fchown): Ditto. (fhandler_disk_file::mkdir): Call new set_created_file_access rather than set_file_attribute. * fhandler_socket.cc (fhandler_socket::bind): Don't umask here. Add WRITE_OWNER access to allow writing group in case of SGID bit set. Call new set_created_file_access rather than set_file_attribute. * path.cc (symlink_worker): Call new set_created_file_access rather than set_file_attribute. * sec_acl.cc (searchace): Un-staticize. (set_posix_access): New, complementary functionality to get_posix_access. (setacl): Implement in terms of get_posix_access/set_posix_access. (get_posix_access): Add handling for just created files requiring their first Cygwin ACL. Fix new_style recognition. Handle SGID bit. For old-style ACLs, ignore SYSTEM and Administrators when computing the {DEF_}CLASS_OBJ perms. * security.cc (get_file_sd): Revamp comment. Change and (hopefully) speed up inheritance processing for just created files. (alloc_sd): Remove. (set_security_attribute): Call set_posix_access instead of alloc_sd. (get_object_attribute): Fix return value. (create_object_sd_from_attribute): Call set_posix_access instead of alloc_sd. (set_file_attribute): Remove. (set_created_file_access): New function implemented in terms of get_posix_access/set_posix_access. * security.h (set_file_attribute): Remove prototype. (set_created_file_access): Add prototype. (searchace): Ditto. (set_posix_access): Ditto. * syscalls.cc (open): Call open_with_arch with mode not umasked. * sec_acl.cc: Change preceeding comment explaining new-style ACLs. Describe how to generate deny ACEs in more detail. Accommodate the fact that a NULL deny ACE is used for {DEF_}CLASS_OBJ, rather than a special Cygwin ACE. Improve further comments. (CYG_ACE_NEW_STYLE): Define. (get_posix_access): Change from Cygwin ACE to NULL deny ACE. Fix CLASS_OBJ handling to generate CLASS_OBJ and DEF_CLASS_OBJ from a single NULL deny ACE if the inheritance flags say so. * sec_helper.cc (well_known_cygwin_sid): Remove. * security.h (well_known_cygwin_sid): Drop declaration. * sec_acl.cc (CYG_ACE_ISBITS_TO_WIN): Fix typo. (get_posix_access): Rename index variable from i to idx. Define only once at top level. * security.cc (add_access_allowed_ace): Drop unused parameter "offset". Accommodate throughout. (add_access_denied_ace): Ditto. * sec_acl.cc: Accommodate above change throughout. * security.h (add_access_allowed_ace): Adjust prototype to above change. (add_access_denied_ace): Ditto. * sec_acl.cc (get_posix_access): Handle multiple ACEs for the owner and primary group of the file. Handle the default primary group ACE as DEF_GROUP_OBJ entry if the directory has the S_ISGID bit set. Add comments. Minor code rearrangements. Preliminary read side implementation of new permission handling. * acl.h (MAX_ACL_ENTRIES): Raise to 2730. Add comment to explain. * sec_acl.cc: Add leading comment to explain new ACL style. Add definitions and macros to use for bits in new Cygwin ACL. (DENY_RWX): New mask value for all temporary deny bits. (getace): Add bool parameter to decide when leaving all bits intact, rather than filtering them per the already set bits. (get_posix_access): New function, taking over functionality to read POSIX ACL from SECURITY_DESCRIPTOR. (getacl): Just call get_posix_access. * sec_helper.cc (well_known_cygwin_sid): Define. * security.cc (get_attribute_from_acl): Remove. (get_info_from_sd): Remove. (get_reg_sd): Call get_posix_access instead of get_info_from_sd. (get_file_attribute): Ditto. (get_object_attribute): Ditto. * security.h (well_known_cygwin_sid): Declare. (get_posix_access): Add prototype. * Throughout, use simpler ACE macros from Windows' accctrl.h. * getfacl.c (main): Special-case SYSTEM and Admins group. Add comments. * setfacl.c: Align more to Linux tool. (delacl): New function to delete acl entries only. (modacl): Drop delete functionality. Add handling of recomputing the mask and default mask values. (delallacl): Rename from delacl. (setfacl): Call delacl in Delete case. Call delallacl in DeleteAll and DeleteDef case. (usage): Accommodate new options. Rearrange and rephrase slightly. (longopts): Emit 'x' in --delete case. Add --no-mask and --mask options. (opts): Add -x and -n options. (main): Handle -d and -x the same. Handle -n and --mask options. Drop handling for -r option. * getfacl.c (usage): Align more closely to Linux version. Add new options -c, -e, -E. Change formatting to accommodate longer options. (longopts): Rename --noname to --numeric. Keep --noname for backward compatibility. Add --omit-header, --all-effective and --no-effective options. (opts): Add -c, -e and -E option. (main): Handle new -c, -e, and -E options. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
693 lines
21 KiB
C++
693 lines
21 KiB
C++
/* sec_helper.cc: NT security helper functions
|
|
|
|
Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
|
2011, 2012, 2013, 2014, 2015 Red Hat, Inc.
|
|
|
|
Written by Corinna Vinschen <corinna@vinschen.de>
|
|
|
|
This file is part of Cygwin.
|
|
|
|
This software is a copyrighted work licensed under the terms of the
|
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
|
details. */
|
|
|
|
#include "winsup.h"
|
|
#include <stdlib.h>
|
|
#include <sys/acl.h>
|
|
#include <wchar.h>
|
|
#include "cygerrno.h"
|
|
#include "security.h"
|
|
#include "path.h"
|
|
#include "fhandler.h"
|
|
#include "dtable.h"
|
|
#include "pinfo.h"
|
|
#include "cygheap.h"
|
|
#include "ntdll.h"
|
|
#include "ldap.h"
|
|
|
|
/* General purpose security attribute objects for global use. */
|
|
static NO_COPY_RO SECURITY_DESCRIPTOR null_sdp =
|
|
{ SECURITY_DESCRIPTOR_REVISION, 0, SE_DACL_PRESENT,
|
|
NULL, NULL, NULL, NULL };
|
|
SECURITY_ATTRIBUTES NO_COPY_RO sec_none =
|
|
{ sizeof (SECURITY_ATTRIBUTES), NULL, TRUE };
|
|
SECURITY_ATTRIBUTES NO_COPY_RO sec_none_nih =
|
|
{ sizeof (SECURITY_ATTRIBUTES), NULL, FALSE };
|
|
SECURITY_ATTRIBUTES NO_COPY_RO sec_all =
|
|
{ sizeof (SECURITY_ATTRIBUTES), &null_sdp, TRUE };
|
|
SECURITY_ATTRIBUTES NO_COPY_RO sec_all_nih =
|
|
{ sizeof (SECURITY_ATTRIBUTES), &null_sdp, FALSE };
|
|
|
|
MKSID (well_known_null_sid, "S-1-0-0",
|
|
SECURITY_NULL_SID_AUTHORITY, 1, SECURITY_NULL_RID);
|
|
MKSID (well_known_world_sid, "S-1-1-0",
|
|
SECURITY_WORLD_SID_AUTHORITY, 1, SECURITY_WORLD_RID);
|
|
MKSID (well_known_local_sid, "S-1-2-0",
|
|
SECURITY_LOCAL_SID_AUTHORITY, 1, SECURITY_LOCAL_RID);
|
|
MKSID (well_known_console_logon_sid, "S-1-2-1",
|
|
SECURITY_LOCAL_SID_AUTHORITY, 1, 1);
|
|
MKSID (well_known_creator_owner_sid, "S-1-3-0",
|
|
SECURITY_CREATOR_SID_AUTHORITY, 1, SECURITY_CREATOR_OWNER_RID);
|
|
MKSID (well_known_creator_group_sid, "S-1-3-1",
|
|
SECURITY_CREATOR_SID_AUTHORITY, 1, SECURITY_CREATOR_GROUP_RID);
|
|
MKSID (well_known_dialup_sid, "S-1-5-1",
|
|
SECURITY_NT_AUTHORITY, 1, SECURITY_DIALUP_RID);
|
|
MKSID (well_known_network_sid, "S-1-5-2",
|
|
SECURITY_NT_AUTHORITY, 1, SECURITY_NETWORK_RID);
|
|
MKSID (well_known_batch_sid, "S-1-5-3",
|
|
SECURITY_NT_AUTHORITY, 1, SECURITY_BATCH_RID);
|
|
MKSID (well_known_interactive_sid, "S-1-5-4",
|
|
SECURITY_NT_AUTHORITY, 1, SECURITY_INTERACTIVE_RID);
|
|
MKSID (well_known_service_sid, "S-1-5-6",
|
|
SECURITY_NT_AUTHORITY, 1, SECURITY_SERVICE_RID);
|
|
MKSID (well_known_authenticated_users_sid, "S-1-5-11",
|
|
SECURITY_NT_AUTHORITY, 1, SECURITY_AUTHENTICATED_USER_RID);
|
|
MKSID (well_known_this_org_sid, "S-1-5-15",
|
|
SECURITY_NT_AUTHORITY, 1, 15);
|
|
MKSID (well_known_system_sid, "S-1-5-18",
|
|
SECURITY_NT_AUTHORITY, 1, SECURITY_LOCAL_SYSTEM_RID);
|
|
MKSID (well_known_local_service_sid, "S-1-5-19",
|
|
SECURITY_NT_AUTHORITY, 1, SECURITY_LOCAL_SERVICE_RID);
|
|
MKSID (well_known_network_service_sid, "S-1-5-20",
|
|
SECURITY_NT_AUTHORITY, 1, SECURITY_NETWORK_SERVICE_RID);
|
|
MKSID (well_known_builtin_sid, "S-1-5-32",
|
|
SECURITY_NT_AUTHORITY, 1, SECURITY_BUILTIN_DOMAIN_RID);
|
|
MKSID (well_known_admins_sid, "S-1-5-32-544",
|
|
SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS);
|
|
MKSID (well_known_users_sid, "S-1-5-32-545",
|
|
SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_USERS);
|
|
MKSID (trusted_installer_sid,
|
|
"S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464",
|
|
SECURITY_NT_AUTHORITY, SECURITY_SERVICE_ID_RID_COUNT,
|
|
SECURITY_SERVICE_ID_BASE_RID, 956008885U, 3418522649U, 1831038044U,
|
|
1853292631U, 2271478464U);
|
|
MKSID (mandatory_medium_integrity_sid, "S-1-16-8192",
|
|
SECURITY_MANDATORY_LABEL_AUTHORITY, 1, SECURITY_MANDATORY_MEDIUM_RID);
|
|
MKSID (mandatory_high_integrity_sid, "S-1-16-12288",
|
|
SECURITY_MANDATORY_LABEL_AUTHORITY, 1, SECURITY_MANDATORY_HIGH_RID);
|
|
MKSID (mandatory_system_integrity_sid, "S-1-16-16384",
|
|
SECURITY_MANDATORY_LABEL_AUTHORITY, 1, SECURITY_MANDATORY_SYSTEM_RID);
|
|
/* UNIX accounts on a Samba server have the SID prefix "S-1-22-1" */
|
|
#define SECURITY_SAMBA_UNIX_AUTHORITY {0,0,0,0,0,22}
|
|
MKSID (well_known_samba_unix_user_fake_sid, "S-1-22-1-0",
|
|
SECURITY_SAMBA_UNIX_AUTHORITY, 2, 1, 0);
|
|
|
|
bool
|
|
cygpsid::operator== (const char *nsidstr) const
|
|
{
|
|
cygsid nsid (nsidstr);
|
|
return psid == nsid;
|
|
}
|
|
|
|
uid_t
|
|
cygpsid::get_id (BOOL search_grp, int *type, cyg_ldap *pldap)
|
|
{
|
|
/* First try to get SID from group, then passwd */
|
|
uid_t id = ILLEGAL_UID;
|
|
|
|
if (search_grp)
|
|
{
|
|
struct group *gr;
|
|
if (cygheap->user.groups.pgsid == psid)
|
|
id = myself->gid;
|
|
else if (sid_id_auth (psid) == 22 && cygheap->pg.nss_grp_db ())
|
|
{
|
|
/* Samba UNIX group. Try to map to Cygwin gid. If there's no
|
|
mapping in the cache, try to fetch it from the configured
|
|
RFC 2307 domain (see last comment in cygheap_domain_info::init()
|
|
for more information) and add it to the mapping cache. */
|
|
gid_t gid = sid_sub_auth_rid (psid);
|
|
gid_t map_gid = cygheap->ugid_cache.get_gid (gid);
|
|
if (map_gid == ILLEGAL_GID)
|
|
{
|
|
if (pldap->open (cygheap->dom.get_rfc2307_domain ()) == NO_ERROR)
|
|
map_gid = pldap->remap_gid (gid);
|
|
if (map_gid == ILLEGAL_GID)
|
|
map_gid = MAP_UNIX_TO_CYGWIN_ID (gid);
|
|
cygheap->ugid_cache.add_gid (gid, map_gid);
|
|
}
|
|
id = (uid_t) map_gid;
|
|
}
|
|
else if ((gr = internal_getgrsid (*this, pldap)))
|
|
id = gr->gr_gid;
|
|
if ((gid_t) id != ILLEGAL_GID)
|
|
{
|
|
if (type)
|
|
*type = GROUP;
|
|
return id;
|
|
}
|
|
}
|
|
if (!search_grp || type)
|
|
{
|
|
struct passwd *pw;
|
|
if (*this == cygheap->user.sid ())
|
|
id = myself->uid;
|
|
else if (sid_id_auth (psid) == 22 && cygheap->pg.nss_pwd_db ())
|
|
{
|
|
/* Samba UNIX user. See comment above. */
|
|
uid_t uid = sid_sub_auth_rid (psid);
|
|
uid_t map_uid = cygheap->ugid_cache.get_uid (uid);
|
|
if (map_uid == ILLEGAL_UID)
|
|
{
|
|
if (pldap->open (cygheap->dom.get_rfc2307_domain ()) == NO_ERROR)
|
|
map_uid = pldap->remap_uid (uid);
|
|
if (map_uid == ILLEGAL_UID)
|
|
map_uid = MAP_UNIX_TO_CYGWIN_ID (uid);
|
|
cygheap->ugid_cache.add_uid (uid, map_uid);
|
|
}
|
|
id = map_uid;
|
|
}
|
|
else if ((pw = internal_getpwsid (*this, pldap)))
|
|
id = pw->pw_uid;
|
|
if (id != ILLEGAL_UID && type)
|
|
*type = USER;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
PWCHAR
|
|
cygpsid::pstring (PWCHAR nsidstr) const
|
|
{
|
|
UNICODE_STRING sid;
|
|
|
|
if (!psid || !nsidstr)
|
|
return NULL;
|
|
RtlInitEmptyUnicodeString (&sid, nsidstr, 256);
|
|
RtlConvertSidToUnicodeString (&sid, psid, FALSE);
|
|
return nsidstr + sid.Length / sizeof (WCHAR);
|
|
}
|
|
|
|
PWCHAR
|
|
cygpsid::string (PWCHAR nsidstr) const
|
|
{
|
|
if (pstring (nsidstr))
|
|
return nsidstr;
|
|
return NULL;
|
|
}
|
|
|
|
char *
|
|
cygpsid::pstring (char *nsidstr) const
|
|
{
|
|
char *t;
|
|
DWORD i;
|
|
|
|
if (!psid || !nsidstr)
|
|
return NULL;
|
|
strcpy (nsidstr, "S-1-");
|
|
t = nsidstr + sizeof ("S-1-") - 1;
|
|
t += __small_sprintf (t, "%u", sid_id_auth (psid));
|
|
for (i = 0; i < sid_sub_auth_count (psid); ++i)
|
|
t += __small_sprintf (t, "-%lu", sid_sub_auth (psid, i));
|
|
return t;
|
|
}
|
|
|
|
char *
|
|
cygpsid::string (char *nsidstr) const
|
|
{
|
|
if (pstring (nsidstr))
|
|
return nsidstr;
|
|
return NULL;
|
|
}
|
|
|
|
PSID
|
|
cygsid::get_sid (DWORD s, DWORD cnt, DWORD *r, bool well_known)
|
|
{
|
|
DWORD i;
|
|
SID_IDENTIFIER_AUTHORITY sid_auth = { SECURITY_NULL_SID_AUTHORITY };
|
|
# define SECURITY_NT_AUTH 5
|
|
|
|
/* 2015-10-22: Note that we let slip SIDs with a subauthority count of 0.
|
|
There are systems, which generate the SID S-1-0 as group ownership SID,
|
|
see https://cygwin.com/ml/cygwin/2015-10/msg00141.html. */
|
|
if (s > 255 || cnt > SID_MAX_SUB_AUTHORITIES)
|
|
{
|
|
psid = NO_SID;
|
|
return NULL;
|
|
}
|
|
sid_auth.Value[5] = s;
|
|
set ();
|
|
RtlInitializeSid (psid, &sid_auth, cnt);
|
|
PISID dsid = (PISID) psid;
|
|
for (i = 0; i < cnt; ++i)
|
|
dsid->SubAuthority[i] = r[i];
|
|
/* If the well_known flag isn't set explicitely, we check the SID
|
|
for being a well-known SID ourselves. That's necessary because this
|
|
cygsid is created from a SID string, usually from /etc/passwd or
|
|
/etc/group. The calling code just doesn't know if the SID is well-known
|
|
or not. All SIDs are well-known SIDs, except those in the non-unique NT
|
|
authority range. */
|
|
if (well_known)
|
|
well_known_sid = well_known;
|
|
else
|
|
well_known_sid = (s != SECURITY_NT_AUTH
|
|
|| r[0] != SECURITY_NT_NON_UNIQUE);
|
|
return psid;
|
|
}
|
|
|
|
const PSID
|
|
cygsid::getfromstr (PCWSTR nsidstr, bool well_known)
|
|
{
|
|
PWCHAR lasts;
|
|
DWORD s, cnt = 0;
|
|
DWORD r[SID_MAX_SUB_AUTHORITIES];
|
|
|
|
if (nsidstr && !wcsncmp (nsidstr, L"S-1-", 4))
|
|
{
|
|
s = wcstoul (nsidstr + 4, &lasts, 10);
|
|
while (cnt < SID_MAX_SUB_AUTHORITIES && *lasts == '-')
|
|
r[cnt++] = wcstoul (lasts + 1, &lasts, 10);
|
|
if (!*lasts)
|
|
return get_sid (s, cnt, r, well_known);
|
|
}
|
|
return psid = NO_SID;
|
|
}
|
|
|
|
const PSID
|
|
cygsid::getfromstr (const char *nsidstr, bool well_known)
|
|
{
|
|
char *lasts;
|
|
DWORD s, cnt = 0;
|
|
DWORD r[SID_MAX_SUB_AUTHORITIES];
|
|
|
|
if (nsidstr && !strncmp (nsidstr, "S-1-", 4))
|
|
{
|
|
s = strtoul (nsidstr + 4, &lasts, 10);
|
|
while (cnt < SID_MAX_SUB_AUTHORITIES && *lasts == '-')
|
|
r[cnt++] = strtoul (lasts + 1, &lasts, 10);
|
|
if (!*lasts)
|
|
return get_sid (s, cnt, r, well_known);
|
|
}
|
|
return psid = NO_SID;
|
|
}
|
|
|
|
cygsid *
|
|
cygsidlist::alloc_sids (int n)
|
|
{
|
|
if (n > 0)
|
|
return (cygsid *) cmalloc (HEAP_STR, n * sizeof (cygsid));
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
cygsidlist::free_sids ()
|
|
{
|
|
if (sids)
|
|
cfree (sids);
|
|
sids = NULL;
|
|
cnt = maxcnt = 0;
|
|
type = cygsidlist_empty;
|
|
}
|
|
|
|
BOOL
|
|
cygsidlist::add (const PSID nsi, bool well_known)
|
|
{
|
|
if (contains (nsi))
|
|
return TRUE;
|
|
if (cnt >= maxcnt)
|
|
{
|
|
cygsid *tmp = new cygsid [2 * maxcnt];
|
|
if (!tmp)
|
|
return FALSE;
|
|
maxcnt *= 2;
|
|
for (int i = 0; i < cnt; ++i)
|
|
tmp[i] = sids[i];
|
|
delete [] sids;
|
|
sids = tmp;
|
|
}
|
|
if (well_known)
|
|
sids[cnt++] *= nsi;
|
|
else
|
|
sids[cnt++] = nsi;
|
|
return TRUE;
|
|
}
|
|
|
|
bool
|
|
get_sids_info (cygpsid owner_sid, cygpsid group_sid, uid_t * uidret, gid_t * gidret)
|
|
{
|
|
BOOL ret = false;
|
|
cyg_ldap cldap;
|
|
|
|
owner_sid.debug_print ("get_sids_info: owner SID =");
|
|
group_sid.debug_print ("get_sids_info: group SID =");
|
|
|
|
*uidret = owner_sid.get_uid (&cldap);
|
|
*gidret = group_sid.get_gid (&cldap);
|
|
if (*uidret == myself->uid)
|
|
{
|
|
if (*gidret == myself->gid)
|
|
ret = TRUE;
|
|
else
|
|
CheckTokenMembership (cygheap->user.issetuid ()
|
|
? cygheap->user.imp_token () : NULL,
|
|
group_sid, &ret);
|
|
}
|
|
return (bool) ret;
|
|
}
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
security_descriptor::malloc (size_t nsize)
|
|
{
|
|
free ();
|
|
if ((psd = (PSECURITY_DESCRIPTOR) ::malloc (nsize)))
|
|
sd_size = nsize;
|
|
return psd;
|
|
}
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
security_descriptor::realloc (size_t nsize)
|
|
{
|
|
PSECURITY_DESCRIPTOR tmp;
|
|
|
|
/* Can't re-use buffer allocated by GetSecurityInfo. */
|
|
if (psd && !sd_size)
|
|
free ();
|
|
if (!(tmp = (PSECURITY_DESCRIPTOR) ::realloc (psd, nsize)))
|
|
return NULL;
|
|
sd_size = nsize;
|
|
return psd = tmp;
|
|
}
|
|
|
|
void
|
|
security_descriptor::free ()
|
|
{
|
|
if (psd)
|
|
{
|
|
if (!sd_size)
|
|
LocalFree (psd);
|
|
else
|
|
::free (psd);
|
|
}
|
|
psd = NULL;
|
|
sd_size = 0;
|
|
}
|
|
|
|
#undef TEXT
|
|
#define TEXT(q) L##q
|
|
|
|
/* Index must match the corresponding foo_PRIVILEGE value, see security.h. */
|
|
static const struct {
|
|
const wchar_t *name;
|
|
bool high_integrity; /* UAC: High Mandatory Label required to
|
|
be allowed to enable this privilege in
|
|
the user token. */
|
|
} cygpriv[] =
|
|
{
|
|
{ L"", false },
|
|
{ L"", false },
|
|
{ SE_CREATE_TOKEN_NAME, true },
|
|
{ SE_ASSIGNPRIMARYTOKEN_NAME, true },
|
|
{ SE_LOCK_MEMORY_NAME, false },
|
|
{ SE_INCREASE_QUOTA_NAME, true },
|
|
{ SE_MACHINE_ACCOUNT_NAME, false },
|
|
{ SE_TCB_NAME, true },
|
|
{ SE_SECURITY_NAME, true },
|
|
{ SE_TAKE_OWNERSHIP_NAME, true },
|
|
{ SE_LOAD_DRIVER_NAME, true },
|
|
{ SE_SYSTEM_PROFILE_NAME, true },
|
|
{ SE_SYSTEMTIME_NAME, true },
|
|
{ SE_PROF_SINGLE_PROCESS_NAME, true },
|
|
{ SE_INC_BASE_PRIORITY_NAME, true },
|
|
{ SE_CREATE_PAGEFILE_NAME, true },
|
|
{ SE_CREATE_PERMANENT_NAME, false },
|
|
{ SE_BACKUP_NAME, true },
|
|
{ SE_RESTORE_NAME, true },
|
|
{ SE_SHUTDOWN_NAME, false },
|
|
{ SE_DEBUG_NAME, true },
|
|
{ SE_AUDIT_NAME, false },
|
|
{ SE_SYSTEM_ENVIRONMENT_NAME, true },
|
|
{ SE_CHANGE_NOTIFY_NAME, false },
|
|
{ SE_REMOTE_SHUTDOWN_NAME, true },
|
|
{ SE_UNDOCK_NAME, false },
|
|
{ SE_SYNC_AGENT_NAME, false },
|
|
{ SE_ENABLE_DELEGATION_NAME, false },
|
|
{ SE_MANAGE_VOLUME_NAME, true },
|
|
{ SE_IMPERSONATE_NAME, true },
|
|
{ SE_CREATE_GLOBAL_NAME, false },
|
|
{ SE_TRUSTED_CREDMAN_ACCESS_NAME, false },
|
|
{ SE_RELABEL_NAME, true },
|
|
{ SE_INC_WORKING_SET_NAME, false },
|
|
{ SE_TIME_ZONE_NAME, true },
|
|
{ SE_CREATE_SYMBOLIC_LINK_NAME, true }
|
|
};
|
|
|
|
bool
|
|
privilege_luid (const PWCHAR pname, LUID &luid, bool &high_integrity)
|
|
{
|
|
ULONG idx;
|
|
for (idx = SE_CREATE_TOKEN_PRIVILEGE;
|
|
idx <= SE_MAX_WELL_KNOWN_PRIVILEGE;
|
|
++idx)
|
|
if (!wcscmp (cygpriv[idx].name, pname))
|
|
{
|
|
luid.HighPart = 0;
|
|
luid.LowPart = idx;
|
|
high_integrity = cygpriv[idx].high_integrity;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static const wchar_t *
|
|
privilege_name (const LUID &priv_luid)
|
|
{
|
|
if (priv_luid.HighPart || priv_luid.LowPart < SE_CREATE_TOKEN_PRIVILEGE
|
|
|| priv_luid.LowPart > SE_MAX_WELL_KNOWN_PRIVILEGE)
|
|
return L"<unknown privilege>";
|
|
return cygpriv[priv_luid.LowPart].name;
|
|
}
|
|
|
|
int
|
|
set_privilege (HANDLE token, DWORD privilege, bool enable)
|
|
{
|
|
int ret = -1;
|
|
TOKEN_PRIVILEGES new_priv, orig_priv;
|
|
ULONG size;
|
|
NTSTATUS status;
|
|
|
|
new_priv.PrivilegeCount = 1;
|
|
new_priv.Privileges[0].Luid.HighPart = 0L;
|
|
new_priv.Privileges[0].Luid.LowPart = privilege;
|
|
new_priv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
|
|
|
|
status = NtAdjustPrivilegesToken (token, FALSE, &new_priv, sizeof orig_priv,
|
|
&orig_priv, &size);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
__seterrno_from_nt_status (status);
|
|
goto out;
|
|
}
|
|
|
|
/* If orig_priv.PrivilegeCount is 0, the privilege hasn't been changed. */
|
|
if (!orig_priv.PrivilegeCount)
|
|
ret = enable ? 1 : 0;
|
|
else
|
|
ret = (orig_priv.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED) ? 1 : 0;
|
|
|
|
out:
|
|
if (ret < 0)
|
|
debug_printf ("%d = set_privilege((token %p) %W, %d)", ret, token,
|
|
privilege_name (new_priv.Privileges[0].Luid), enable);
|
|
return ret;
|
|
}
|
|
|
|
/* This is called very early in process initialization. The code must
|
|
not depend on anything. */
|
|
void
|
|
set_cygwin_privileges (HANDLE token)
|
|
{
|
|
/* Setting these rights at process startup allows processes running under
|
|
user tokens which are in the administrstors group to have root-like
|
|
permissions. */
|
|
/* Allow to access all files, independent of their ACL settings. */
|
|
set_privilege (token, SE_RESTORE_PRIVILEGE, true);
|
|
set_privilege (token, SE_BACKUP_PRIVILEGE, true);
|
|
/* Allow full access to other user's processes. */
|
|
set_privilege (token, SE_DEBUG_PRIVILEGE, true);
|
|
#if 0
|
|
/* Allow to create global shared memory. This isn't required anymore since
|
|
Cygwin 1.7. It uses its own subdirectories in the global NT namespace
|
|
which isn't affected by the SE_CREATE_GLOBAL_PRIVILEGE restriction. */
|
|
set_privilege (token, SE_CREATE_GLOBAL_PRIVILEGE, true);
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
sec_acl (PACL acl, bool original, bool admins, PSID sid1, PSID sid2, DWORD access2)
|
|
{
|
|
NTSTATUS status;
|
|
size_t acl_len = MAX_DACL_LEN (5);
|
|
LPVOID pAce;
|
|
cygpsid psid;
|
|
|
|
#ifdef DEBUGGING
|
|
if ((unsigned long) acl % 4)
|
|
api_fatal ("Incorrectly aligned incoming ACL buffer!");
|
|
#endif
|
|
status = RtlCreateAcl (acl, acl_len, ACL_REVISION);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
debug_printf ("RtlCreateAcl: %y", status);
|
|
return false;
|
|
}
|
|
if (sid1)
|
|
{
|
|
status = RtlAddAccessAllowedAce (acl, ACL_REVISION, GENERIC_ALL, sid1);
|
|
if (!NT_SUCCESS (status))
|
|
debug_printf ("RtlAddAccessAllowedAce(sid1) %y", status);
|
|
}
|
|
if (original && (psid = cygheap->user.saved_sid ())
|
|
&& psid != sid1 && psid != well_known_system_sid)
|
|
{
|
|
status = RtlAddAccessAllowedAce (acl, ACL_REVISION, GENERIC_ALL, psid);
|
|
if (!NT_SUCCESS (status))
|
|
debug_printf ("RtlAddAccessAllowedAce(original) %y", status);
|
|
}
|
|
if (sid2)
|
|
{
|
|
status = RtlAddAccessAllowedAce (acl, ACL_REVISION, access2, sid2);
|
|
if (!NT_SUCCESS (status))
|
|
debug_printf ("RtlAddAccessAllowedAce(sid2) %y", status);
|
|
}
|
|
if (admins)
|
|
{
|
|
status = RtlAddAccessAllowedAce (acl, ACL_REVISION, GENERIC_ALL,
|
|
well_known_admins_sid);
|
|
if (!NT_SUCCESS (status))
|
|
debug_printf ("RtlAddAccessAllowedAce(admin) %y", status);
|
|
}
|
|
status = RtlAddAccessAllowedAce (acl, ACL_REVISION, GENERIC_ALL,
|
|
well_known_system_sid);
|
|
if (!NT_SUCCESS (status))
|
|
debug_printf ("RtlAddAccessAllowedAce(system) %y", status);
|
|
status = RtlFirstFreeAce (acl, &pAce);
|
|
if (NT_SUCCESS (status) && pAce)
|
|
acl->AclSize = (char *) pAce - (char *) acl;
|
|
else
|
|
debug_printf ("RtlFirstFreeAce: %y", status);
|
|
|
|
return true;
|
|
}
|
|
|
|
PSECURITY_ATTRIBUTES __reg3
|
|
__sec_user (PVOID sa_buf, PSID sid1, PSID sid2, DWORD access2, BOOL inherit)
|
|
{
|
|
PSECURITY_ATTRIBUTES psa = (PSECURITY_ATTRIBUTES) sa_buf;
|
|
PISECURITY_DESCRIPTOR psd = (PISECURITY_DESCRIPTOR)
|
|
((char *) sa_buf + sizeof (*psa));
|
|
PACL acl = (PACL) ((char *) sa_buf + sizeof (*psa) + sizeof (*psd));
|
|
NTSTATUS status;
|
|
|
|
#ifdef DEBUGGING
|
|
if ((unsigned long) sa_buf % 4)
|
|
api_fatal ("Incorrectly aligned incoming SA buffer!");
|
|
#endif
|
|
if (!sec_acl (acl, true, true, sid1, sid2, access2))
|
|
return inherit ? &sec_none : &sec_none_nih;
|
|
|
|
RtlCreateSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION);
|
|
status = RtlSetDaclSecurityDescriptor (psd, TRUE, acl, FALSE);
|
|
if (!NT_SUCCESS (status))
|
|
debug_printf ("RtlSetDaclSecurityDescriptor %y", status);
|
|
|
|
psa->nLength = sizeof (SECURITY_ATTRIBUTES);
|
|
psa->lpSecurityDescriptor = psd;
|
|
psa->bInheritHandle = inherit;
|
|
return psa;
|
|
}
|
|
|
|
/* Helper function to create a file security descriptor which allows
|
|
full access to admins, system, and the sid given as parameter. See
|
|
try_to_bin for how it's used. */
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
_recycler_sd (void *buf, bool users, bool dir)
|
|
{
|
|
NTSTATUS status;
|
|
PISECURITY_DESCRIPTOR psd = (PISECURITY_DESCRIPTOR) buf;
|
|
|
|
if (!psd)
|
|
return NULL;
|
|
RtlCreateSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION);
|
|
PACL dacl = (PACL) (psd + 1);
|
|
/* Pre-Vista, the per-user recycler dir has a rather too complicated
|
|
ACL by default, which has distinct ACEs for inheritable and non-inheritable
|
|
permissions. However, this ACL is practically equivalent to the ACL
|
|
created since Vista. Therefore we simplify our job here and create the
|
|
pre-Vista permissions the same way as on Vista and later. */
|
|
RtlCreateAcl (dacl, MAX_DACL_LEN (3), ACL_REVISION);
|
|
RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
|
|
dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
|
|
: NO_INHERITANCE,
|
|
FILE_ALL_ACCESS, well_known_admins_sid);
|
|
RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
|
|
dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
|
|
: NO_INHERITANCE,
|
|
FILE_ALL_ACCESS, well_known_system_sid);
|
|
if (users)
|
|
RtlAddAccessAllowedAceEx (dacl, ACL_REVISION, INHERIT_NO_PROPAGATE,
|
|
FILE_GENERIC_READ | FILE_GENERIC_EXECUTE
|
|
| FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES,
|
|
well_known_users_sid);
|
|
else
|
|
RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
|
|
dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
|
|
: NO_INHERITANCE,
|
|
FILE_ALL_ACCESS, cygheap->user.sid ());
|
|
LPVOID ace;
|
|
status = RtlFirstFreeAce (dacl, &ace);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
debug_printf ("RtlFirstFreeAce: %y", status);
|
|
return NULL;
|
|
}
|
|
dacl->AclSize = (char *) ace - (char *) dacl;
|
|
RtlSetDaclSecurityDescriptor (psd, TRUE, dacl, FALSE);
|
|
/* If the directory DACL is not marked as protected, shell32 thinks
|
|
the recycle dir is corrupted. As soon as Explorer accesses the
|
|
Recycler, the user will get a GUI dialog "The Recycle Bin on X:\
|
|
is corrupted. Do you want to empty the Recycle Bin for this drive?"
|
|
Of course we want to avoid that. */
|
|
if (dir)
|
|
psd->Control |= SE_DACL_PROTECTED;
|
|
return psd;
|
|
}
|
|
|
|
/* Helper function to create an event security descriptor which only allows
|
|
specific access to everyone. Only the creating process has all access
|
|
rights. */
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
_everyone_sd (void *buf, ACCESS_MASK access)
|
|
{
|
|
NTSTATUS status;
|
|
PISECURITY_DESCRIPTOR psd = (PISECURITY_DESCRIPTOR) buf;
|
|
|
|
if (psd)
|
|
{
|
|
RtlCreateSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION);
|
|
PACL dacl = (PACL) (psd + 1);
|
|
RtlCreateAcl (dacl, MAX_DACL_LEN (1), ACL_REVISION);
|
|
status = RtlAddAccessAllowedAce (dacl, ACL_REVISION, access,
|
|
well_known_world_sid);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
debug_printf ("RtlAddAccessAllowedAce: %y", status);
|
|
return NULL;
|
|
}
|
|
LPVOID ace;
|
|
status = RtlFirstFreeAce (dacl, &ace);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
debug_printf ("RtlFirstFreeAce: %y", status);
|
|
return NULL;
|
|
}
|
|
dacl->AclSize = (char *) ace - (char *) dacl;
|
|
RtlSetDaclSecurityDescriptor (psd, TRUE, dacl, FALSE);
|
|
}
|
|
return psd;
|
|
}
|
|
|