diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2015-04-10 12:25:40 +0300 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2015-04-10 12:39:15 +0300 |
commit | a44e09fd49296664c6ced76bae5cc70b3063e3e9 (patch) | |
tree | c6e4062c03c698d1f49bccc3ea1f1dbfd148c57b /winsup | |
parent | aadd5f029569350d590cff33c7a3c643601fd5db (diff) |
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>
Diffstat (limited to 'winsup')
-rw-r--r-- | winsup/cygwin/ChangeLog | 41 | ||||
-rw-r--r-- | winsup/cygwin/fhandler.cc | 9 | ||||
-rw-r--r-- | winsup/cygwin/fhandler_disk_file.cc | 155 | ||||
-rw-r--r-- | winsup/cygwin/fhandler_socket.cc | 13 | ||||
-rw-r--r-- | winsup/cygwin/path.cc | 7 | ||||
-rw-r--r-- | winsup/cygwin/release/1.7.36 | 18 | ||||
-rw-r--r-- | winsup/cygwin/sec_acl.cc | 551 | ||||
-rw-r--r-- | winsup/cygwin/security.cc | 554 | ||||
-rw-r--r-- | winsup/cygwin/security.h | 6 | ||||
-rw-r--r-- | winsup/cygwin/syscalls.cc | 3 |
10 files changed, 632 insertions, 725 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 6b8bd5663..e53ff94db 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -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. diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc index 6f024da32..4c1bdbaf1 100644 --- a/winsup/cygwin/fhandler.cc +++ b/winsup/cygwin/fhandler.cc @@ -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 diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index f5edb03de..abc4b4134 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -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 ()) { - if (pc.isdir ()) - mode |= S_IFDIR; - if (!set_file_attribute (get_handle (), pc, - ILLEGAL_UID, ILLEGAL_GID, mode)) - res = 0; + 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_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 - 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 ()) - { - PSID sid; + ret = 0; + goto out; + } - if (old_uid == ILLEGAL_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; - } + /* 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 ()) + 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 (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"); + 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; } diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index 1b28e5220..e441fd18c 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -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' diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index b05333fd7..5439a161a 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -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)) diff --git a/winsup/cygwin/release/1.7.36 b/winsup/cygwin/release/1.7.36 index f01e497fe..c9fdd8510 100644 --- a/winsup/cygwin/release/1.7.36 +++ b/winsup/cygwin/release/1.7.36 @@ -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 diff --git a/winsup/cygwin/sec_acl.cc b/winsup/cygwin/sec_acl.cc index 55ff1bea7..628b22146 100644 --- a/winsup/cygwin/sec_acl.cc +++ b/winsup/cygwin/sec_acl.cc @@ -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); + /* No POSIX ACL? Use attr to generate one from scratch. */ + if (!aclbufp) + { + 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; + } + } - cygsid sid; - struct passwd *pw; - struct group *gr; - int pos; - cyg_ldap cldap; + /* 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: + aclsid[idx] = (aclbufp[idx].a_type & ACL_DEFAULT) + ? (PSID) well_known_creator_owner_sid : owner; + break; + case USER: + aclsid[idx] = sidfromuid (aclbufp[idx].a_id, &cldap); + break; + case GROUP_OBJ: + aclsid[idx] = (aclbufp[idx].a_type & ACL_DEFAULT && !(attr & S_ISGID)) + ? (PSID) well_known_creator_group_sid : group; + break; + case GROUP: + aclsid[idx] = sidfromgid (aclbufp[idx].a_id, &cldap); + break; + 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); - 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) + /* 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) { - switch (aclbufp[i].a_type) + DWORD inherit = def ? SUB_CONTAINERS_AND_OBJECTS_INHERIT | INHERIT_ONLY + : NO_INHERITANCE; + + /* No default ACEs on files. */ + if (def && !S_ISDIR (attr)) { - 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; + /* 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; - 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) + /* 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) { - allow |= FILE_GENERIC_WRITE; - writable = true; + class_obj = aclbufp[class_idx].a_perm; + access |= CYG_ACE_MASK_TO_WIN (class_obj); } - 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) + else { - inheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; - /* invalidate the corresponding default entry. */ - invalid[i + 1 + pos] = true; + /* 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; } - switch (aclbufp[i].a_type) + 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) { - 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; - break; - case USER: - case DEF_USER: - if (!(pw = internal_getpwuid (aclbufp[i].a_id, &cldap)) - || !sid.getfrompw (pw)) + /* Create deny ACEs for users, then groups. */ + for (start_idx = idx; + idx < nentries && aclbufp[idx].a_type & check_types; + ++idx) { - set_errno (EINVAL); - return -1; + /* 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; } - if (!add_access_allowed_ace (acl, allow, sid, acl_len, inheritance)) - return -1; - break; - case DEF_GROUP_OBJ: - if (!add_access_allowed_ace (acl, allow, well_known_creator_group_sid, - acl_len, inheritance)) - return -1; - break; - case GROUP: - case DEF_GROUP: - if (!(gr = internal_getgrgid (aclbufp[i].a_id, &cldap)) - || !sid.getfromgr (gr)) + /* Create allow ACEs for users, then groups. */ + for (idx = start_idx; + idx < nentries && aclbufp[idx].a_type & check_types; + ++idx) { - set_errno (EINVAL); - return -1; + /* 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; } - if (!add_access_allowed_ace (acl, allow, sid, acl_len, inheritance)) - return -1; - break; - case DEF_OTHER_OBJ: - if (!add_access_allowed_ace (acl, allow, well_known_world_sid, - acl_len, inheritance)) - return -1; } + /* 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,14 +719,18 @@ 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; - class_perm |= lacl[pos].a_perm; + /* 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) diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc index c50f515cf..c2d30b5af 100644 --- a/winsup/cygwin/security.cc +++ b/winsup/cygwin/security.cc @@ -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 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. - - 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 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. */ + the ACEs has the INHERITED_ACE flag set. In all of these cases we + know the DACL has been inherited. + + 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. + + 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. + + 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; - - if (pc.has_acls ()) - { - 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); + 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 (!get_file_sd (handle, pc, sd, true)) + { + 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 + 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); + } } - else - ret = 0; - syscall_printf ("%d = set_file_attribute(%S, %d, %d, 0%o)", - ret, pc.get_nt_native_path (), uid, gid, attribute); return ret; } diff --git a/winsup/cygwin/security.h b/winsup/cygwin/security.h index 0378814ca..db5b9f65f 100644 --- a/winsup/cygwin/security.h +++ b/winsup/cygwin/security.h @@ -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 *); diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index d2fb3534b..5dc2a452c 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -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 { |