Support acl(2) method for reading pty ACLs, fix pty chown

* fhandler.h (fhandler_pty_slave::facl): Add prototype.
        * fhandler_tty.cc (fhandler_pty_slave::facl): New method.
        (fhandler_pty_slave::fchown): Fix uid/gid handling.
        * sec_acl.cc (set_posix_access): Drop superfluous class_idx variable.
        Simplify and move around code in a few places.  To improve ACL
        readability, add r/w permissions to Admins ACE appended to pty ACL.
        Add comment to explain Windows ACE Mask filtering being in the way of
        creating a real CLASS_OBJ.
        (get_posix_access): Fake CLASS_OBJ for ptys.  Explain why.
        * security.cc (get_object_attribute): Add S_IFCHR flag to attributes
        when calling get_posix_access.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2015-04-17 19:54:59 +02:00
parent 02bbbb7506
commit f63dffb818
5 changed files with 120 additions and 24 deletions

View File

@ -1,3 +1,17 @@
2015-04-17 Corinna Vinschen <corinna@vinschen.de>
* fhandler.h (fhandler_pty_slave::facl): Add prototype.
* fhandler_tty.cc (fhandler_pty_slave::facl): New method.
(fhandler_pty_slave::fchown): Fix uid/gid handling.
* sec_acl.cc (set_posix_access): Drop superfluous class_idx variable.
Simplify and move around code in a few places. To improve ACL
readability, add r/w permissions to Admins ACE appended to pty ACL.
Add comment to explain Windows ACE Mask filtering being in the way of
creating a real CLASS_OBJ.
(get_posix_access): Fake CLASS_OBJ for ptys. Explain why.
* security.cc (get_object_attribute): Add S_IFCHR flag to attributes
when calling get_posix_access.
2015-04-17 Corinna Vinschen <corinna@vinschen.de> 2015-04-17 Corinna Vinschen <corinna@vinschen.de>
* uinfo.cc (pwdgrp::fetch_account_from_windows): Always revert SID * uinfo.cc (pwdgrp::fetch_account_from_windows): Always revert SID

View File

@ -1545,6 +1545,7 @@ class fhandler_pty_slave: public fhandler_pty_common
select_record *select_read (select_stuff *); select_record *select_read (select_stuff *);
virtual char const *ttyname () { return pc.dev.name; } virtual char const *ttyname () { return pc.dev.name; }
int __reg2 fstat (struct stat *buf); int __reg2 fstat (struct stat *buf);
int __reg3 facl (int, int, struct acl *);
int __reg1 fchmod (mode_t mode); int __reg1 fchmod (mode_t mode);
int __reg2 fchown (uid_t uid, gid_t gid); int __reg2 fchown (uid_t uid, gid_t gid);

View File

@ -12,6 +12,7 @@ details. */
#include "winsup.h" #include "winsup.h"
#include <stdlib.h> #include <stdlib.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/acl.h>
#include <cygwin/kd.h> #include <cygwin/kd.h>
#include "cygerrno.h" #include "cygerrno.h"
#include "security.h" #include "security.h"
@ -1018,6 +1019,62 @@ fhandler_pty_slave::fstat (struct stat *st)
return 0; return 0;
} }
int __reg3
fhandler_pty_slave::facl (int cmd, int nentries, aclent_t *aclbufp)
{
int res = -1;
bool to_close = false;
security_descriptor sd;
mode_t attr = S_IFCHR;
switch (cmd)
{
case SETACL:
if (!aclsort32 (nentries, 0, aclbufp))
set_errno (ENOTSUP);
break;
case GETACL:
if (!aclbufp)
{
set_errno (EFAULT);
break;
}
/*FALLTHRU*/
case GETACLCNT:
if (!input_available_event)
{
char buf[MAX_PATH];
shared_name (buf, INPUT_AVAILABLE_EVENT, get_minor ());
input_available_event = OpenEvent (READ_CONTROL, TRUE, buf);
if (input_available_event)
to_close = true;
}
if (!input_available_event
|| get_object_sd (input_available_event, sd))
{
res = get_posix_access (NULL, &attr, NULL, NULL, aclbufp, nentries);
if (aclbufp && res == MIN_ACL_ENTRIES)
{
aclbufp[0].a_perm = S_IROTH | S_IWOTH;
aclbufp[0].a_id = 18;
aclbufp[1].a_id = 544;
}
break;
}
if (cmd == GETACL)
res = get_posix_access (sd, &attr, NULL, NULL, aclbufp, nentries);
else
res = get_posix_access (sd, &attr, NULL, NULL, NULL, 0);
break;
default:
set_errno (EINVAL);
break;
}
if (to_close)
CloseHandle (input_available_event);
return res;
}
/* Helper function for fchmod and fchown, which just opens all handles /* Helper function for fchmod and fchown, which just opens all handles
and signals success via bool return. */ and signals success via bool return. */
bool bool
@ -1122,8 +1179,11 @@ fhandler_pty_slave::fchown (uid_t uid, gid_t gid)
RtlCreateSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION); RtlCreateSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
if (!get_object_attribute (input_available_event, &o_uid, &o_gid, &mode)) if (!get_object_attribute (input_available_event, &o_uid, &o_gid, &mode))
{ {
if ((uid == ILLEGAL_UID || uid == o_uid) if (uid == ILLEGAL_UID)
&& (gid == ILLEGAL_GID || gid == o_gid)) uid = o_uid;
if (gid == ILLEGAL_GID)
gid = o_gid;
if (uid == o_uid && gid == o_gid)
ret = 0; ret = 0;
else if (!create_object_sd_from_attribute (uid, gid, mode, sd)) else if (!create_object_sd_from_attribute (uid, gid, mode, sd))
ret = fch_set_sd (sd, true); ret = fch_set_sd (sd, true);

View File

@ -140,9 +140,9 @@ set_posix_access (mode_t attr, uid_t uid, gid_t gid,
size_t acl_len = sizeof (ACL); size_t acl_len = sizeof (ACL);
mode_t class_obj = 0, other_obj, group_obj, deny; mode_t class_obj = 0, other_obj, group_obj, deny;
DWORD access; DWORD access;
int idx, start_idx, class_idx, tmp_idx; int idx, start_idx, tmp_idx;
bool owner_eq_group = false; bool owner_eq_group = false;
bool dev_saw_admins = false; bool dev_has_admins = false;
/* Initialize local security descriptor. */ /* Initialize local security descriptor. */
RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION); RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
@ -172,8 +172,9 @@ set_posix_access (mode_t attr, uid_t uid, gid_t gid,
return NULL; return NULL;
} }
owner_eq_group = RtlEqualSid (owner, group); owner_eq_group = RtlEqualSid (owner, group);
if (S_ISCHR (attr))
dev_has_admins = well_known_admins_sid == owner
|| well_known_admins_sid == group;
/* No POSIX ACL? Use attr to generate one from scratch. */ /* No POSIX ACL? Use attr to generate one from scratch. */
if (!aclbufp) if (!aclbufp)
@ -276,11 +277,12 @@ set_posix_access (mode_t attr, uid_t uid, gid_t gid,
/* ... class_obj. Create Cygwin ACE. Only the S_ISGID attribute gets /* ... class_obj. Create Cygwin ACE. Only the S_ISGID attribute gets
inherited. */ inherited. */
access = CYG_ACE_ISBITS_TO_WIN (def ? attr & S_ISGID : attr); access = CYG_ACE_ISBITS_TO_WIN (def ? attr & S_ISGID : attr)
class_idx = searchace (aclbufp, nentries, def | CLASS_OBJ); | CYG_ACE_NEW_STYLE;
if (class_idx >= 0) tmp_idx = searchace (aclbufp, nentries, def | CLASS_OBJ);
if (tmp_idx >= 0)
{ {
class_obj = aclbufp[class_idx].a_perm; class_obj = aclbufp[tmp_idx].a_perm;
access |= CYG_ACE_MASK_TO_WIN (class_obj); access |= CYG_ACE_MASK_TO_WIN (class_obj);
} }
else else
@ -288,9 +290,11 @@ set_posix_access (mode_t attr, uid_t uid, gid_t gid,
/* Setting class_obj to group_obj allows to write below code without /* Setting class_obj to group_obj allows to write below code without
additional checks for existence of a CLASS_OBJ. */ additional checks for existence of a CLASS_OBJ. */
class_obj = group_obj; class_obj = group_obj;
class_idx = -1;
} }
access |= CYG_ACE_NEW_STYLE; /* Note that Windows filters the ACE Mask value so it only reflects
the bit values supported by the object type. The result is that
we can't set a CLASS_OBJ value for ptys. The get_posix_access
function has to workaround that. */
if (!add_access_denied_ace (acl, access, well_known_null_sid, acl_len, if (!add_access_denied_ace (acl, access, well_known_null_sid, acl_len,
inherit)) inherit))
return NULL; return NULL;
@ -359,14 +363,6 @@ set_posix_access (mode_t attr, uid_t uid, gid_t gid,
if (aclbufp[idx].a_perm == S_IRWXO) if (aclbufp[idx].a_perm == S_IRWXO)
access |= FILE_DELETE_CHILD; access |= FILE_DELETE_CHILD;
} }
/* For ptys check if the admins group is in the ACL. If so,
make sure the group has WRITE_DAC and WRITE_OWNER perms. */
if (S_ISCHR (attr) && !dev_saw_admins
&& aclsid[idx] == well_known_admins_sid)
{
access |= STD_RIGHTS_OWNER;
dev_saw_admins = true;
}
if (aclbufp[idx].a_perm & S_IROTH) if (aclbufp[idx].a_perm & S_IROTH)
access |= FILE_ALLOW_READ; access |= FILE_ALLOW_READ;
if (aclbufp[idx].a_perm & S_IWOTH) if (aclbufp[idx].a_perm & S_IWOTH)
@ -379,6 +375,10 @@ set_posix_access (mode_t attr, uid_t uid, gid_t gid,
== (S_IWOTH | S_IXOTH) == (S_IWOTH | S_IXOTH)
&& (!(attr & S_ISVTX) || aclbufp[idx].a_type & USER_OBJ)) && (!(attr & S_ISVTX) || aclbufp[idx].a_type & USER_OBJ))
access |= FILE_DELETE_CHILD; access |= FILE_DELETE_CHILD;
/* For ptys, make sure the Administrators group has WRITE_DAC
and WRITE_OWNER perms. */
if (dev_has_admins && aclsid[idx] == well_known_admins_sid)
access |= STD_RIGHTS_OWNER;
if (!add_access_allowed_ace (acl, access, aclsid[idx], acl_len, if (!add_access_allowed_ace (acl, access, aclsid[idx], acl_len,
inherit)) inherit))
return NULL; return NULL;
@ -386,8 +386,10 @@ set_posix_access (mode_t attr, uid_t uid, gid_t gid,
} }
/* For ptys if the admins group isn't in the ACL, add an ACE to make /* For ptys if the admins group isn't in the ACL, add an ACE to make
sure the group has WRITE_DAC and WRITE_OWNER perms. */ sure the group has WRITE_DAC and WRITE_OWNER perms. */
if (S_ISCHR (attr) && !dev_saw_admins if (S_ISCHR (attr) && !dev_has_admins
&& !add_access_allowed_ace (acl, STD_RIGHTS_OWNER, && !add_access_allowed_ace (acl,
STD_RIGHTS_OWNER | FILE_ALLOW_READ
| FILE_ALLOW_WRITE,
well_known_admins_sid, acl_len, well_known_admins_sid, acl_len,
NO_INHERITANCE)) NO_INHERITANCE))
return NULL; return NULL;
@ -860,6 +862,20 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
lacl[pos].a_id = ILLEGAL_GID; lacl[pos].a_id = ILLEGAL_GID;
lacl[pos].a_perm = class_perm | lacl[1].a_perm; lacl[pos].a_perm = class_perm | lacl[1].a_perm;
} }
/* For ptys, fake a mask if the admins group is neither owner nor group.
In that case we have an extra ACE for the admins group, and we need a
CLASS_OBJ to get a valid POSIX ACL. However, Windows filters the ACE
Mask value so it only reflects the bit values supported by the object
type. The result is that we can't set an explicit CLASS_OBJ value for
ptys in the NULL SID ACE. */
else if (S_ISCHR (attr) && owner_sid != well_known_admins_sid
&& group_sid != well_known_admins_sid
&& (pos = searchace (lacl, MAX_ACL_ENTRIES, CLASS_OBJ)) >= 0)
{
lacl[pos].a_type = CLASS_OBJ;
lacl[pos].a_id = ILLEGAL_GID;
lacl[pos].a_perm = lacl[1].a_perm; /* == group perms */
}
/* If this is a just created file, and there are no default permissions /* If this is a just created file, and there are no default permissions
(probably no inherited ACEs so created from a default DACL), assign (probably no inherited ACEs so created from a default DACL), assign
the permissions specified by the file creation mask. The values get the permissions specified by the file creation mask. The values get

View File

@ -401,11 +401,16 @@ get_object_attribute (HANDLE handle, uid_t *uidret, gid_t *gidret,
mode_t *attribute) mode_t *attribute)
{ {
security_descriptor sd; security_descriptor sd;
mode_t attr = S_IFCHR;
if (get_object_sd (handle, sd)) if (get_object_sd (handle, sd))
return -1; return -1;
return get_posix_access (sd, attribute, uidret, gidret, NULL, 0) >= 0 if (attribute)
? 0 : -1; *attribute |= S_IFCHR;
else
attribute = &attr;
return get_posix_access (sd, attribute, uidret, gidret, NULL, 0)
>= 0 ? 0 : -1;
} }
int int