First cut of full implementation of new permission handling
* 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. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
parent
aadd5f0295
commit
a44e09fd49
|
@ -1,3 +1,44 @@
|
|||
2015-04-10 Corinna Vinschen <corinna@vinschen.de>
|
||||
|
||||
* 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.
|
||||
|
||||
2015-04-09 Corinna Vinschen <corinna@vinschen.de>
|
||||
|
||||
* fhandler_dsp.cc (fhandler_dev_dsp::open): Call open_null.
|
||||
|
|
|
@ -463,7 +463,7 @@ fhandler_base::open_with_arch (int flags, mode_t mode)
|
|||
{
|
||||
int res;
|
||||
if (!(res = (archetype && archetype->io_handle)
|
||||
|| open (flags, (mode & 07777) & ~cygheap->umask)))
|
||||
|| open (flags, mode & 07777)))
|
||||
{
|
||||
if (archetype)
|
||||
delete archetype;
|
||||
|
@ -662,9 +662,10 @@ fhandler_base::open (int flags, mode_t mode)
|
|||
+ p->EaNameLength + 1);
|
||||
memset (nfs_attr, 0, sizeof (fattr3));
|
||||
nfs_attr->type = NF3REG;
|
||||
nfs_attr->mode = mode;
|
||||
nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
|
||||
}
|
||||
else if (!has_acls () && !(mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
|
||||
else if (!has_acls ()
|
||||
&& !(mode & ~cygheap->umask & (S_IWUSR | S_IWGRP | S_IWOTH)))
|
||||
/* If mode has no write bits set, and ACLs are not used, we set
|
||||
the DOS R/O attribute. */
|
||||
file_attributes |= FILE_ATTRIBUTE_READONLY;
|
||||
|
@ -716,7 +717,7 @@ fhandler_base::open (int flags, mode_t mode)
|
|||
This is the result of a discussion on the samba-technical list, starting at
|
||||
http://lists.samba.org/archive/samba-technical/2008-July/060247.html */
|
||||
if (io.Information == FILE_CREATED && has_acls ())
|
||||
set_file_attribute (fh, pc, ILLEGAL_UID, ILLEGAL_GID, S_JUSTCREATED | mode);
|
||||
set_created_file_access (fh, pc, mode);
|
||||
|
||||
/* If you O_TRUNC a file on Linux, the data is truncated, but the EAs are
|
||||
preserved. If you open a file on Windows with FILE_OVERWRITE{_IF} or
|
||||
|
|
|
@ -835,7 +835,7 @@ int __reg1
|
|||
fhandler_disk_file::fchmod (mode_t mode)
|
||||
{
|
||||
extern int chmod_device (path_conv& pc, mode_t mode);
|
||||
int res = -1;
|
||||
int ret = -1;
|
||||
int oret = 0;
|
||||
NTSTATUS status;
|
||||
IO_STATUS_BLOCK io;
|
||||
|
@ -882,17 +882,42 @@ fhandler_disk_file::fchmod (mode_t mode)
|
|||
if (!NT_SUCCESS (status))
|
||||
__seterrno_from_nt_status (status);
|
||||
else
|
||||
res = 0;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pc.has_acls ())
|
||||
{
|
||||
security_descriptor sd, sd_ret;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
tmp_pathbuf tp;
|
||||
aclent_t *aclp;
|
||||
int nentries, idx;
|
||||
|
||||
if (!get_file_sd (get_handle (), pc, sd, false))
|
||||
{
|
||||
aclp = (aclent_t *) tp.c_get ();
|
||||
if ((nentries = get_posix_access (sd, NULL, &uid, &gid,
|
||||
aclp, MAX_ACL_ENTRIES)) >= 0)
|
||||
{
|
||||
/* Overwrite ACL permissions as required by POSIX 1003.1e
|
||||
draft 17. */
|
||||
aclp[0].a_perm = (mode >> 6) & S_IRWXO;
|
||||
if (nentries > MIN_ACL_ENTRIES
|
||||
&& (idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0)
|
||||
aclp[idx].a_perm = (mode >> 6) & S_IRWXO;
|
||||
else
|
||||
aclp[1].a_perm = (mode >> 6) & S_IRWXO;
|
||||
if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0)
|
||||
aclp[idx].a_perm = mode & S_IRWXO;
|
||||
if (pc.isdir ())
|
||||
mode |= S_IFDIR;
|
||||
if (!set_file_attribute (get_handle (), pc,
|
||||
ILLEGAL_UID, ILLEGAL_GID, mode))
|
||||
res = 0;
|
||||
if (set_posix_access (mode, uid, gid, aclp, nentries, sd_ret,
|
||||
pc.fs_is_samba ()))
|
||||
ret = set_file_sd (get_handle (), pc, sd_ret, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the mode has any write bits set, the DOS R/O flag is in the way. */
|
||||
|
@ -929,20 +954,28 @@ fhandler_disk_file::fchmod (mode_t mode)
|
|||
if (!NT_SUCCESS (status))
|
||||
__seterrno_from_nt_status (status);
|
||||
else
|
||||
res = 0;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
if (oret)
|
||||
close_fs ();
|
||||
|
||||
return res;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __reg2
|
||||
fhandler_disk_file::fchown (uid_t uid, gid_t gid)
|
||||
{
|
||||
int oret = 0;
|
||||
int ret = -1;
|
||||
security_descriptor sd, sd_ret;
|
||||
mode_t attr = pc.isdir () ? S_IFDIR : 0;
|
||||
uid_t old_uid;
|
||||
gid_t old_gid;
|
||||
tmp_pathbuf tp;
|
||||
aclent_t *aclp;
|
||||
int nentries;
|
||||
|
||||
if (!pc.has_acls ())
|
||||
{
|
||||
|
@ -959,52 +992,71 @@ fhandler_disk_file::fchown (uid_t uid, gid_t gid)
|
|||
return -1;
|
||||
}
|
||||
|
||||
mode_t attrib = 0;
|
||||
if (pc.isdir ())
|
||||
attrib |= S_IFDIR;
|
||||
uid_t old_uid;
|
||||
int res = get_file_attribute (get_handle (), pc, &attrib, &old_uid, NULL);
|
||||
if (!res)
|
||||
if (get_file_sd (get_handle (), pc, sd, false))
|
||||
goto out;
|
||||
|
||||
aclp = (aclent_t *) tp.c_get ();
|
||||
if ((nentries = get_posix_access (sd, &attr, &old_uid, &old_gid,
|
||||
aclp, MAX_ACL_ENTRIES)) < 0)
|
||||
goto out;
|
||||
|
||||
if (uid == ILLEGAL_UID)
|
||||
uid = old_uid;
|
||||
if (gid == ILLEGAL_GID)
|
||||
gid = old_gid;
|
||||
if (uid == old_uid && gid == old_gid)
|
||||
{
|
||||
/* Typical Windows default ACLs can contain permissions for one
|
||||
group, while being owned by another user/group. The permission
|
||||
bits returned above are pretty much useless then. Creating a
|
||||
new ACL with these useless permissions results in a potentially
|
||||
broken symlink. So what we do here is to set the underlying
|
||||
permissions of symlinks to a sensible value which allows the
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Windows ACLs can contain permissions for one group, while being owned by
|
||||
another user/group. The permission bits returned above are pretty much
|
||||
useless then. Creating a new ACL with these useless permissions results
|
||||
in a potentially broken symlink. So what we do here is to set the
|
||||
underlying permissions of symlinks to a sensible value which allows the
|
||||
world to read the symlink and only the new owner to change it. */
|
||||
if (pc.issymlink ())
|
||||
attrib = S_IFLNK | STD_RBITS | STD_WBITS;
|
||||
res = set_file_attribute (get_handle (), pc, uid, gid, attrib);
|
||||
/* If you're running a Samba server which has no winbind running, the
|
||||
uid<->SID mapping is disfunctional. Even trying to chown to your
|
||||
own account fails since the account used on the server is the UNIX
|
||||
account which gets used for the standard user mapping. This is a
|
||||
default mechanism which doesn't know your real Windows SID.
|
||||
There are two possible error codes in different Samba releases for
|
||||
this situation, one of them is unfortunately the not very significant
|
||||
STATUS_ACCESS_DENIED. Instead of relying on the error codes, we're
|
||||
using the below very simple heuristic. If set_file_attribute failed,
|
||||
and the original user account was either already unknown, or one of
|
||||
the standard UNIX accounts, we're faking success. */
|
||||
if (res == -1 && pc.fs_is_samba ())
|
||||
for (int idx = 0; idx < nentries; ++idx)
|
||||
{
|
||||
aclp[idx].a_perm |= S_IROTH;
|
||||
if (aclp[idx].a_type & USER_OBJ)
|
||||
aclp[idx].a_perm |= S_IWOTH;
|
||||
}
|
||||
|
||||
if (set_posix_access (attr, uid, gid, aclp, nentries, sd_ret,
|
||||
pc.fs_is_samba ()))
|
||||
ret = set_file_sd (get_handle (), pc, sd_ret, true);
|
||||
|
||||
/* If you're running a Samba server with no winbind, the uid<->SID mapping
|
||||
is disfunctional. Even trying to chown to your own account fails since
|
||||
the account used on the server is the UNIX account which gets used for
|
||||
the standard user mapping. This is a default mechanism which doesn't
|
||||
know your real Windows SID. There are two possible error codes in
|
||||
different Samba releases for this situation, one of them unfortunately
|
||||
the not very significant STATUS_ACCESS_DENIED. Instead of relying on
|
||||
the error codes, we're using the below very simple heuristic.
|
||||
If set_file_sd failed, and the original user account was either already
|
||||
unknown, or one of the standard UNIX accounts, we're faking success. */
|
||||
if (ret == -1 && pc.fs_is_samba ())
|
||||
{
|
||||
PSID sid;
|
||||
|
||||
if (old_uid == ILLEGAL_UID
|
||||
if (uid == old_uid
|
||||
|| ((sid = sidfromuid (old_uid, NULL)) != NO_SID
|
||||
&& RtlEqualPrefixSid (sid,
|
||||
well_known_samba_unix_user_fake_sid)))
|
||||
{
|
||||
debug_printf ("Faking chown worked on standalone Samba");
|
||||
res = 0;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (oret)
|
||||
close_fs ();
|
||||
|
||||
return res;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __reg3
|
||||
|
@ -1763,10 +1815,11 @@ fhandler_disk_file::mkdir (mode_t mode)
|
|||
p, plen);
|
||||
if (NT_SUCCESS (status))
|
||||
{
|
||||
/* Set the "directory attribute" so that pc.isdir() returns correct
|
||||
value in subsequent function calls. */
|
||||
pc.file_attributes (FILE_ATTRIBUTE_DIRECTORY);
|
||||
if (has_acls ())
|
||||
set_file_attribute (dir, pc, ILLEGAL_UID, ILLEGAL_GID,
|
||||
S_JUSTCREATED | S_IFDIR
|
||||
| ((mode & 07777) & ~cygheap->umask));
|
||||
set_created_file_access (dir, pc, mode & 07777);
|
||||
NtClose (dir);
|
||||
res = 0;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* fhandler_socket.cc. See fhandler.h for a description of the fhandler classes.
|
||||
|
||||
Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011, 2012, 2013, 2014 Red Hat, Inc.
|
||||
2011, 2012, 2013, 2014, 2015 Red Hat, Inc.
|
||||
|
||||
This file is part of Cygwin.
|
||||
|
||||
|
@ -1039,10 +1039,10 @@ fhandler_socket::bind (const struct sockaddr *name, int namelen)
|
|||
sin.sin_port = ntohs (sin.sin_port);
|
||||
debug_printf ("AF_LOCAL: socket bound to port %u", sin.sin_port);
|
||||
|
||||
mode_t mode = adjust_socket_file_mode ((S_IRWXU | S_IRWXG | S_IRWXO)
|
||||
& ~cygheap->umask);
|
||||
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||
DWORD fattr = FILE_ATTRIBUTE_SYSTEM;
|
||||
if (!(mode & (S_IWUSR | S_IWGRP | S_IWOTH)) && !pc.has_acls ())
|
||||
if (!pc.has_acls ()
|
||||
&& !(mode & ~cygheap->umask & (S_IWUSR | S_IWGRP | S_IWOTH)))
|
||||
fattr |= FILE_ATTRIBUTE_READONLY;
|
||||
SECURITY_ATTRIBUTES sa = sec_none_nih;
|
||||
NTSTATUS status;
|
||||
|
@ -1060,7 +1060,7 @@ fhandler_socket::bind (const struct sockaddr *name, int namelen)
|
|||
I don't know what setting that is or how to recognize such a share,
|
||||
so for now we don't request WRITE_DAC on remote drives. */
|
||||
if (pc.has_acls () && !pc.isremote ())
|
||||
access |= READ_CONTROL | WRITE_DAC;
|
||||
access |= READ_CONTROL | WRITE_DAC | WRITE_OWNER;
|
||||
|
||||
status = NtCreateFile (&fh, access, pc.get_object_attr (attr, sa), &io,
|
||||
NULL, fattr, 0, FILE_CREATE,
|
||||
|
@ -1078,8 +1078,7 @@ fhandler_socket::bind (const struct sockaddr *name, int namelen)
|
|||
else
|
||||
{
|
||||
if (pc.has_acls ())
|
||||
set_file_attribute (fh, pc, ILLEGAL_UID, ILLEGAL_GID,
|
||||
S_JUSTCREATED | mode);
|
||||
set_created_file_access (fh, pc, mode);
|
||||
char buf[sizeof (SOCKET_COOKIE) + 80];
|
||||
__small_sprintf (buf, "%s%u %c ", SOCKET_COOKIE, sin.sin_port,
|
||||
get_socket_type () == SOCK_STREAM ? 's'
|
||||
|
|
|
@ -2037,10 +2037,9 @@ symlink_worker (const char *oldpath, const char *newpath, bool isdevice)
|
|||
__seterrno_from_nt_status (status);
|
||||
__leave;
|
||||
}
|
||||
if (win32_newpath.has_acls ())
|
||||
set_file_attribute (fh, win32_newpath, ILLEGAL_UID, ILLEGAL_GID,
|
||||
(io.Information == FILE_CREATED ? S_JUSTCREATED : 0)
|
||||
| S_IFLNK | STD_RBITS | STD_WBITS);
|
||||
if (io.Information == FILE_CREATED && win32_newpath.has_acls ())
|
||||
set_created_file_access (fh, win32_newpath,
|
||||
S_IFLNK | STD_RBITS | STD_WBITS);
|
||||
status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf,
|
||||
NULL, NULL);
|
||||
if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf))
|
||||
|
|
|
@ -1,6 +1,24 @@
|
|||
What's new:
|
||||
-----------
|
||||
|
||||
- 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.
|
||||
|
||||
- basename(3) now comes in two flavors, POSIX and GNU. The POSIX version is
|
||||
the default. You get the GNU version after
|
||||
|
||||
|
|
|
@ -91,8 +91,8 @@ details. */
|
|||
| CYG_ACE_MASK_VALID)
|
||||
#define CYG_ACE_NEW_STYLE READ_CONTROL /* New style if set. */
|
||||
|
||||
static int
|
||||
searchace (aclent_t *aclp, int nentries, int type, uid_t id = ILLEGAL_UID)
|
||||
int
|
||||
searchace (aclent_t *aclp, int nentries, int type, uid_t id)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -103,265 +103,276 @@ searchace (aclent_t *aclp, int nentries, int type, uid_t id = ILLEGAL_UID)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* This function *requires* an acl list sorted with aclsort{32}. */
|
||||
int
|
||||
setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
|
||||
bool &writable)
|
||||
/* Define own bit masks rather than using the GENERIC masks. The latter
|
||||
also contain standard rights, which we don't need here. */
|
||||
#define FILE_ALLOW_READ (FILE_READ_DATA | FILE_READ_ATTRIBUTES | \
|
||||
FILE_READ_EA)
|
||||
#define FILE_DENY_READ (FILE_READ_DATA | FILE_READ_EA)
|
||||
#define FILE_ALLOW_WRITE (FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | \
|
||||
FILE_WRITE_EA | FILE_APPEND_DATA)
|
||||
#define FILE_DENY_WRITE FILE_ALLOW_WRITE | FILE_DELETE_CHILD
|
||||
#define FILE_DENY_WRITE_OWNER (FILE_WRITE_DATA | FILE_WRITE_EA | \
|
||||
FILE_APPEND_DATA | FILE_DELETE_CHILD)
|
||||
#define FILE_ALLOW_EXEC (FILE_EXECUTE)
|
||||
#define FILE_DENY_EXEC FILE_ALLOW_EXEC
|
||||
|
||||
#define STD_RIGHTS_OTHER (STANDARD_RIGHTS_READ | SYNCHRONIZE)
|
||||
#define STD_RIGHTS_OWNER (STANDARD_RIGHTS_ALL | SYNCHRONIZE)
|
||||
|
||||
/* From the attributes and the POSIX ACL list, compute a new-style Cygwin
|
||||
security descriptor. The function returns a pointer to the
|
||||
SECURITY_DESCRIPTOR in sd_ret, or NULL if the function fails.
|
||||
|
||||
This function *requires* a verified and sorted acl list! */
|
||||
PSECURITY_DESCRIPTOR
|
||||
set_posix_access (mode_t attr, uid_t uid, gid_t gid,
|
||||
aclent_t *aclbufp, int nentries,
|
||||
security_descriptor &sd_ret,
|
||||
bool is_samba)
|
||||
{
|
||||
security_descriptor sd_ret;
|
||||
tmp_pathbuf tp;
|
||||
|
||||
if (get_file_sd (handle, pc, sd_ret, false))
|
||||
return -1;
|
||||
|
||||
SECURITY_DESCRIPTOR sd;
|
||||
cyg_ldap cldap;
|
||||
PSID owner, group;
|
||||
NTSTATUS status;
|
||||
tmp_pathbuf tp;
|
||||
cygpsid *aclsid;
|
||||
PACL acl;
|
||||
BOOLEAN acl_exists, dummy;
|
||||
|
||||
/* Get owner SID. */
|
||||
PSID owner_sid;
|
||||
status = RtlGetOwnerSecurityDescriptor (sd_ret, &owner_sid, &dummy);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return -1;
|
||||
}
|
||||
cygsid owner (owner_sid);
|
||||
|
||||
/* Get group SID. */
|
||||
PSID group_sid;
|
||||
status = RtlGetGroupSecurityDescriptor (sd_ret, &group_sid, &dummy);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return -1;
|
||||
}
|
||||
cygsid group (group_sid);
|
||||
|
||||
/* Search for NULL ACE and store state of SUID, SGID and VTX bits. */
|
||||
DWORD null_mask = 0;
|
||||
if (NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd_ret, &acl_exists, &acl,
|
||||
&dummy)))
|
||||
for (USHORT i = 0; i < acl->AceCount; ++i)
|
||||
{
|
||||
ACCESS_ALLOWED_ACE *ace;
|
||||
if (NT_SUCCESS (RtlGetAce (acl, i, (PVOID *) &ace)))
|
||||
{
|
||||
cygpsid ace_sid ((PSID) &ace->SidStart);
|
||||
if (ace_sid == well_known_null_sid)
|
||||
{
|
||||
null_mask = ace->Mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t acl_len = sizeof (ACL);
|
||||
mode_t class_obj = 0, other_obj, group_obj, deny;
|
||||
DWORD access;
|
||||
int idx, start_idx, class_idx, tmp_idx;
|
||||
|
||||
/* Initialize local security descriptor. */
|
||||
SECURITY_DESCRIPTOR sd;
|
||||
RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
|
||||
|
||||
/* As in alloc_sd, set SE_DACL_PROTECTED to prevent the DACL from being
|
||||
modified by inheritable ACEs. */
|
||||
RtlSetControlSecurityDescriptor (&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
|
||||
|
||||
/* Fetch owner and group and set in security descriptor. */
|
||||
owner = sidfromuid (uid, &cldap);
|
||||
group = sidfromgid (gid, &cldap);
|
||||
status = RtlSetOwnerSecurityDescriptor (&sd, owner, FALSE);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
status = RtlSetGroupSecurityDescriptor (&sd, group, FALSE);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Fill access control list. */
|
||||
acl = (PACL) tp.w_get ();
|
||||
size_t acl_len = sizeof (ACL);
|
||||
|
||||
cygsid sid;
|
||||
struct passwd *pw;
|
||||
struct group *gr;
|
||||
int pos;
|
||||
cyg_ldap cldap;
|
||||
|
||||
RtlCreateAcl (acl, ACL_MAXIMUM_SIZE, ACL_REVISION);
|
||||
|
||||
writable = false;
|
||||
|
||||
bool *invalid = (bool *) tp.c_get ();
|
||||
memset (invalid, 0, nentries * sizeof *invalid);
|
||||
|
||||
/* Pre-compute owner, group, and other permissions to allow creating
|
||||
matching deny ACEs as in alloc_sd. */
|
||||
DWORD owner_allow = 0, group_allow = 0, other_allow = 0;
|
||||
PDWORD allow;
|
||||
for (int i = 0; i < nentries; ++i)
|
||||
/* No POSIX ACL? Use attr to generate one from scratch. */
|
||||
if (!aclbufp)
|
||||
{
|
||||
switch (aclbufp[i].a_type)
|
||||
aclbufp = (aclent_t *) tp.c_get ();
|
||||
aclbufp[0].a_type = USER_OBJ;
|
||||
aclbufp[0].a_id = ILLEGAL_UID;
|
||||
aclbufp[0].a_perm = (attr >> 6) & S_IRWXO;
|
||||
aclbufp[1].a_type = GROUP_OBJ;
|
||||
aclbufp[1].a_id = ILLEGAL_GID;
|
||||
aclbufp[1].a_perm = (attr >> 3) & S_IRWXO;
|
||||
aclbufp[2].a_type = OTHER_OBJ;
|
||||
aclbufp[2].a_id = ILLEGAL_GID;
|
||||
aclbufp[2].a_perm = attr & S_IRWXO;
|
||||
nentries = MIN_ACL_ENTRIES;
|
||||
if (S_ISDIR (attr))
|
||||
{
|
||||
aclbufp[3].a_type = DEF_USER_OBJ;
|
||||
aclbufp[3].a_id = ILLEGAL_UID;
|
||||
aclbufp[3].a_perm = (attr >> 6) & S_IRWXO;
|
||||
aclbufp[4].a_type = GROUP_OBJ;
|
||||
aclbufp[4].a_id = ILLEGAL_GID;
|
||||
aclbufp[4].a_perm = (attr >> 3) & S_IRWXO;
|
||||
aclbufp[5].a_type = OTHER_OBJ;
|
||||
aclbufp[5].a_id = ILLEGAL_GID;
|
||||
aclbufp[5].a_perm = attr & S_IRWXO;
|
||||
nentries += MIN_ACL_ENTRIES;
|
||||
}
|
||||
}
|
||||
|
||||
/* Collect SIDs of all entries in aclbufp. */
|
||||
aclsid = (cygpsid *) tp.w_get ();
|
||||
for (idx = 0; idx < nentries; ++idx)
|
||||
switch (aclbufp[idx].a_type & ~ACL_DEFAULT)
|
||||
{
|
||||
case USER_OBJ:
|
||||
allow = &owner_allow;
|
||||
*allow = STANDARD_RIGHTS_ALL
|
||||
| (pc.fs_is_samba () ? 0 : FILE_WRITE_ATTRIBUTES);
|
||||
break;
|
||||
case GROUP_OBJ:
|
||||
allow = &group_allow;
|
||||
break;
|
||||
case OTHER_OBJ:
|
||||
allow = &other_allow;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
*allow |= STANDARD_RIGHTS_READ | SYNCHRONIZE
|
||||
| (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
|
||||
if (aclbufp[i].a_perm & S_IROTH)
|
||||
*allow |= FILE_GENERIC_READ;
|
||||
if (aclbufp[i].a_perm & S_IWOTH)
|
||||
{
|
||||
*allow |= FILE_GENERIC_WRITE;
|
||||
writable = true;
|
||||
}
|
||||
if (aclbufp[i].a_perm & S_IXOTH)
|
||||
*allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
|
||||
/* Keep S_ISVTX rule in sync with alloc_sd. */
|
||||
if (pc.isdir ()
|
||||
&& (aclbufp[i].a_perm & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
|
||||
&& (aclbufp[i].a_type == USER_OBJ
|
||||
|| !(null_mask & FILE_READ_DATA)))
|
||||
*allow |= FILE_DELETE_CHILD;
|
||||
invalid[i] = true;
|
||||
}
|
||||
bool isownergroup = (owner == group);
|
||||
DWORD owner_deny = ~owner_allow & (group_allow | other_allow);
|
||||
owner_deny &= ~(STANDARD_RIGHTS_READ
|
||||
| FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES);
|
||||
DWORD group_deny = ~group_allow & other_allow;
|
||||
group_deny &= ~(STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES);
|
||||
|
||||
/* Set deny ACE for owner. */
|
||||
if (owner_deny
|
||||
&& !add_access_denied_ace (acl, owner_deny, owner, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return -1;
|
||||
/* Set deny ACE for group here to respect the canonical order,
|
||||
if this does not impact owner */
|
||||
if (group_deny && !(group_deny & owner_allow) && !isownergroup
|
||||
&& !add_access_denied_ace (acl, group_deny, group, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return -1;
|
||||
/* Set allow ACE for owner. */
|
||||
if (!add_access_allowed_ace (acl, owner_allow, owner, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return -1;
|
||||
/* Set deny ACE for group, if still needed. */
|
||||
if (group_deny & owner_allow && !isownergroup
|
||||
&& !add_access_denied_ace (acl, group_deny, group, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return -1;
|
||||
/* Set allow ACE for group. */
|
||||
if (!isownergroup
|
||||
&& !add_access_allowed_ace (acl, group_allow, group, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return -1;
|
||||
/* Set allow ACE for everyone. */
|
||||
if (!add_access_allowed_ace (acl, other_allow, well_known_world_sid, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return -1;
|
||||
/* If a NULL ACE exists, copy it verbatim. */
|
||||
if (null_mask)
|
||||
if (!add_access_allowed_ace (acl, null_mask, well_known_null_sid, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return -1;
|
||||
for (int i = 0; i < nentries; ++i)
|
||||
{
|
||||
DWORD allow;
|
||||
/* Skip invalidated entries. */
|
||||
if (invalid[i])
|
||||
continue;
|
||||
|
||||
allow = STANDARD_RIGHTS_READ
|
||||
| (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
|
||||
if (aclbufp[i].a_perm & S_IROTH)
|
||||
allow |= FILE_GENERIC_READ;
|
||||
if (aclbufp[i].a_perm & S_IWOTH)
|
||||
{
|
||||
allow |= FILE_GENERIC_WRITE;
|
||||
writable = true;
|
||||
}
|
||||
if (aclbufp[i].a_perm & S_IXOTH)
|
||||
allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
|
||||
/* Keep S_ISVTX rule in sync with alloc_sd. */
|
||||
if (pc.isdir ()
|
||||
&& (aclbufp[i].a_perm & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
|
||||
&& !(null_mask & FILE_READ_DATA))
|
||||
allow |= FILE_DELETE_CHILD;
|
||||
/* Set inherit property. */
|
||||
DWORD inheritance = (aclbufp[i].a_type & ACL_DEFAULT)
|
||||
? (SUB_CONTAINERS_AND_OBJECTS_INHERIT | INHERIT_ONLY)
|
||||
: NO_INHERITANCE;
|
||||
/*
|
||||
* If a specific acl contains a corresponding default entry with
|
||||
* identical permissions, only one Windows ACE with proper
|
||||
* inheritance bits is created.
|
||||
*/
|
||||
if (!(aclbufp[i].a_type & ACL_DEFAULT)
|
||||
&& aclbufp[i].a_type & (USER|GROUP)
|
||||
&& (pos = searchace (aclbufp + i + 1, nentries - i - 1,
|
||||
aclbufp[i].a_type | ACL_DEFAULT,
|
||||
(aclbufp[i].a_type & (USER|GROUP))
|
||||
? aclbufp[i].a_id : ILLEGAL_UID)) >= 0
|
||||
&& aclbufp[i].a_perm == aclbufp[i + 1 + pos].a_perm)
|
||||
{
|
||||
inheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
|
||||
/* invalidate the corresponding default entry. */
|
||||
invalid[i + 1 + pos] = true;
|
||||
}
|
||||
switch (aclbufp[i].a_type)
|
||||
{
|
||||
case DEF_USER_OBJ:
|
||||
allow |= STANDARD_RIGHTS_ALL
|
||||
| (pc.fs_is_samba () ? 0 : FILE_WRITE_ATTRIBUTES);
|
||||
if (!add_access_allowed_ace (acl, allow, well_known_creator_owner_sid,
|
||||
acl_len, inheritance))
|
||||
return -1;
|
||||
aclsid[idx] = (aclbufp[idx].a_type & ACL_DEFAULT)
|
||||
? (PSID) well_known_creator_owner_sid : owner;
|
||||
break;
|
||||
case USER:
|
||||
case DEF_USER:
|
||||
if (!(pw = internal_getpwuid (aclbufp[i].a_id, &cldap))
|
||||
|| !sid.getfrompw (pw))
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
if (!add_access_allowed_ace (acl, allow, sid, acl_len, inheritance))
|
||||
return -1;
|
||||
aclsid[idx] = sidfromuid (aclbufp[idx].a_id, &cldap);
|
||||
break;
|
||||
case DEF_GROUP_OBJ:
|
||||
if (!add_access_allowed_ace (acl, allow, well_known_creator_group_sid,
|
||||
acl_len, inheritance))
|
||||
return -1;
|
||||
case GROUP_OBJ:
|
||||
aclsid[idx] = (aclbufp[idx].a_type & ACL_DEFAULT && !(attr & S_ISGID))
|
||||
? (PSID) well_known_creator_group_sid : group;
|
||||
break;
|
||||
case GROUP:
|
||||
case DEF_GROUP:
|
||||
if (!(gr = internal_getgrgid (aclbufp[i].a_id, &cldap))
|
||||
|| !sid.getfromgr (gr))
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
if (!add_access_allowed_ace (acl, allow, sid, acl_len, inheritance))
|
||||
return -1;
|
||||
aclsid[idx] = sidfromgid (aclbufp[idx].a_id, &cldap);
|
||||
break;
|
||||
case DEF_OTHER_OBJ:
|
||||
if (!add_access_allowed_ace (acl, allow, well_known_world_sid,
|
||||
acl_len, inheritance))
|
||||
return -1;
|
||||
case CLASS_OBJ:
|
||||
aclsid[idx] = well_known_null_sid;
|
||||
break;
|
||||
case OTHER_OBJ:
|
||||
aclsid[idx] = well_known_world_sid;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Initialize ACL. */
|
||||
acl = (PACL) tp.w_get ();
|
||||
RtlCreateAcl (acl, ACL_MAXIMUM_SIZE, ACL_REVISION);
|
||||
|
||||
/* This loop has two runs, the first handling the actual permission,
|
||||
the second handling the default permissions. */
|
||||
idx = 0;
|
||||
for (int def = 0; def <= ACL_DEFAULT; def += ACL_DEFAULT)
|
||||
{
|
||||
DWORD inherit = def ? SUB_CONTAINERS_AND_OBJECTS_INHERIT | INHERIT_ONLY
|
||||
: NO_INHERITANCE;
|
||||
|
||||
/* No default ACEs on files. */
|
||||
if (def && !S_ISDIR (attr))
|
||||
{
|
||||
/* Trying to set default ACEs on a non-directory is an error.
|
||||
The underlying functions on Linux return EACCES. */
|
||||
if (idx < nentries && aclbufp[idx].a_type & ACL_DEFAULT)
|
||||
{
|
||||
set_errno (EACCES);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* To compute deny access masks, we need group_obj, other_obj and... */
|
||||
tmp_idx = searchace (aclbufp, nentries, def | GROUP_OBJ);
|
||||
/* No default entries present? */
|
||||
if (tmp_idx < 0)
|
||||
break;
|
||||
group_obj = aclbufp[tmp_idx].a_perm;
|
||||
tmp_idx = searchace (aclbufp, nentries, def | OTHER_OBJ);
|
||||
other_obj = aclbufp[tmp_idx].a_perm;
|
||||
|
||||
/* ... class_obj. Create Cygwin ACE. Only the S_ISGID attribute gets
|
||||
inherited. */
|
||||
access = CYG_ACE_ISBITS_TO_WIN (def ? attr & S_ISGID : attr);
|
||||
class_idx = searchace (aclbufp, nentries, def | CLASS_OBJ);
|
||||
if (class_idx >= 0)
|
||||
{
|
||||
class_obj = aclbufp[class_idx].a_perm;
|
||||
access |= CYG_ACE_MASK_TO_WIN (class_obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Setting class_obj to group_obj allows to write below code without
|
||||
additional checks for existence of a CLASS_OBJ. */
|
||||
class_obj = group_obj;
|
||||
class_idx = -1;
|
||||
}
|
||||
access |= CYG_ACE_NEW_STYLE;
|
||||
if (!add_access_denied_ace (acl, access, well_known_null_sid, acl_len,
|
||||
inherit))
|
||||
return NULL;
|
||||
|
||||
/* This loop has two runs, the first w/ check_types == (USER_OBJ | USER),
|
||||
the second w/ check_types == (GROUP_OBJ | GROUP). Each run creates
|
||||
first the deny, then the allow ACEs for the current types. */
|
||||
for (int check_types = USER_OBJ | USER;
|
||||
check_types < CLASS_OBJ;
|
||||
check_types <<= 2)
|
||||
{
|
||||
/* Create deny ACEs for users, then groups. */
|
||||
for (start_idx = idx;
|
||||
idx < nentries && aclbufp[idx].a_type & check_types;
|
||||
++idx)
|
||||
{
|
||||
/* For the rules how to construct the deny access mask, see the
|
||||
comment right at the start of this file. */
|
||||
if (aclbufp[idx].a_type & USER_OBJ)
|
||||
deny = ~aclbufp[idx].a_perm & (class_obj | other_obj);
|
||||
else if (aclbufp[idx].a_type & USER)
|
||||
deny = (aclbufp[idx].a_perm ^ class_obj)
|
||||
| (~aclbufp[idx].a_perm & other_obj);
|
||||
else
|
||||
deny = (aclbufp[idx].a_perm & ~class_obj)
|
||||
| (~aclbufp[idx].a_perm & other_obj);
|
||||
if (!deny)
|
||||
continue;
|
||||
/* Accommodate Windows: Never generate deny masks for SYSTEM
|
||||
and the Administrators group. */
|
||||
if (aclsid[idx] == well_known_system_sid
|
||||
|| aclsid[idx] == well_known_admins_sid)
|
||||
continue;
|
||||
access = 0;
|
||||
if (deny & S_IROTH)
|
||||
access |= FILE_DENY_READ;
|
||||
if (deny & S_IWOTH)
|
||||
access |= (aclbufp[idx].a_type & USER_OBJ)
|
||||
? FILE_DENY_WRITE_OWNER : FILE_DENY_WRITE;
|
||||
if (deny & S_IXOTH)
|
||||
access |= FILE_DENY_EXEC;
|
||||
if (!add_access_denied_ace (acl, access, aclsid[idx], acl_len,
|
||||
inherit))
|
||||
return NULL;
|
||||
}
|
||||
/* Create allow ACEs for users, then groups. */
|
||||
for (idx = start_idx;
|
||||
idx < nentries && aclbufp[idx].a_type & check_types;
|
||||
++idx)
|
||||
{
|
||||
/* Don't set FILE_READ/WRITE_ATTRIBUTES unconditionally on Samba,
|
||||
otherwise it enforces read permissions. */
|
||||
access = STD_RIGHTS_OTHER | (is_samba ? 0 : FILE_READ_ATTRIBUTES);
|
||||
if (aclbufp[idx].a_type & USER_OBJ)
|
||||
{
|
||||
access |= STD_RIGHTS_OWNER;
|
||||
if (!is_samba)
|
||||
access |= FILE_WRITE_ATTRIBUTES;
|
||||
/* Set FILE_DELETE_CHILD on files with "rwx" perms for the
|
||||
owner so that the owner gets "full control" (Duh). */
|
||||
if (aclbufp[idx].a_perm == S_IRWXO)
|
||||
access |= FILE_DELETE_CHILD;
|
||||
}
|
||||
if (aclbufp[idx].a_perm & S_IROTH)
|
||||
access |= FILE_ALLOW_READ;
|
||||
if (aclbufp[idx].a_perm & S_IWOTH)
|
||||
access |= FILE_ALLOW_WRITE;
|
||||
if (aclbufp[idx].a_perm & S_IXOTH)
|
||||
access |= FILE_ALLOW_EXEC;
|
||||
/* Handle S_ISVTX. */
|
||||
if (S_ISDIR (attr)
|
||||
&& (aclbufp[idx].a_perm & (S_IWOTH | S_IXOTH))
|
||||
== (S_IWOTH | S_IXOTH)
|
||||
&& (!(attr & S_ISVTX) || aclbufp[idx].a_type & USER_OBJ))
|
||||
access |= FILE_DELETE_CHILD;
|
||||
if (!add_access_allowed_ace (acl, access, aclsid[idx], acl_len,
|
||||
inherit))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* Create allow ACE for other. It's preceeded by class_obj if it exists.
|
||||
If so, skip it. */
|
||||
if (aclbufp[idx].a_type & CLASS_OBJ)
|
||||
++idx;
|
||||
access = STD_RIGHTS_OTHER | (is_samba ? 0 : FILE_READ_ATTRIBUTES);
|
||||
if (aclbufp[idx].a_perm & S_IROTH)
|
||||
access |= FILE_ALLOW_READ;
|
||||
if (aclbufp[idx].a_perm & S_IWOTH)
|
||||
access |= FILE_ALLOW_WRITE;
|
||||
if (aclbufp[idx].a_perm & S_IXOTH)
|
||||
access |= FILE_ALLOW_EXEC;
|
||||
/* Handle S_ISVTX. */
|
||||
if (S_ISDIR (attr)
|
||||
&& (aclbufp[idx].a_perm & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
|
||||
&& !(attr & S_ISVTX))
|
||||
access |= FILE_DELETE_CHILD;
|
||||
if (!add_access_allowed_ace (acl, access, aclsid[idx++], acl_len,
|
||||
inherit))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set AclSize to computed value. */
|
||||
acl->AclSize = acl_len;
|
||||
debug_printf ("ACL-Size: %u", acl_len);
|
||||
|
@ -370,7 +381,7 @@ setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
|
|||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
/* Make self relative security descriptor in sd_ret. */
|
||||
DWORD sd_size = 0;
|
||||
|
@ -378,20 +389,43 @@ setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
|
|||
if (sd_size <= 0)
|
||||
{
|
||||
__seterrno ();
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
if (!sd_ret.realloc (sd_size))
|
||||
{
|
||||
set_errno (ENOMEM);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
status = RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
debug_printf ("Created SD-Size: %u", sd_ret.size ());
|
||||
return sd_ret;
|
||||
}
|
||||
|
||||
/* This function *requires* a verified and sorted acl list! */
|
||||
int
|
||||
setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
|
||||
bool &writable)
|
||||
{
|
||||
security_descriptor sd, sd_ret;
|
||||
mode_t attr = pc.isdir () ? S_IFDIR : 0;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
if (get_file_sd (handle, pc, sd, false))
|
||||
return -1;
|
||||
if (get_posix_access (sd, &attr, &uid, &gid, NULL, 0) < 0)
|
||||
return -1;
|
||||
if (!set_posix_access (attr, uid, gid, aclbufp, nentries,
|
||||
sd_ret, pc.fs_is_samba ()))
|
||||
return -1;
|
||||
/* FIXME? Caller needs to know if any write perms are set to allow removing
|
||||
the DOS R/O bit. */
|
||||
writable = true;
|
||||
return set_file_sd (handle, pc, sd_ret, false);
|
||||
}
|
||||
|
||||
|
@ -451,6 +485,8 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
|
|||
tmp_pathbuf tp;
|
||||
NTSTATUS status;
|
||||
BOOLEAN dummy, acl_exists;
|
||||
SECURITY_DESCRIPTOR_CONTROL ctrl;
|
||||
ULONG rev;
|
||||
PACL acl;
|
||||
PACCESS_ALLOWED_ACE ace;
|
||||
cygpsid owner_sid, group_sid;
|
||||
|
@ -462,9 +498,11 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
|
|||
cygpsid ace_sid;
|
||||
int pos, type, id, idx;
|
||||
|
||||
bool just_created = false;
|
||||
bool new_style = false;
|
||||
bool saw_user_obj = false;
|
||||
bool saw_group_obj = false;
|
||||
bool saw_other_obj = false;
|
||||
bool saw_def_group_obj = false;
|
||||
bool has_class_perm = false;
|
||||
bool has_def_class_perm = false;
|
||||
|
@ -527,7 +565,10 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
|
|||
uid = owner_sid.get_uid (&cldap);
|
||||
gid = group_sid.get_gid (&cldap);
|
||||
if (attr_ret)
|
||||
attr |= (*attr_ret & S_IFMT);
|
||||
{
|
||||
attr = *attr_ret & S_IFMT;
|
||||
just_created = *attr_ret & S_JUSTCREATED;
|
||||
}
|
||||
|
||||
/* Create and initialize local aclent_t array. */
|
||||
lacl = (aclent_t *) tp.c_get ();
|
||||
|
@ -547,7 +588,18 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
|
|||
goto out;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < acl->AceCount; ++idx)
|
||||
/* Files and dirs are created with a NULL descriptor, so inheritence
|
||||
rules kick in. If no inheritable entries exist in the parent object,
|
||||
Windows will create entries according to the user token's default DACL.
|
||||
These entries are not desired and we ignore them at creation time.
|
||||
We're just checking the SE_DACL_AUTO_INHERITED flag here, since that's
|
||||
what we set in get_file_sd. Read the longish comment there before
|
||||
changing this test! */
|
||||
if (just_created
|
||||
&& NT_SUCCESS (RtlGetControlSecurityDescriptor (psd, &ctrl, &rev))
|
||||
&& !(ctrl & SE_DACL_AUTO_INHERITED))
|
||||
;
|
||||
else for (idx = 0; idx < acl->AceCount; ++idx)
|
||||
{
|
||||
if (!NT_SUCCESS (RtlGetAce (acl, idx, (PVOID *) &ace)))
|
||||
continue;
|
||||
|
@ -567,10 +619,10 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
|
|||
USER, GROUP and GROUP_OBJ entries. Any ACL not created that
|
||||
way has been rearranged by the Windows functionality to create
|
||||
the brain-dead "canonical" ACL order and is broken anyway. */
|
||||
new_style = true;
|
||||
attr |= CYG_ACE_ISBITS_TO_POSIX (ace->Mask);
|
||||
if (ace->Mask & CYG_ACE_MASK_VALID)
|
||||
{
|
||||
new_style = true;
|
||||
if (!(ace->Header.AceFlags & INHERIT_ONLY))
|
||||
{
|
||||
if ((pos = searchace (lacl, MAX_ACL_ENTRIES, CLASS_OBJ))
|
||||
|
@ -613,6 +665,9 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
|
|||
{
|
||||
type = OTHER_OBJ;
|
||||
id = ILLEGAL_GID;
|
||||
if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE
|
||||
&& !(ace->Header.AceFlags & INHERIT_ONLY))
|
||||
saw_other_obj = true;
|
||||
}
|
||||
else if (ace_sid == well_known_creator_owner_sid)
|
||||
{
|
||||
|
@ -632,6 +687,15 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
|
|||
id = ace_sid.get_id (TRUE, &type, &cldap);
|
||||
if (!type)
|
||||
continue;
|
||||
/* If the SGID attribute is set on a new-style Cygwin ACL on
|
||||
a just created file or dir, the first group in the ACL is
|
||||
the desired primary group of the new object. */
|
||||
if (just_created && new_style && attr & S_ISGID
|
||||
&& !saw_group_obj && type == GROUP)
|
||||
{
|
||||
type = GROUP_OBJ;
|
||||
lacl[1].a_id = gid = id;
|
||||
}
|
||||
}
|
||||
if (!(ace->Header.AceFlags & INHERIT_ONLY || type & ACL_DEFAULT))
|
||||
{
|
||||
|
@ -655,13 +719,17 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
|
|||
if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
|
||||
{
|
||||
getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType,
|
||||
new_style && type & (USER | GROUP));
|
||||
new_style && type & (USER | GROUP_OBJ | GROUP));
|
||||
if (!new_style)
|
||||
{
|
||||
/* Fix up CLASS_OBJ value. */
|
||||
if (type & (USER | GROUP))
|
||||
if (type & (USER | GROUP_OBJ | GROUP))
|
||||
{
|
||||
has_class_perm = true;
|
||||
/* Accommodate Windows: Never add SYSTEM and Admins
|
||||
perms to CLASS_OBJ perms. */
|
||||
if (ace_sid != well_known_system_sid
|
||||
&& ace_sid != well_known_admins_sid)
|
||||
class_perm |= lacl[pos].a_perm;
|
||||
}
|
||||
}
|
||||
|
@ -686,17 +754,21 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
|
|||
if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
|
||||
{
|
||||
getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType,
|
||||
new_style && type & (USER | GROUP));
|
||||
new_style && type & (USER | GROUP_OBJ | GROUP));
|
||||
if (!new_style)
|
||||
{
|
||||
/* Fix up DEF_CLASS_OBJ value. */
|
||||
if (type & (USER | GROUP))
|
||||
if (type & (USER | GROUP_OBJ | GROUP))
|
||||
{
|
||||
has_def_class_perm = true;
|
||||
/* Accommodate Windows: Never add SYSTEM and Admins
|
||||
perms to CLASS_OBJ perms. */
|
||||
if (ace_sid != well_known_system_sid
|
||||
&& ace_sid != well_known_admins_sid)
|
||||
def_class_perm |= lacl[pos].a_perm;
|
||||
}
|
||||
/* And note the position of the DEF_GROUP_OBJ entry. */
|
||||
else if (type == DEF_GROUP_OBJ)
|
||||
if (type == DEF_GROUP_OBJ)
|
||||
def_pgrp_pos = pos;
|
||||
}
|
||||
}
|
||||
|
@ -713,6 +785,21 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
|
|||
lacl[pos].a_id = ILLEGAL_GID;
|
||||
lacl[pos].a_perm = class_perm | lacl[1].a_perm;
|
||||
}
|
||||
/* If this is a just created file, and there are no default permissions
|
||||
(probably no inherited ACEs so created from a default DACL), assign
|
||||
the permissions specified by the file creation mask. The values get
|
||||
masked by the actually requested permissions by the caller.
|
||||
See POSIX 1003.1e draft 17. */
|
||||
if (just_created)
|
||||
{
|
||||
mode_t perms = (S_IRWXU | S_IRWXG | S_IRWXO) & ~cygheap->umask;
|
||||
if (!saw_user_obj)
|
||||
lacl[0].a_perm = (perms >> 6) & S_IRWXO;
|
||||
if (!saw_group_obj)
|
||||
lacl[1].a_perm = (perms >> 3) & S_IRWXO;
|
||||
if (!saw_other_obj)
|
||||
lacl[2].a_perm = perms & S_IRWXO;
|
||||
}
|
||||
/* Ensure that the default acl contains at least
|
||||
DEF_(USER|GROUP|OTHER)_OBJ entries. */
|
||||
if (types_def && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
|
||||
|
|
|
@ -15,6 +15,7 @@ details. */
|
|||
#include "winsup.h"
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/acl.h>
|
||||
#include "cygerrno.h"
|
||||
#include "security.h"
|
||||
#include "path.h"
|
||||
|
@ -34,7 +35,6 @@ static GENERIC_MAPPING NO_COPY_RO file_mapping = { FILE_GENERIC_READ,
|
|||
FILE_GENERIC_WRITE,
|
||||
FILE_GENERIC_EXECUTE,
|
||||
FILE_ALL_ACCESS };
|
||||
|
||||
LONG
|
||||
get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd,
|
||||
bool justcreated)
|
||||
|
@ -85,62 +85,46 @@ get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd,
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
/* Ok, so we have a security descriptor now. Unfortunately, if you want
|
||||
to know if an ACE is inherited from the parent object, you can't just
|
||||
call NtQuerySecurityObject once. The problem is this:
|
||||
/* We have a security descriptor now. Unfortunately, if you want to know
|
||||
if an ACE is inherited from the parent object, this isn't sufficient.
|
||||
|
||||
In the simple case, the SDs control word contains one of the
|
||||
SE_DACL_AUTO_INHERITED or SE_DACL_PROTECTED flags, or at least one of
|
||||
the ACEs has the INHERITED_ACE flag set. In all of these cases the
|
||||
GetSecurityInfo function calls NtQuerySecurityObject only once, too,
|
||||
apparently because it figures that the DACL is self-sufficient, which
|
||||
it usually is. Windows Explorer, for instance, takes great care to
|
||||
set these flags in a security descriptor if you change the ACL in the
|
||||
GUI property dialog.
|
||||
the ACEs has the INHERITED_ACE flag set. In all of these cases we
|
||||
know the DACL has been inherited.
|
||||
|
||||
The tricky case is if none of these flags is set in the SD. That means
|
||||
the information whether or not an ACE has been inherited is not available
|
||||
in the DACL of the object. In this case GetSecurityInfo also fetches the
|
||||
SD from the parent directory and tests if the object's SD contains
|
||||
inherited ACEs from the parent. The below code is closly emulating the
|
||||
behaviour of GetSecurityInfo so we can get rid of this advapi32 dependency.
|
||||
If none of these flags is set in the SD, the information whether
|
||||
or not an ACE has been inherited is not available in the DACL of the
|
||||
object. In this case GetSecurityInfo fetches the SD from the parent
|
||||
directory and tests if the object's SD contains inherited ACEs from the
|
||||
parent.
|
||||
|
||||
However, this functionality is slow, and the extra information is only
|
||||
required when the file has been created and the permissions are about
|
||||
to be set to POSIX permissions. Therefore we only use it in case the
|
||||
file just got created.
|
||||
Note that we're not testing the SE_DACL_AUTO_INHERITED and
|
||||
SE_DACL_PROTECTED flags here because we know the state the file's SD
|
||||
is in. Since we're creating all files with a NULL descriptor, the DACL
|
||||
is either inherited from the parent, or it's the default DACL. In
|
||||
neither case, one of these flags is set.
|
||||
|
||||
Note that GetSecurityInfo has a problem on 5.1 and 5.2 kernels. Sometimes
|
||||
it returns ERROR_INVALID_ADDRESS if a former request for the parent
|
||||
directories' SD used NtQuerySecurityObject, rather than GetSecurityInfo
|
||||
as well. See http://cygwin.com/ml/cygwin-developers/2011-03/msg00027.html
|
||||
for the solution. This problem does not occur with the below code, so
|
||||
the workaround has been removed. */
|
||||
For speed, we're not calling RtlConvertToAutoInheritSecurityObject
|
||||
anymore (but keep the code here for reference). Rather we just test
|
||||
if one of the parent's ACEs is inheritable. If so, we know we inherited
|
||||
it and set the SE_DACL_AUTO_INHERITED flag. If not, we may assume our
|
||||
object's DACL is the default DACL.
|
||||
|
||||
This functionality is slow and the extra information is only required
|
||||
when the file has been created and the permissions are about to be set
|
||||
to POSIX permissions. Therefore we only use it in case the file just
|
||||
got created. */
|
||||
if (justcreated)
|
||||
{
|
||||
SECURITY_DESCRIPTOR_CONTROL ctrl;
|
||||
ULONG dummy;
|
||||
PACL dacl;
|
||||
BOOLEAN exists, def;
|
||||
ACCESS_ALLOWED_ACE *ace;
|
||||
UNICODE_STRING dirname;
|
||||
PSECURITY_DESCRIPTOR psd, nsd;
|
||||
PSECURITY_DESCRIPTOR psd;
|
||||
tmp_pathbuf tp;
|
||||
|
||||
/* Check SDs control flags. If SE_DACL_AUTO_INHERITED or
|
||||
SE_DACL_PROTECTED is set we're done. */
|
||||
RtlGetControlSecurityDescriptor (sd, &ctrl, &dummy);
|
||||
if (ctrl & (SE_DACL_AUTO_INHERITED | SE_DACL_PROTECTED))
|
||||
return 0;
|
||||
/* Otherwise iterate over the ACEs and see if any one of them has the
|
||||
INHERITED_ACE flag set. If so, we're done. */
|
||||
if (NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd, &exists, &dacl, &def))
|
||||
&& exists && dacl)
|
||||
for (ULONG idx = 0; idx < dacl->AceCount; ++idx)
|
||||
if (NT_SUCCESS (RtlGetAce (dacl, idx, (PVOID *) &ace))
|
||||
&& (ace->Header.AceFlags & INHERITED_ACE))
|
||||
return 0;
|
||||
/* Otherwise, open the parent directory with READ_CONTROL... */
|
||||
/* Open the parent directory with READ_CONTROL... */
|
||||
RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
|
||||
InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
|
||||
NULL, NULL);
|
||||
|
@ -164,12 +148,14 @@ get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd,
|
|||
&dirname, status);
|
||||
return 0;
|
||||
}
|
||||
#if 0
|
||||
/* ... and create a new security descriptor in which all inherited ACEs
|
||||
are marked with the INHERITED_ACE flag. For a description of the
|
||||
undocumented RtlConvertToAutoInheritSecurityObject function from
|
||||
ntdll.dll see the MSDN man page for the advapi32 function
|
||||
ConvertToAutoInheritPrivateObjectSecurity. Fortunately the latter
|
||||
is just a shim. */
|
||||
PSECURITY_DESCRIPTOR nsd;
|
||||
status = RtlConvertToAutoInheritSecurityObject (psd, sd, &nsd, NULL,
|
||||
pc.isdir (),
|
||||
&file_mapping);
|
||||
|
@ -185,6 +171,36 @@ get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd,
|
|||
len = RtlLengthSecurityDescriptor (nsd);
|
||||
memcpy ((PSECURITY_DESCRIPTOR) sd, nsd, len);
|
||||
RtlDeleteSecurityObject (&nsd);
|
||||
#else
|
||||
/* ... and check the parent descriptor for inheritable ACEs matching
|
||||
our current object type (file/dir). The simple truth in our case
|
||||
is, either the parent dir had inheritable ACEs and all our ACEs are
|
||||
inherited, or the parent dir didn't have inheritable ACEs and all
|
||||
our ACEs are taken from the default DACL. */
|
||||
bool inherited = false;
|
||||
BYTE search_flags = pc.isdir () ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
|
||||
: SUB_OBJECTS_ONLY_INHERIT;
|
||||
if (NT_SUCCESS (RtlGetDaclSecurityDescriptor (psd, &exists, &dacl, &def))
|
||||
&& exists && dacl)
|
||||
for (ULONG idx = 0; idx < dacl->AceCount; ++idx)
|
||||
if (NT_SUCCESS (RtlGetAce (dacl, idx, (PVOID *) &ace))
|
||||
&& (ace->Header.AceFlags & search_flags))
|
||||
{
|
||||
inherited = true;
|
||||
break;
|
||||
}
|
||||
/* Then, if the parent descriptor contained inheritable ACEs, we mark
|
||||
the SD as SE_DACL_AUTO_INHERITED. Note that this requires the
|
||||
matching check in get_posix_access. If we ever revert to
|
||||
RtlConvertToAutoInheritSecurityObject, the check in get_posix_access
|
||||
has to test every single ACE for the INHERITED_ACE flag again. */
|
||||
if (inherited
|
||||
&& NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd, &exists, &dacl,
|
||||
&def))
|
||||
&& exists && dacl)
|
||||
RtlSetControlSecurityDescriptor (sd, SE_DACL_AUTO_INHERITED,
|
||||
SE_DACL_AUTO_INHERITED);
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -340,363 +356,6 @@ add_access_denied_ace (PACL acl, DWORD attributes, PSID sid, size_t &len_add,
|
|||
return true;
|
||||
}
|
||||
|
||||
static PSECURITY_DESCRIPTOR
|
||||
alloc_sd (path_conv &pc, uid_t uid, gid_t gid, int attribute,
|
||||
security_descriptor &sd_ret)
|
||||
{
|
||||
NTSTATUS status;
|
||||
BOOLEAN dummy;
|
||||
tmp_pathbuf tp;
|
||||
|
||||
/* NOTE: If the high bit of attribute is set, we have just created
|
||||
a file or directory. See below for an explanation. */
|
||||
|
||||
debug_printf("uid %u, gid %u, attribute 0%o", uid, gid, attribute);
|
||||
|
||||
/* Get owner and group from current security descriptor. */
|
||||
PSID cur_owner_sid = NULL;
|
||||
PSID cur_group_sid = NULL;
|
||||
status = RtlGetOwnerSecurityDescriptor (sd_ret, &cur_owner_sid, &dummy);
|
||||
if (!NT_SUCCESS (status))
|
||||
debug_printf ("RtlGetOwnerSecurityDescriptor: %y", status);
|
||||
status = RtlGetGroupSecurityDescriptor (sd_ret, &cur_group_sid, &dummy);
|
||||
if (!NT_SUCCESS (status))
|
||||
debug_printf ("RtlGetGroupSecurityDescriptor: %y", status);
|
||||
|
||||
/* Get SID of owner. */
|
||||
cygsid owner_sid;
|
||||
/* Check for current user first */
|
||||
if (uid == myself->uid)
|
||||
owner_sid = cygheap->user.sid ();
|
||||
else if (uid == ILLEGAL_UID)
|
||||
owner_sid = cur_owner_sid;
|
||||
else if (!owner_sid.getfrompw (internal_getpwuid (uid)))
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
owner_sid.debug_print ("alloc_sd: owner SID =");
|
||||
|
||||
/* Get SID of new group. */
|
||||
cygsid group_sid;
|
||||
/* Check for current user first */
|
||||
if (gid == myself->gid)
|
||||
group_sid = cygheap->user.groups.pgsid;
|
||||
else if (gid == ILLEGAL_GID)
|
||||
group_sid = cur_group_sid;
|
||||
else if (!group_sid.getfromgr (internal_getgrgid (gid)))
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
group_sid.debug_print ("alloc_sd: group SID =");
|
||||
|
||||
/* Initialize local security descriptor. */
|
||||
SECURITY_DESCRIPTOR sd;
|
||||
RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
|
||||
|
||||
/* We set the SE_DACL_PROTECTED flag here to prevent the DACL from being
|
||||
modified by inheritable ACEs. */
|
||||
RtlSetControlSecurityDescriptor (&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
|
||||
|
||||
/* Create owner for local security descriptor. */
|
||||
status = RtlSetOwnerSecurityDescriptor (&sd, owner_sid, FALSE);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create group for local security descriptor. */
|
||||
status = RtlSetGroupSecurityDescriptor (&sd, group_sid, FALSE);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize local access control list. */
|
||||
PACL acl = (PACL) tp.w_get ();
|
||||
RtlCreateAcl (acl, ACL_MAXIMUM_SIZE, ACL_REVISION);
|
||||
|
||||
/* From here fill ACL. */
|
||||
size_t acl_len = sizeof (ACL);
|
||||
/* Only used for sync objects (for ttys). The admins group should
|
||||
always have the right to manipulate the ACL, so we have to make sure
|
||||
that the ACL gives the admins group STANDARD_RIGHTS_ALL access. */
|
||||
bool saw_admins = false;
|
||||
|
||||
/* Construct allow attribute for owner.
|
||||
Don't set FILE_READ/WRITE_ATTRIBUTES unconditionally on Samba, otherwise
|
||||
it enforces read permissions. Same for other's below. */
|
||||
DWORD owner_allow = STANDARD_RIGHTS_ALL
|
||||
| (pc.fs_is_samba ()
|
||||
? 0 : (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES));
|
||||
if (attribute & S_IRUSR)
|
||||
owner_allow |= FILE_GENERIC_READ;
|
||||
if (attribute & S_IWUSR)
|
||||
owner_allow |= FILE_GENERIC_WRITE;
|
||||
if (attribute & S_IXUSR)
|
||||
owner_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
|
||||
if (S_ISDIR (attribute)
|
||||
&& (attribute & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR))
|
||||
owner_allow |= FILE_DELETE_CHILD;
|
||||
/* For sync objects note that the owner is admin. */
|
||||
if (S_ISCHR (attribute) && owner_sid == well_known_admins_sid)
|
||||
saw_admins = true;
|
||||
|
||||
/* Construct allow attribute for group. */
|
||||
DWORD group_allow = STANDARD_RIGHTS_READ | SYNCHRONIZE
|
||||
| (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
|
||||
if (attribute & S_IRGRP)
|
||||
group_allow |= FILE_GENERIC_READ;
|
||||
if (attribute & S_IWGRP)
|
||||
group_allow |= FILE_GENERIC_WRITE;
|
||||
if (attribute & S_IXGRP)
|
||||
group_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
|
||||
if (S_ISDIR (attribute)
|
||||
&& (attribute & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP)
|
||||
&& !(attribute & S_ISVTX))
|
||||
group_allow |= FILE_DELETE_CHILD;
|
||||
/* For sync objects, add STANDARD_RIGHTS_ALL for admins group. */
|
||||
if (S_ISCHR (attribute) && group_sid == well_known_admins_sid)
|
||||
{
|
||||
group_allow |= STANDARD_RIGHTS_ALL;
|
||||
saw_admins = true;
|
||||
}
|
||||
|
||||
/* Construct allow attribute for everyone. */
|
||||
DWORD other_allow = STANDARD_RIGHTS_READ | SYNCHRONIZE
|
||||
| (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
|
||||
if (attribute & S_IROTH)
|
||||
other_allow |= FILE_GENERIC_READ;
|
||||
if (attribute & S_IWOTH)
|
||||
other_allow |= FILE_GENERIC_WRITE;
|
||||
if (attribute & S_IXOTH)
|
||||
other_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
|
||||
if (S_ISDIR (attribute)
|
||||
&& (attribute & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
|
||||
&& !(attribute & S_ISVTX))
|
||||
other_allow |= FILE_DELETE_CHILD;
|
||||
|
||||
/* Construct SUID, SGID and VTX bits in NULL ACE. */
|
||||
DWORD null_allow = 0L;
|
||||
if (attribute & (S_ISUID | S_ISGID | S_ISVTX))
|
||||
{
|
||||
if (attribute & S_ISUID)
|
||||
null_allow |= FILE_APPEND_DATA;
|
||||
if (attribute & S_ISGID)
|
||||
null_allow |= FILE_WRITE_DATA;
|
||||
if (attribute & S_ISVTX)
|
||||
null_allow |= FILE_READ_DATA;
|
||||
}
|
||||
|
||||
/* Add owner and group permissions if SIDs are equal
|
||||
and construct deny attributes for group and owner. */
|
||||
bool isownergroup;
|
||||
if ((isownergroup = (owner_sid == group_sid)))
|
||||
owner_allow |= group_allow;
|
||||
|
||||
DWORD owner_deny = ~owner_allow & (group_allow | other_allow);
|
||||
owner_deny &= ~(STANDARD_RIGHTS_READ
|
||||
| FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES);
|
||||
|
||||
DWORD group_deny = ~group_allow & other_allow;
|
||||
group_deny &= ~(STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES);
|
||||
|
||||
/* Set deny ACE for owner. */
|
||||
if (owner_deny
|
||||
&& !add_access_denied_ace (acl, owner_deny, owner_sid, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return NULL;
|
||||
/* Set deny ACE for group here to respect the canonical order,
|
||||
if this does not impact owner */
|
||||
if (group_deny && !(group_deny & owner_allow) && !isownergroup
|
||||
&& !add_access_denied_ace (acl, group_deny, group_sid, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return NULL;
|
||||
/* Set allow ACE for owner. */
|
||||
if (!add_access_allowed_ace (acl, owner_allow, owner_sid, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return NULL;
|
||||
/* Set deny ACE for group, if still needed. */
|
||||
if ((group_deny & owner_allow) && !isownergroup
|
||||
&& !add_access_denied_ace (acl, group_deny, group_sid, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return NULL;
|
||||
/* Set allow ACE for group. */
|
||||
if (!isownergroup
|
||||
&& !add_access_allowed_ace (acl, group_allow, group_sid, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return NULL;
|
||||
|
||||
/* For sync objects, if we didn't see the admins group so far, add entry
|
||||
with STANDARD_RIGHTS_ALL access. */
|
||||
if (S_ISCHR (attribute) && !saw_admins)
|
||||
{
|
||||
if (!add_access_allowed_ace (acl, STANDARD_RIGHTS_ALL,
|
||||
well_known_admins_sid, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return NULL;
|
||||
saw_admins = true;
|
||||
}
|
||||
|
||||
/* Set allow ACE for everyone. */
|
||||
if (!add_access_allowed_ace (acl, other_allow, well_known_world_sid, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return NULL;
|
||||
/* Set null ACE for special bits. */
|
||||
if (null_allow
|
||||
&& !add_access_allowed_ace (acl, null_allow, well_known_null_sid, acl_len,
|
||||
NO_INHERITANCE))
|
||||
return NULL;
|
||||
|
||||
/* Fill ACL with unrelated ACEs from current security descriptor. */
|
||||
PACL oacl;
|
||||
BOOLEAN acl_exists = FALSE;
|
||||
ACCESS_ALLOWED_ACE *ace;
|
||||
|
||||
status = RtlGetDaclSecurityDescriptor (sd_ret, &acl_exists, &oacl, &dummy);
|
||||
if (NT_SUCCESS (status) && acl_exists && oacl)
|
||||
for (DWORD i = 0; i < oacl->AceCount; ++i)
|
||||
if (NT_SUCCESS (RtlGetAce (oacl, i, (PVOID *) &ace)))
|
||||
{
|
||||
cygpsid ace_sid ((PSID) &ace->SidStart);
|
||||
|
||||
/* Always skip NULL SID as well as admins SID on virtual device files
|
||||
in /proc/sys. */
|
||||
if (ace_sid == well_known_null_sid
|
||||
|| (S_ISCHR (attribute) && ace_sid == well_known_admins_sid))
|
||||
continue;
|
||||
/* Check for ACEs which are always created in the preceding code
|
||||
and check for the default inheritence ACEs which will be created
|
||||
for just created directories. Skip them for just created
|
||||
directories or if they are not inherited. If they are inherited,
|
||||
make sure they are *only* inherited, so they don't collide with
|
||||
the permissions set in this function. */
|
||||
if ((ace_sid == cur_owner_sid)
|
||||
|| (ace_sid == owner_sid)
|
||||
|| (ace_sid == cur_group_sid)
|
||||
|| (ace_sid == group_sid)
|
||||
|| (ace_sid == well_known_creator_owner_sid)
|
||||
|| (ace_sid == well_known_creator_group_sid)
|
||||
|| (ace_sid == well_known_world_sid))
|
||||
{
|
||||
if ((S_ISDIR (attribute) && (attribute & S_JUSTCREATED))
|
||||
|| (ace->Header.AceFlags
|
||||
& (SUB_CONTAINERS_AND_OBJECTS_INHERIT)) == 0)
|
||||
continue;
|
||||
else
|
||||
ace->Header.AceFlags |= INHERIT_ONLY;
|
||||
}
|
||||
if (attribute & S_JUSTCREATED)
|
||||
{
|
||||
/* Since files and dirs are created with a NULL descriptor,
|
||||
inheritence rules kick in. If no inheritable entries exist
|
||||
in the parent object, Windows will create entries from the
|
||||
user token's default DACL in the file DACL. These entries
|
||||
are not desired and we drop them silently. */
|
||||
if (!(ace->Header.AceFlags & INHERITED_ACE))
|
||||
continue;
|
||||
/* Remove the INHERITED_ACE flag since on POSIX systems
|
||||
inheritance is settled when the file has been created.
|
||||
This also avoids error messages in Windows Explorer when
|
||||
opening a file's security tab. Explorer complains if
|
||||
inheritable ACEs are preceding non-inheritable ACEs. */
|
||||
ace->Header.AceFlags &= ~INHERITED_ACE;
|
||||
/* However, if the newly created object is a directory,
|
||||
it inherits the default ACL from its parent, so mark
|
||||
all unrelated, inherited ACEs inheritable. */
|
||||
if (S_ISDIR (attribute))
|
||||
ace->Header.AceFlags |= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
|
||||
}
|
||||
else if (uid == ILLEGAL_UID && gid == ILLEGAL_UID
|
||||
&& ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE
|
||||
&& ace_sid != well_known_creator_group_sid
|
||||
&& ace_sid != well_known_creator_owner_sid
|
||||
&& ace_sid != well_known_world_sid)
|
||||
{
|
||||
/* FIXME: Temporary workaround for the problem that chmod does
|
||||
not affect the group permissions if other users and groups
|
||||
in the ACL have more permissions than the primary group due
|
||||
to the CLASS_OBJ emulation. The temporary workaround is to
|
||||
disallow any secondary ACE in the ACL more permissions than
|
||||
the primary group when writing a new ACL via chmod. */
|
||||
ace->Mask &= group_allow;
|
||||
}
|
||||
/* Add unrelated ACCESS_DENIED_ACE to the beginning but behind
|
||||
the owner_deny, ACCESS_ALLOWED_ACE to the end. FIXME: this
|
||||
would break the order of the inherit-only ACEs. */
|
||||
status = RtlAddAce (acl, ACL_REVISION,
|
||||
ace->Header.AceType == ACCESS_DENIED_ACE_TYPE
|
||||
? (owner_deny ? 1 : 0) : MAXDWORD,
|
||||
(LPVOID) ace, ace->Header.AceSize);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return NULL;
|
||||
}
|
||||
acl_len += ace->Header.AceSize;
|
||||
}
|
||||
|
||||
/* Construct appropriate inherit attribute for new directories. Keep in
|
||||
mind that we do this only for the sake of non-Cygwin applications.
|
||||
Cygwin applications don't need this. */
|
||||
if (S_ISDIR (attribute) && (attribute & S_JUSTCREATED))
|
||||
{
|
||||
const DWORD inherit = SUB_CONTAINERS_AND_OBJECTS_INHERIT | INHERIT_ONLY;
|
||||
/* Set allow ACE for owner. */
|
||||
if (!add_access_allowed_ace (acl, owner_allow,
|
||||
well_known_creator_owner_sid, acl_len,
|
||||
inherit))
|
||||
return NULL;
|
||||
/* Set allow ACE for group. */
|
||||
if (!add_access_allowed_ace (acl, group_allow,
|
||||
well_known_creator_group_sid, acl_len,
|
||||
inherit))
|
||||
return NULL;
|
||||
/* Set allow ACE for everyone. */
|
||||
if (!add_access_allowed_ace (acl, other_allow, well_known_world_sid,
|
||||
acl_len, inherit))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set AclSize to computed value. */
|
||||
acl->AclSize = acl_len;
|
||||
debug_printf ("ACL-Size: %d", acl_len);
|
||||
|
||||
/* Create DACL for local security descriptor. */
|
||||
status = RtlSetDaclSecurityDescriptor (&sd, TRUE, acl, FALSE);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Make self relative security descriptor. */
|
||||
DWORD sd_size = 0;
|
||||
RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
|
||||
if (sd_size <= 0)
|
||||
{
|
||||
__seterrno ();
|
||||
return NULL;
|
||||
}
|
||||
if (!sd_ret.malloc (sd_size))
|
||||
{
|
||||
set_errno (ENOMEM);
|
||||
return NULL;
|
||||
}
|
||||
status = RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return NULL;
|
||||
}
|
||||
debug_printf ("Created SD-Size: %u", sd_ret.size ());
|
||||
|
||||
return sd_ret;
|
||||
}
|
||||
|
||||
void
|
||||
set_security_attribute (path_conv &pc, int attribute, PSECURITY_ATTRIBUTES psa,
|
||||
security_descriptor &sd)
|
||||
|
@ -704,8 +363,9 @@ set_security_attribute (path_conv &pc, int attribute, PSECURITY_ATTRIBUTES psa,
|
|||
psa->lpSecurityDescriptor = sd.malloc (SECURITY_DESCRIPTOR_MIN_LENGTH);
|
||||
RtlCreateSecurityDescriptor ((PSECURITY_DESCRIPTOR) psa->lpSecurityDescriptor,
|
||||
SECURITY_DESCRIPTOR_REVISION);
|
||||
psa->lpSecurityDescriptor = alloc_sd (pc, geteuid32 (), getegid32 (),
|
||||
attribute, sd);
|
||||
psa->lpSecurityDescriptor = set_posix_access (attribute, geteuid32 (),
|
||||
getegid32 (), NULL, 0,
|
||||
sd, false);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -744,8 +404,8 @@ get_object_attribute (HANDLE handle, uid_t *uidret, gid_t *gidret,
|
|||
|
||||
if (get_object_sd (handle, sd))
|
||||
return -1;
|
||||
get_posix_access (sd, attribute, uidret, gidret, NULL, 0);
|
||||
return 0;
|
||||
return get_posix_access (sd, attribute, uidret, gidret, NULL, 0) >= 0
|
||||
? 0 : -1;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -754,7 +414,7 @@ create_object_sd_from_attribute (HANDLE handle, uid_t uid, gid_t gid,
|
|||
{
|
||||
path_conv pc;
|
||||
if ((handle && get_object_sd (handle, sd))
|
||||
|| !alloc_sd (pc, uid, gid, attribute, sd))
|
||||
|| !set_posix_access (attribute, uid, gid, NULL, 0, sd, false))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -786,24 +446,72 @@ set_object_attribute (HANDLE handle, uid_t uid, gid_t gid,
|
|||
}
|
||||
|
||||
int
|
||||
set_file_attribute (HANDLE handle, path_conv &pc,
|
||||
uid_t uid, gid_t gid, mode_t attribute)
|
||||
set_created_file_access (HANDLE handle, path_conv &pc, mode_t attr)
|
||||
{
|
||||
int ret = -1;
|
||||
security_descriptor sd, sd_ret;
|
||||
mode_t attr_rd;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
tmp_pathbuf tp;
|
||||
aclent_t *aclp;
|
||||
int nentries, idx;
|
||||
|
||||
if (pc.has_acls ())
|
||||
if (!get_file_sd (handle, pc, sd, true))
|
||||
{
|
||||
security_descriptor sd;
|
||||
|
||||
if (!get_file_sd (handle, pc, sd, (bool)(attribute & S_JUSTCREATED))
|
||||
&& alloc_sd (pc, uid, gid, attribute, sd))
|
||||
ret = set_file_sd (handle, pc, sd,
|
||||
uid != ILLEGAL_UID || gid != ILLEGAL_GID);
|
||||
}
|
||||
attr |= S_JUSTCREATED;
|
||||
if (pc.isdir ())
|
||||
attr |= S_IFDIR;
|
||||
attr_rd = attr;
|
||||
aclp = (aclent_t *) tp.c_get ();
|
||||
if ((nentries = get_posix_access (sd, &attr_rd, &uid, &gid,
|
||||
aclp, MAX_ACL_ENTRIES)) >= 0)
|
||||
{
|
||||
/* Symlinks always get the request POSIX perms. */
|
||||
if (S_ISLNK (attr))
|
||||
attr_rd = 0777;
|
||||
/* Overwrite ACL permissions as required by POSIX 1003.1e
|
||||
draft 17. */
|
||||
aclp[0].a_perm = ((attr & attr_rd) >> 6) & S_IRWXO;
|
||||
if (nentries > MIN_ACL_ENTRIES
|
||||
&& (idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0)
|
||||
aclp[idx].a_perm = ((attr & attr_rd) >> 3) & S_IRWXO;
|
||||
else
|
||||
ret = 0;
|
||||
syscall_printf ("%d = set_file_attribute(%S, %d, %d, 0%o)",
|
||||
ret, pc.get_nt_native_path (), uid, gid, attribute);
|
||||
aclp[1].a_perm = ((attr & attr_rd) >> 3) & S_IRWXO;
|
||||
if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0)
|
||||
aclp[idx].a_perm = (attr & attr_rd) & S_IRWXO;
|
||||
/* Construct appropriate inherit attribute for new directories.
|
||||
Basically we do this only for the sake of non-Cygwin applications.
|
||||
Cygwin applications don't need these. Additionally, if the
|
||||
S_ISGID bit is set, propagate it. */
|
||||
if (S_ISDIR (attr))
|
||||
{
|
||||
if (searchace (aclp, nentries, DEF_USER_OBJ) < 0)
|
||||
{
|
||||
aclp[nentries].a_type = DEF_USER_OBJ;
|
||||
aclp[nentries].a_id = ILLEGAL_UID;
|
||||
aclp[nentries++].a_perm = (attr >> 6) & S_IRWXO;
|
||||
}
|
||||
if (searchace (aclp, nentries, DEF_GROUP_OBJ) < 0)
|
||||
{
|
||||
aclp[nentries].a_type = DEF_GROUP_OBJ;
|
||||
aclp[nentries].a_id = ILLEGAL_GID;
|
||||
aclp[nentries++].a_perm = (attr >> 3) & S_IRWXO;
|
||||
}
|
||||
if (searchace (aclp, nentries, DEF_OTHER_OBJ) < 0)
|
||||
{
|
||||
aclp[nentries].a_type = DEF_OTHER_OBJ;
|
||||
aclp[nentries].a_id = ILLEGAL_UID;
|
||||
aclp[nentries++].a_perm = attr & S_IRWXO;
|
||||
}
|
||||
if (attr_rd & S_ISGID)
|
||||
attr |= S_ISGID;
|
||||
}
|
||||
if (set_posix_access (attr, uid, gid, aclp, nentries, sd_ret,
|
||||
pc.fs_is_samba ()))
|
||||
ret = set_file_sd (handle, pc, sd_ret, attr_rd & S_ISGID);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -436,8 +436,7 @@ class path_conv;
|
|||
/* File manipulation */
|
||||
int __reg3 get_file_attribute (HANDLE, path_conv &, mode_t *,
|
||||
uid_t *, gid_t *);
|
||||
int __reg3 set_file_attribute (HANDLE, path_conv &,
|
||||
uid_t, gid_t, mode_t);
|
||||
int __reg3 set_created_file_access (HANDLE, path_conv &, mode_t);
|
||||
int __reg2 get_object_sd (HANDLE, security_descriptor &);
|
||||
int __reg3 get_object_attribute (HANDLE, uid_t *, gid_t *, mode_t *);
|
||||
int __reg3 set_object_attribute (HANDLE, uid_t, gid_t, mode_t);
|
||||
|
@ -463,6 +462,9 @@ bool get_sids_info (cygpsid, cygpsid, uid_t * , gid_t *);
|
|||
struct acl;
|
||||
extern "C" int aclsort32 (int, int, struct acl *);
|
||||
extern "C" int acl32 (const char *, int, int, struct acl *);
|
||||
int searchace (struct acl *, int, int, uid_t id = ILLEGAL_UID);
|
||||
PSECURITY_DESCRIPTOR set_posix_access (mode_t, uid_t, gid_t, struct acl *, int,
|
||||
security_descriptor &, bool);
|
||||
int get_posix_access (PSECURITY_DESCRIPTOR, mode_t *, uid_t *, gid_t *,
|
||||
struct acl *, int);
|
||||
int getacl (HANDLE, path_conv &, int, struct acl *);
|
||||
|
|
|
@ -1412,8 +1412,7 @@ open (const char *unix_path, int flags, ...)
|
|||
}
|
||||
else if ((fh->is_fs_special ()
|
||||
&& fh->device_access_denied (flags))
|
||||
|| !fh->open_with_arch (flags, (mode & 07777)
|
||||
& ~cygheap->umask))
|
||||
|| !fh->open_with_arch (flags, mode & 07777))
|
||||
delete fh;
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue