summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--winsup/cygwin/ChangeLog186
-rw-r--r--winsup/cygwin/fhandler.cc9
-rw-r--r--winsup/cygwin/fhandler.h1
-rw-r--r--winsup/cygwin/fhandler_disk_file.cc158
-rw-r--r--winsup/cygwin/fhandler_socket.cc13
-rw-r--r--winsup/cygwin/fhandler_tty.cc79
-rw-r--r--winsup/cygwin/include/cygwin/acl.h12
-rw-r--r--winsup/cygwin/path.cc7
-rw-r--r--winsup/cygwin/release/2.0.018
-rw-r--r--winsup/cygwin/sec_acl.cc1195
-rw-r--r--winsup/cygwin/sec_helper.cc9
-rw-r--r--winsup/cygwin/security.cc794
-rw-r--r--winsup/cygwin/security.h16
-rw-r--r--winsup/cygwin/syscalls.cc3
-rw-r--r--winsup/doc/ChangeLog8
-rw-r--r--winsup/doc/utils.xml113
-rw-r--r--winsup/utils/ChangeLog30
-rw-r--r--winsup/utils/getfacl.c197
-rw-r--r--winsup/utils/setfacl.c375
19 files changed, 1344 insertions, 1879 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index c8d1470d9..e2ada7784 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -109,37 +109,6 @@
* include/libgen.h: Remove in favor of newlib version.
-2015-04-21 Corinna Vinschen <corinna@vinschen.de>
-
- * sec_acl.cc (get_posix_access): Check for Cygwin "standard" ACL.
- Apply umask, if so. Align comments.
- * security.cc (set_created_file_access): Fix permission masking by
- incoming requested file mode.
-
-2015-04-20 Corinna Vinschen <corinna@vinschen.de>
-
- * sec_acl.cc (set_posix_access): Apply mask only in terms of execute bit
- for SYSTEM and Admins group.
-
-2015-04-17 Corinna Vinschen <corinna@vinschen.de>
-
- * sec_acl.cc (set_posix_access): Don't create DENY ACEs for USER and
- GROUP entries if they are the same as USER_OBJ or GROUP_OBJ.
-
-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>
* uinfo.cc (pwdgrp::fetch_account_from_windows): Always revert SID
@@ -153,68 +122,12 @@
* grp.cc (pwdgrp::parse_group): Accommodate above change.
* passwd.cc (pwdgrp::parse_passwd): Ditto.
-2015-04-16 Corinna Vinschen <corinna@vinschen.de>
-
- * sec_acl.cc (set_posix_access): Move merging group perms into owner
- perms in case of owner == group after mask has been computed. Take
- mask into account when doing so to avoid unnecessary ACCESS_DENIED_ACE.
-
-2015-04-16 Corinna Vinschen <corinna@vinschen.de>
-
- * sec_acl.cc (get_posix_access): Only set saw_group_obj flag if we saw
- the ACCESS_ALLOWED_ACE.
-
-2015-04-16 Corinna Vinschen <corinna@vinschen.de>
-
- * fhandler_disk_file.cc (fhandler_disk_file::fchmod): Deliberatly
- set GROUP_OBJ and CLASS_OBJ perms to new group perms. Add comment
- to explain why.
- * security.cc (set_created_file_access): Ditto.
-
-2015-04-16 Corinna Vinschen <corinna@vinschen.de>
-
- * sec_acl.cc (set_posix_access): Replace previous patch. Return
- EINVAL if uid and/or guid is invalid and not backed by an actual
- Windows account.
-
-2015-04-16 Corinna Vinschen <corinna@vinschen.de>
-
- * sec_acl.cc (set_posix_access): Workaround owner/group SIDs being NULL.
-
-2015-04-15 Corinna Vinschen <corinna@vinschen.de>
-
- * sec_acl.cc (set_posix_access): Handle files with owner == group.
- Rephrase switch statement checking against unfiltered a_type value.
- (get_posix_access): Handle files with owner == group.
-
-2015-04-14 Corinna Vinschen <corinna@vinschen.de>
-
- * sec_acl.cc (get_posix_access): Don't use GROUP_OBJ access to fix up
- CLASS_OBJ mask on old-style ACLs. Fix a comment.
-
-2015-04-12 Corinna Vinschen <corinna@vinschen.de>
-
- * sec_acl.cc (set_posix_access): Always make sure Admins have
- WRITE_DAC and WRITE_OWNER permissions.
- * security.h (create_object_sd_from_attribute): Drop handle parameter
- from prototype.
- * security.cc (create_object_sd_from_attribute): Drop handle parameter.
- Just create the standard POSIXy security descriptor.
- (set_object_attribute): Accommodate dropped paramter in call to
- create_object_sd_from_attribute.
- * fhandler_tty.cc: Ditto, throughout.
-
2015-04-12 Corinna Vinschen <corinna@vinschen.de>
* shm.cc (shmget): Fetch segment size from server rather than using
size argument to accommodate existing segments. Add comment to explain
why.
-2015-04-11 Corinna Vinschen <corinna@vinschen.de>
-
- * fhandler_disk_file.cc (fhandler_disk_file::fchmod): Fix typo in
- mask computation.
-
2015-04-10 Corinna Vinschen <corinna@vinschen.de>
* include/cygwin/version.h (CYGWIN_VERSION_DLL_MAJOR): Bump to 2000.
@@ -236,47 +149,6 @@
* exceptions.cc (call_signal_handler): Zero initialize context and set
context flags, as RlCaptureContext doesn't.
-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.
@@ -290,19 +162,6 @@
2015-04-08 Corinna Vinschen <corinna@vinschen.de>
- * sec_acl.cc: Change preceeding comment explaining new-style ACLs.
- Describe how to generate deny ACEs in more detail. Accommodate the
- fact that a NULL deny ACE is used for {DEF_}CLASS_OBJ, rather than
- a special Cygwin ACE. Improve further comments.
- (CYG_ACE_NEW_STYLE): Define.
- (get_posix_access): Change from Cygwin ACE to NULL deny ACE. Fix
- CLASS_OBJ handling to generate CLASS_OBJ and DEF_CLASS_OBJ from a single
- NULL deny ACE if the inheritance flags say so.
- * sec_helper.cc (well_known_cygwin_sid): Remove.
- * security.h (well_known_cygwin_sid): Drop declaration.
-
-2015-04-08 Corinna Vinschen <corinna@vinschen.de>
-
* include/cyggwin/acl.h (struct __acl16): Move from here...
* sec_acl.cc: ...to here.
@@ -333,21 +192,6 @@
* include/cygwin/signal.h (struct __mcontext): 16-byte align.
* include/sys/ucontext.h (ucontext_t): Ditto.
-2015-04-02 Corinna Vinschen <corinna@vinschen.de>
-
- * sec_acl.cc (CYG_ACE_ISBITS_TO_WIN): Fix typo.
- (get_posix_access): Rename index variable from i to idx. Define only
- once at top level.
-
-2015-04-02 Corinna Vinschen <corinna@vinschen.de>
-
- * security.cc (add_access_allowed_ace): Drop unused parameter "offset".
- Accommodate throughout.
- (add_access_denied_ace): Ditto.
- * sec_acl.cc: Accommodate above change throughout.
- * security.h (add_access_allowed_ace): Adjust prototype to above change.
- (add_access_denied_ace): Ditto.
-
2015-04-01 Corinna Vinschen <corinna@vinschen.de>
* include/cygwin/types.h: Include sys/_stdint.h rather than stdint.h.
@@ -457,36 +301,6 @@
* include/cygwin/_types.h: Drop unused file.
-2015-03-19 Corinna Vinschen <corinna@vinschen.de>
-
- * sec_acl.cc (get_posix_access): Handle multiple ACEs for the
- owner and primary group of the file. Handle the default primary
- group ACE as DEF_GROUP_OBJ entry if the directory has the S_ISGID bit
- set. Add comments. Minor code rearrangements.
-
-2015-03-18 Corinna Vinschen <corinna@vinschen.de>
-
- Preliminary read side implementation of new permission handling.
- * acl.h (MAX_ACL_ENTRIES): Raise to 2730. Add comment to explain.
- * sec_acl.cc: Add leading comment to explain new ACL style.
- Add definitions and macros to use for bits in new Cygwin ACL.
- (DENY_RWX): New mask value for all temporary deny bits.
- (getace): Add bool parameter to decide when leaving all bits intact,
- rather than filtering them per the already set bits.
- (get_posix_access): New function, taking over functionality to read
- POSIX ACL from SECURITY_DESCRIPTOR.
- (getacl): Just call get_posix_access.
- * sec_helper.cc (well_known_cygwin_sid): Define.
- * security.cc (get_attribute_from_acl): Remove.
- (get_info_from_sd): Remove.
- (get_reg_sd): Call get_posix_access instead of get_info_from_sd.
- (get_file_attribute): Ditto.
- (get_object_attribute): Ditto.
- * security.h (well_known_cygwin_sid): Declare.
- (get_posix_access): Add prototype.
-
- * Throughout, use simpler ACE macros from Windows' accctrl.h.
-
2015-03-18 Corinna Vinschen <corinna@vinschen.de>
* grp.cc (pwdgrp::parse_group): Call cygsid::getfromgr_passwd.
diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
index 4c1bdbaf1..6f024da32 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)))
+ || open (flags, (mode & 07777) & ~cygheap->umask)))
{
if (archetype)
delete archetype;
@@ -662,10 +662,9 @@ 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 & 07777) & ~cygheap->umask;
+ nfs_attr->mode = mode;
}
- else if (!has_acls ()
- && !(mode & ~cygheap->umask & (S_IWUSR | S_IWGRP | S_IWOTH)))
+ else if (!has_acls () && !(mode & (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;
@@ -717,7 +716,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_created_file_access (fh, pc, mode);
+ set_file_attribute (fh, pc, ILLEGAL_UID, ILLEGAL_GID, S_JUSTCREATED | 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.h b/winsup/cygwin/fhandler.h
index db3cef572..e15f94632 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1550,7 +1550,6 @@ class fhandler_pty_slave: public fhandler_pty_common
select_record *select_read (select_stuff *);
virtual char const *ttyname () { return pc.dev.name; }
int __reg2 fstat (struct stat *buf);
- int __reg3 facl (int, int, struct acl *);
int __reg1 fchmod (mode_t mode);
int __reg2 fchown (uid_t uid, gid_t gid);
diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc
index b283934c4..f5edb03de 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 ret = -1;
+ int res = -1;
int oret = 0;
NTSTATUS status;
IO_STATUS_BLOCK io;
@@ -882,45 +882,17 @@ fhandler_disk_file::fchmod (mode_t mode)
if (!NT_SUCCESS (status))
__seterrno_from_nt_status (status);
else
- ret = 0;
+ res = 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;
- /* Deliberate deviation from POSIX 1003.1e here. We're not
- writing CLASS_OBJ *or* GROUP_OBJ, but both. Otherwise we're
- going to be in constant trouble with user expectations. */
- if ((idx = searchace (aclp, nentries, GROUP_OBJ)) >= 0)
- aclp[idx].a_perm = (mode >> 3) & S_IRWXO;
- if (nentries > MIN_ACL_ENTRIES
- && (idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0)
- aclp[idx].a_perm = (mode >> 3) & 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 (pc.isdir ())
+ mode |= S_IFDIR;
+ if (!set_file_attribute (get_handle (), pc,
+ ILLEGAL_UID, ILLEGAL_GID, mode))
+ res = 0;
}
/* If the mode has any write bits set, the DOS R/O flag is in the way. */
@@ -957,28 +929,20 @@ fhandler_disk_file::fchmod (mode_t mode)
if (!NT_SUCCESS (status))
__seterrno_from_nt_status (status);
else
- ret = 0;
+ res = 0;
}
out:
if (oret)
close_fs ();
- return ret;
+ return res;
}
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 ())
{
@@ -995,71 +959,52 @@ fhandler_disk_file::fchown (uid_t uid, gid_t gid)
return -1;
}
- 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)
+ 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)
{
- 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 ())
- 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)))
+ /* 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 ())
{
- debug_printf ("Faking chown worked on standalone Samba");
- ret = 0;
+ PSID sid;
+
+ 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;
+ }
}
}
-
-out:
if (oret)
close_fs ();
- return ret;
+ return res;
}
int __reg3
@@ -1818,11 +1763,10 @@ 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_created_file_access (dir, pc, mode & 07777);
+ set_file_attribute (dir, pc, ILLEGAL_UID, ILLEGAL_GID,
+ S_JUSTCREATED | S_IFDIR
+ | ((mode & 07777) & ~cygheap->umask));
NtClose (dir);
res = 0;
}
diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc
index e441fd18c..1b28e5220 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, 2015 Red Hat, Inc.
+ 2011, 2012, 2013, 2014 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 = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ mode_t mode = adjust_socket_file_mode ((S_IRWXU | S_IRWXG | S_IRWXO)
+ & ~cygheap->umask);
DWORD fattr = FILE_ATTRIBUTE_SYSTEM;
- if (!pc.has_acls ()
- && !(mode & ~cygheap->umask & (S_IWUSR | S_IWGRP | S_IWOTH)))
+ if (!(mode & (S_IWUSR | S_IWGRP | S_IWOTH)) && !pc.has_acls ())
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 | WRITE_OWNER;
+ access |= READ_CONTROL | WRITE_DAC;
status = NtCreateFile (&fh, access, pc.get_object_attr (attr, sa), &io,
NULL, fattr, 0, FILE_CREATE,
@@ -1078,7 +1078,8 @@ fhandler_socket::bind (const struct sockaddr *name, int namelen)
else
{
if (pc.has_acls ())
- set_created_file_access (fh, pc, mode);
+ set_file_attribute (fh, pc, ILLEGAL_UID, ILLEGAL_GID,
+ S_JUSTCREATED | 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/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index b74c39799..1b5c18bc7 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -12,7 +12,6 @@ details. */
#include "winsup.h"
#include <stdlib.h>
#include <sys/param.h>
-#include <sys/acl.h>
#include <cygwin/kd.h>
#include "cygerrno.h"
#include "security.h"
@@ -389,8 +388,9 @@ fhandler_pty_slave::open (int flags, mode_t)
sd.malloc (sizeof (SECURITY_DESCRIPTOR));
RtlCreateSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE };
- if (!create_object_sd_from_attribute (myself->uid, myself->gid,
- S_IRUSR | S_IWUSR | S_IWGRP, sd))
+ if (!create_object_sd_from_attribute (NULL, myself->uid, myself->gid,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IWGRP,
+ sd))
sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR) sd;
acquire_output_mutex (INFINITE);
inuse = get_ttyp ()->create_inuse (&sa);
@@ -1039,62 +1039,6 @@ fhandler_pty_slave::fstat (struct stat *st)
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
and signals success via bool return. */
bool
@@ -1167,7 +1111,7 @@ fhandler_pty_slave::fchmod (mode_t mode)
sd.malloc (sizeof (SECURITY_DESCRIPTOR));
RtlCreateSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
if (!get_object_attribute (input_available_event, &uid, &gid, NULL)
- && !create_object_sd_from_attribute (uid, gid, mode, sd))
+ && !create_object_sd_from_attribute (NULL, uid, gid, S_IFCHR | mode, sd))
ret = fch_set_sd (sd, false);
errout:
if (to_close)
@@ -1197,13 +1141,11 @@ fhandler_pty_slave::fchown (uid_t uid, gid_t gid)
RtlCreateSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
if (!get_object_attribute (input_available_event, &o_uid, &o_gid, &mode))
{
- if (uid == ILLEGAL_UID)
- uid = o_uid;
- if (gid == ILLEGAL_GID)
- gid = o_gid;
- if (uid == o_uid && gid == o_gid)
+ if ((uid == ILLEGAL_UID || uid == o_uid)
+ && (gid == ILLEGAL_GID || gid == o_gid))
ret = 0;
- else if (!create_object_sd_from_attribute (uid, gid, mode, sd))
+ else if (!create_object_sd_from_attribute (input_available_event,
+ uid, gid, S_IFCHR | mode, sd))
ret = fch_set_sd (sd, true);
}
errout:
@@ -1741,8 +1683,9 @@ fhandler_pty_master::setup ()
/* Create security attribute. Default permissions are 0620. */
sd.malloc (sizeof (SECURITY_DESCRIPTOR));
RtlCreateSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION);
- if (!create_object_sd_from_attribute (myself->uid, myself->gid,
- S_IRUSR | S_IWUSR | S_IWGRP, sd))
+ if (!create_object_sd_from_attribute (NULL, myself->uid, myself->gid,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IWGRP,
+ sd))
sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR) sd;
/* Carefully check that the input_available_event didn't already exist.
diff --git a/winsup/cygwin/include/cygwin/acl.h b/winsup/cygwin/include/cygwin/acl.h
index b7cbf03f3..34f1c0c44 100644
--- a/winsup/cygwin/include/cygwin/acl.h
+++ b/winsup/cygwin/include/cygwin/acl.h
@@ -1,6 +1,6 @@
/* cygwin/acl.h header file for Cygwin.
- Copyright 1999, 2000, 2001, 2002, 2010, 2014, 2015 Red Hat, Inc.
+ Copyright 1999, 2000, 2001, 2002, 2010, 2014 Red Hat, Inc.
Written by C. Vinschen.
This file is part of Cygwin.
@@ -25,16 +25,8 @@ extern "C" {
#define GETACL (0x1)
#define GETACLCNT (0x2)
-/* Windows ACLs have a maximum size of 64K. Counting the most pessimistic way,
- the maximum number of ACEs is 3276. Technet claims "approximately 1820",
- which uses the length of normal user and group SIDs for the computation.
- We're now going with 2730, the number of aclent_t entries matching a 32K
- buffer.
- On one hand, there are only a limited number of SIDs shorter than the normal
- user/group SIDs, on the other hand there are no deny aclent_t entries, so we
- should be fine with 32K aclbuf_t buffers provided by the caller. */
#define MIN_ACL_ENTRIES (3) // minimal acl entries from GETACLCNT
-#define MAX_ACL_ENTRIES (2730) // max entries of each type
+#define MAX_ACL_ENTRIES (256) // max entries of each type
// Return values of aclcheck(3) in case of error */
#define GRP_ERROR (0x1)
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index b1a02e133..446d746cc 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -2049,9 +2049,10 @@ symlink_worker (const char *oldpath, const char *newpath, bool isdevice)
__seterrno_from_nt_status (status);
__leave;
}
- if (io.Information == FILE_CREATED && win32_newpath.has_acls ())
- set_created_file_access (fh, win32_newpath,
- S_IFLNK | STD_RBITS | STD_WBITS);
+ 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);
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/2.0.0 b/winsup/cygwin/release/2.0.0
index 6a9628b64..08d44502b 100644
--- a/winsup/cygwin/release/2.0.0
+++ b/winsup/cygwin/release/2.0.0
@@ -1,24 +1,6 @@
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 7d97fca02..5a2048caf 100644
--- a/winsup/cygwin/sec_acl.cc
+++ b/winsup/cygwin/sec_acl.cc
@@ -24,75 +24,8 @@ details. */
#include "ntdll.h"
#include "tls_pbuf.h"
-/* How does a correctly constructed new-style Windows ACL claiming to be a
- POSIX ACL look like?
-
- - NULL ACE (special bits, CLASS_OBJ).
-
- - USER_OBJ deny. If the user has less permissions than the sum of CLASS_OBJ
- (or GROUP_OBJ if CLASS_OBJ doesn't exist) and OTHER_OBJ, deny the excess
- permissions so that group and other perms don't spill into the owner perms.
-
- USER_OBJ deny ACE == ~USER_OBJ & (CLASS_OBJ | OTHER_OBJ)
- or
- USER_OBJ deny ACE == ~USER_OBJ & (GROUP_OBJ | OTHER_OBJ)
-
- - USER deny. If a user has different permissions from CLASS_OBJ, or if the
- user has less permissions than OTHER_OBJ, deny the excess permissions.
-
- USER deny ACE == (USER ^ CLASS_OBJ) | (~USER & OTHER_OBJ)
-
- - USER_OBJ allow ACE
- - USER allow ACEs
-
- The POSIX permissions returned for a USER entry are the allow bits alone!
-
- - GROUP{_OBJ} deny. If a group has more permissions than CLASS_OBJ,
- or less permissions than OTHER_OBJ, deny the excess permissions.
-
- GROUP{_OBJ} deny ACEs == (GROUP & ~CLASS_OBJ) | (~GROUP & OTHER_OBJ)
-
- - GROUP_OBJ allow ACE
- - GROUP allow ACEs
-
- The POSIX permissions returned for a GROUP entry are the allow bits alone!
-
- - OTHER_OBJ allow ACE
-
- Rinse and repeat for default ACEs with INHERIT flags set.
-
- - Default NULL ACE (S_ISGID, CLASS_OBJ). */
-
- /* POSIX <-> Win32 */
-
-/* Historically, these bits are stored in a NULL SID ACE. To distinguish the
- new ACL style from the old one, we're using an access denied ACE, plus
- setting an as yet unused bit in the access mask. The new ACEs can exist
- twice in an ACL, the "normal one" containing CLASS_OBJ and special bits
- and the one with INHERIT bit set to pass the DEF_CLASS_OBJ bits and the
- S_ISGID bit on. */
-#define CYG_ACE_ISVTX 0x001 /* 0x200 <-> 0x001 */
-#define CYG_ACE_ISGID 0x002 /* 0x400 <-> 0x002 */
-#define CYG_ACE_ISUID 0x004 /* 0x800 <-> 0x004 */
-#define CYG_ACE_ISBITS_TO_POSIX(val) \
- (((val) & 0x007) << 9)
-#define CYG_ACE_ISBITS_TO_WIN(val) \
- (((val) & (S_ISVTX | S_ISUID | S_ISGID)) >> 9)
-
-#define CYG_ACE_MASK_X 0x008 /* 0x001 <-> 0x008 */
-#define CYG_ACE_MASK_W 0x010 /* 0x002 <-> 0x010 */
-#define CYG_ACE_MASK_R 0x020 /* 0x004 <-> 0x020 */
-#define CYG_ACE_MASK_RWX 0x038
-#define CYG_ACE_MASK_VALID 0x040 /* has mask if set */
-#define CYG_ACE_MASK_TO_POSIX(val) \
- (((val) & CYG_ACE_MASK_RWX) >> 3)
-#define CYG_ACE_MASK_TO_WIN(val) \
- ((((val) & S_IRWXO) << 3) \
- | CYG_ACE_MASK_VALID)
-#define CYG_ACE_NEW_STYLE READ_CONTROL /* New style if set. */
-
-int
-searchace (aclent_t *aclp, int nentries, int type, uid_t id)
+static int
+searchace (aclent_t *aclp, int nentries, int type, uid_t id = ILLEGAL_UID)
{
int i;
@@ -103,325 +36,270 @@ searchace (aclent_t *aclp, int nentries, int type, uid_t id)
return -1;
}
-/* 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)
+/* This function *requires* an acl list sorted with aclsort{32}. */
+int
+setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
+ bool &writable)
{
- SECURITY_DESCRIPTOR sd;
- cyg_ldap cldap;
- PSID owner, group;
- NTSTATUS status;
+ security_descriptor sd_ret;
tmp_pathbuf tp;
- cygpsid *aclsid;
+
+ if (get_file_sd (handle, pc, sd_ret, false))
+ return -1;
+
+ NTSTATUS status;
PACL acl;
- size_t acl_len = sizeof (ACL);
- mode_t class_obj = 0, other_obj, group_obj, deny;
- DWORD access;
- int idx, start_idx, tmp_idx;
- bool owner_eq_group = false;
- bool dev_has_admins = false;
+ 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;
+ }
+ }
+ }
/* 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);
- if (!owner || !group)
- {
- set_errno (EINVAL);
- return NULL;
- }
status = RtlSetOwnerSecurityDescriptor (&sd, owner, FALSE);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
- return NULL;
+ return -1;
}
status = RtlSetGroupSecurityDescriptor (&sd, group, FALSE);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
- return NULL;
+ return -1;
}
- 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. */
- 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;
- }
- }
+ /* Fill access control list. */
+ acl = (PACL) tp.w_get ();
+ size_t acl_len = sizeof (ACL);
+ int ace_off = 0;
- /* Collect SIDs of all entries in aclbufp. */
- aclsid = (cygpsid *) tp.w_get ();
- for (idx = 0; idx < nentries; ++idx)
- switch (aclbufp[idx].a_type)
- {
- case USER_OBJ:
- aclsid[idx] = owner;
- break;
- case DEF_USER_OBJ:
- aclsid[idx] = well_known_creator_owner_sid;
- break;
- case USER:
- case DEF_USER:
- aclsid[idx] = sidfromuid (aclbufp[idx].a_id, &cldap);
- break;
- case GROUP_OBJ:
- aclsid[idx] = group;
- break;
- case DEF_GROUP_OBJ:
- aclsid[idx] = !(attr & S_ISGID) ? (PSID) well_known_creator_group_sid
- : group;
- break;
- case GROUP:
- case DEF_GROUP:
- aclsid[idx] = sidfromgid (aclbufp[idx].a_id, &cldap);
- break;
- case CLASS_OBJ:
- case DEF_CLASS_OBJ:
- aclsid[idx] = well_known_null_sid;
- break;
- case OTHER_OBJ:
- case DEF_OTHER_OBJ:
- aclsid[idx] = well_known_world_sid;
- break;
- }
+ cygsid sid;
+ struct passwd *pw;
+ struct group *gr;
+ int pos;
+ cyg_ldap cldap;
- /* 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;
+ writable = false;
+
+ bool *invalid = (bool *) tp.c_get ();
+ memset (invalid, 0, nentries * sizeof *invalid);
- /* No default ACEs on files. */
- if (def && !S_ISDIR (attr))
+ /* 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)
+ {
+ switch (aclbufp[i].a_type)
{
- /* 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;
- }
+ 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, ace_off++, 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, ace_off++, group_deny,
+ group, acl_len, NO_INHERITANCE))
+ return -1;
+ /* Set allow ACE for owner. */
+ if (!add_access_allowed_ace (acl, ace_off++, 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, ace_off++, group_deny,
+ group, acl_len, NO_INHERITANCE))
+ return -1;
+ /* Set allow ACE for group. */
+ if (!isownergroup
+ && !add_access_allowed_ace (acl, ace_off++, group_allow,
+ group, acl_len, NO_INHERITANCE))
+ return -1;
+ /* Set allow ACE for everyone. */
+ if (!add_access_allowed_ace (acl, ace_off++, 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, ace_off++, 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;
- /* 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)
- | CYG_ACE_NEW_STYLE;
- tmp_idx = searchace (aclbufp, nentries, def | CLASS_OBJ);
- if (tmp_idx >= 0)
+ 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)
{
- class_obj = aclbufp[tmp_idx].a_perm;
- access |= CYG_ACE_MASK_TO_WIN (class_obj);
+ allow |= FILE_GENERIC_WRITE;
+ writable = true;
}
- else
+ 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)
+ ? (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
+ | INHERIT_ONLY_ACE)
+ : 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)
{
- /* Setting class_obj to group_obj allows to write below code without
- additional checks for existence of a CLASS_OBJ. */
- class_obj = group_obj;
+ inheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
+ /* invalidate the corresponding default entry. */
+ invalid[i + 1 + pos] = true;
}
- /* 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,
- inherit))
- return NULL;
-
- /* Do we potentially chmod a file with owner SID == group SID? If so,
- make sure the owner perms are always >= group perms. */
- if (!def && owner_eq_group)
- aclbufp[0].a_perm |= group_obj & class_obj;
-
- /* 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)
+ switch (aclbufp[i].a_type)
{
- /* Create deny ACEs for users, then groups. */
- for (start_idx = idx;
- idx < nentries && aclbufp[idx].a_type & check_types;
- ++idx)
+ case DEF_USER_OBJ:
+ allow |= STANDARD_RIGHTS_ALL
+ | (pc.fs_is_samba () ? 0 : FILE_WRITE_ATTRIBUTES);
+ if (!add_access_allowed_ace (acl, ace_off++, 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))
{
- /* Avoid creating DENY ACEs for the second occurrence of
- accounts which show up twice, as USER_OBJ and USER, or
- GROUP_OBJ and GROUP. */
- if ((aclbufp[idx].a_type & USER && aclsid[idx] == owner)
- || (aclbufp[idx].a_type & GROUP && aclsid[idx] == group))
- continue;
- /* 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);
- /* Accommodate Windows: Only generate deny masks for SYSTEM
- and the Administrators group in terms of the execute bit,
- if they are not the primary group. */
- else if (aclbufp[idx].a_type & GROUP
- && (aclsid[idx] == well_known_system_sid
- || aclsid[idx] == well_known_admins_sid))
- deny = aclbufp[idx].a_perm & ~(class_obj | S_IROTH | S_IWOTH);
- else
- deny = (aclbufp[idx].a_perm & ~class_obj)
- | (~aclbufp[idx].a_perm & other_obj);
- if (!deny)
- 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;
+ set_errno (EINVAL);
+ return -1;
}
- /* Create allow ACEs for users, then groups. */
- for (idx = start_idx;
- idx < nentries && aclbufp[idx].a_type & check_types;
- ++idx)
+ if (!add_access_allowed_ace (acl, ace_off++, allow,
+ sid, acl_len, inheritance))
+ return -1;
+ break;
+ case DEF_GROUP_OBJ:
+ if (!add_access_allowed_ace (acl, ace_off++, 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))
{
- /* 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;
- /* 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,
- inherit))
- return NULL;
+ set_errno (EINVAL);
+ return -1;
}
+ if (!add_access_allowed_ace (acl, ace_off++, allow,
+ sid, acl_len, inheritance))
+ return -1;
+ break;
+ case DEF_OTHER_OBJ:
+ if (!add_access_allowed_ace (acl, ace_off++, allow,
+ well_known_world_sid,
+ acl_len, inheritance))
+ return -1;
}
- /* 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. */
- if (S_ISCHR (attr) && !dev_has_admins
- && !add_access_allowed_ace (acl,
- STD_RIGHTS_OWNER | FILE_ALLOW_READ
- | FILE_ALLOW_WRITE,
- well_known_admins_sid, acl_len,
- NO_INHERITANCE))
- 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);
@@ -430,7 +308,7 @@ set_posix_access (mode_t attr, uid_t uid, gid_t gid,
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
- return NULL;
+ return -1;
}
/* Make self relative security descriptor in sd_ret. */
DWORD sd_size = 0;
@@ -438,65 +316,36 @@ set_posix_access (mode_t attr, uid_t uid, gid_t gid,
if (sd_size <= 0)
{
__seterrno ();
- return NULL;
+ return -1;
}
if (!sd_ret.realloc (sd_size))
{
set_errno (ENOMEM);
- return NULL;
+ return -1;
}
status = RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
- return NULL;
+ return -1;
}
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);
}
-/* Temporary access denied bits used by getace and get_posix_access during
- Windows ACL processing. These bits get removed before the created POSIX
- ACL gets published. */
+/* Temporary access denied bits */
#define DENY_R 040000
#define DENY_W 020000
#define DENY_X 010000
-#define DENY_RWX (DENY_R | DENY_W | DENY_X)
-/* New style ACL means, just read the bits and store them away. Don't
- create masked values on your own. */
static void
getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
- DWORD win_ace_type, bool new_style)
+ DWORD win_ace_type)
{
acl.a_type = type;
acl.a_id = id;
- if ((win_ace_mask & FILE_READ_BITS)
- && (new_style || !(acl.a_perm & (S_IROTH | DENY_R))))
+ if ((win_ace_mask & FILE_READ_BITS) && !(acl.a_perm & (S_IROTH | DENY_R)))
{
if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
acl.a_perm |= S_IROTH;
@@ -504,8 +353,7 @@ getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
acl.a_perm |= DENY_R;
}
- if ((win_ace_mask & FILE_WRITE_BITS)
- && (new_style || !(acl.a_perm & (S_IWOTH | DENY_W))))
+ if ((win_ace_mask & FILE_WRITE_BITS) && !(acl.a_perm & (S_IWOTH | DENY_W)))
{
if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
acl.a_perm |= S_IWOTH;
@@ -513,8 +361,7 @@ getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
acl.a_perm |= DENY_W;
}
- if ((win_ace_mask & FILE_EXEC_BITS)
- && (new_style || !(acl.a_perm & (S_IXOTH | DENY_X))))
+ if ((win_ace_mask & FILE_EXEC_BITS) && !(acl.a_perm & (S_IXOTH | DENY_X)))
{
if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
acl.a_perm |= S_IXOTH;
@@ -523,110 +370,40 @@ getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
}
}
-/* From the SECURITY_DESCRIPTOR given in psd, compute user, owner, posix
- attributes, as well as the POSIX acl. The function returns the number
- of entries returned in aclbufp, or -1 in case of error. */
int
-get_posix_access (PSECURITY_DESCRIPTOR psd,
- mode_t *attr_ret, uid_t *uid_ret, gid_t *gid_ret,
- aclent_t *aclbufp, int nentries)
+getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
{
- tmp_pathbuf tp;
+ security_descriptor sd;
+
+ if (get_file_sd (handle, pc, sd, false))
+ return -1;
+
+ cygpsid owner_sid;
+ cygpsid group_sid;
NTSTATUS status;
- BOOLEAN dummy, acl_exists;
- SECURITY_DESCRIPTOR_CONTROL ctrl;
- ULONG rev;
- PACL acl;
- PACCESS_ALLOWED_ACE ace;
- cygpsid owner_sid, group_sid;
- cyg_ldap cldap;
+ BOOLEAN dummy;
uid_t uid;
gid_t gid;
- mode_t attr = 0;
- aclent_t *lacl = NULL;
- cygpsid ace_sid;
- int pos, type, id, idx;
-
- bool owner_eq_group;
- bool just_created = false;
- bool standard_ACEs_only = true;
- bool new_style = false;
- bool saw_user_obj = false;
- bool saw_group_obj = false;
- bool saw_other_obj = false;
- bool saw_def_user_obj = false;
- bool saw_def_group_obj = false;
- bool has_class_perm = false;
- bool has_def_class_perm = false;
-
- mode_t class_perm = 0;
- mode_t def_class_perm = 0;
- int types_def = 0;
- int def_pgrp_pos = -1;
-
- if (aclbufp && nentries < MIN_ACL_ENTRIES)
- {
- set_errno (EINVAL);
- return -1;
- }
- /* If reading the security descriptor failed, treat the object as
- unreadable. */
- if (!psd)
- {
- if (attr_ret)
- *attr_ret &= S_IFMT;
- if (uid_ret)
- *uid_ret = ILLEGAL_UID;
- if (gid_ret)
- *gid_ret = ILLEGAL_GID;
- if (aclbufp)
- {
- aclbufp[0].a_type = USER_OBJ;
- aclbufp[0].a_id = ILLEGAL_UID;
- aclbufp[0].a_perm = 0;
- aclbufp[1].a_type = GROUP_OBJ;
- aclbufp[1].a_id = ILLEGAL_GID;
- aclbufp[1].a_perm = 0;
- aclbufp[2].a_type = OTHER_OBJ;
- aclbufp[2].a_id = ILLEGAL_GID;
- aclbufp[2].a_perm = 0;
- return MIN_ACL_ENTRIES;
- }
- return 0;
- }
- /* Fetch owner, group, and ACL from security descriptor. */
- status = RtlGetOwnerSecurityDescriptor (psd, (PSID *) &owner_sid, &dummy);
- if (!NT_SUCCESS (status))
- {
- __seterrno_from_nt_status (status);
- return -1;
- }
- status = RtlGetGroupSecurityDescriptor (psd, (PSID *) &group_sid, &dummy);
+ cyg_ldap cldap;
+
+ status = RtlGetOwnerSecurityDescriptor (sd, (PSID *) &owner_sid, &dummy);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
return -1;
}
- status = RtlGetDaclSecurityDescriptor (psd, &acl_exists, &acl, &dummy);
+ uid = owner_sid.get_uid (&cldap);
+
+ status = RtlGetGroupSecurityDescriptor (sd, (PSID *) &group_sid, &dummy);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
return -1;
}
- /* Set uidret, gidret, and initalize attributes. */
- uid = owner_sid.get_uid (&cldap);
gid = group_sid.get_gid (&cldap);
- if (attr_ret)
- {
- attr = *attr_ret & S_IFMT;
- just_created = *attr_ret & S_JUSTCREATED;
- }
- /* Remember the fact that owner and group are the same account. */
- owner_eq_group = owner_sid == group_sid;
- /* Create and initialize local aclent_t array. */
- lacl = (aclent_t *) tp.c_get ();
- memset (lacl, 0, MAX_ACL_ENTRIES * sizeof (aclent_t *));
+ aclent_t lacl[MAX_ACL_ENTRIES];
+ memset (&lacl, 0, MAX_ACL_ENTRIES * sizeof (aclent_t));
lacl[0].a_type = USER_OBJ;
lacl[0].a_id = uid;
lacl[1].a_type = GROUP_OBJ;
@@ -634,404 +411,186 @@ get_posix_access (PSECURITY_DESCRIPTOR psd,
lacl[2].a_type = OTHER_OBJ;
lacl[2].a_id = ILLEGAL_GID;
- /* No ACEs? Everybody has full access. */
- if (!acl_exists || !acl || acl->AceCount == 0)
+ PACL acl;
+ BOOLEAN acl_exists;
+
+ status = RtlGetDaclSecurityDescriptor (sd, &acl_exists, &acl, &dummy);
+ if (!NT_SUCCESS (status))
{
- for (pos = 0; pos < MIN_ACL_ENTRIES; ++pos)
- lacl[pos].a_perm = S_IROTH | S_IWOTH | S_IXOTH;
- goto out;
+ __seterrno_from_nt_status (status);
+ return -1;
}
- /* 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)
+ int pos, i, types_def = 0;
+ int pgrp_pos = 1, def_pgrp_pos = -1;
+ bool has_class_perm = false, has_def_class_perm = false;
+ mode_t class_perm = 0, def_class_perm = 0;
+
+ if (!acl_exists || !acl)
+ for (pos = 0; pos < 3; ++pos)
+ lacl[pos].a_perm = S_IROTH | S_IWOTH | S_IXOTH;
+ else
{
- if (!NT_SUCCESS (RtlGetAce (acl, idx, (PVOID *) &ace)))
- continue;
+ for (i = 0; i < acl->AceCount; ++i)
+ {
+ ACCESS_ALLOWED_ACE *ace;
- ace_sid = (PSID) &ace->SidStart;
+ if (!NT_SUCCESS (RtlGetAce (acl, i, (PVOID *) &ace)))
+ continue;
- if (ace_sid == well_known_null_sid)
- {
- /* Fetch special bits. */
- attr |= CYG_ACE_ISBITS_TO_POSIX (ace->Mask);
- if (ace->Header.AceType == ACCESS_DENIED_ACE_TYPE
- && ace->Mask & CYG_ACE_NEW_STYLE)
+ cygpsid ace_sid ((PSID) &ace->SidStart);
+ int id;
+ int type = 0;
+
+ if (ace_sid == well_known_null_sid)
{
- /* New-style ACL. Note the fact that a mask value is present
- since that changes how getace fetches the information. That's
- fine, because the Cygwin SID ACE is supposed to precede all
- 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)
- {
- if (!(ace->Header.AceFlags & INHERIT_ONLY))
- {
- if ((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 = CYG_ACE_MASK_TO_POSIX (ace->Mask);
- }
- has_class_perm = true;
- class_perm = lacl[pos].a_perm;
- }
- if (ace->Header.AceFlags & SUB_CONTAINERS_AND_OBJECTS_INHERIT)
- {
- if ((pos = searchace (lacl, MAX_ACL_ENTRIES,
- DEF_CLASS_OBJ)) >= 0)
- {
- lacl[pos].a_type = DEF_CLASS_OBJ;
- lacl[pos].a_id = ILLEGAL_GID;
- lacl[pos].a_perm = CYG_ACE_MASK_TO_POSIX (ace->Mask);
- }
- has_def_class_perm = true;
- def_class_perm = lacl[pos].a_perm;
- }
- }
+ /* Simply ignore. */
+ continue;
}
- continue;
- }
- if (ace_sid == owner_sid)
- {
- type = USER_OBJ;
- id = uid;
- }
- else if (ace_sid == group_sid)
- {
- type = GROUP_OBJ;
- id = gid;
- }
- else if (ace_sid == well_known_world_sid)
- {
- 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)
- {
- type = DEF_USER_OBJ;
- types_def |= type;
- id = ILLEGAL_GID;
- saw_def_user_obj = true;
- }
- else if (ace_sid == well_known_creator_group_sid)
- {
- type = DEF_GROUP_OBJ;
- types_def |= type;
- id = ILLEGAL_GID;
- saw_def_group_obj = true;
- }
- else
- {
- id = ace_sid.get_id (TRUE, &type, &cldap);
- if (!type)
- continue;
- }
- /* If the SGID attribute is set on a just created file or dir, the
- first group in the ACL is the desired primary group of the new
- object. Alternatively, the first repetition of the owner SID is
- the desired primary group, and we mark the object as owner_eq_group
- object. */
- if (just_created && attr & S_ISGID && !saw_group_obj
- && (type == GROUP || (type == USER_OBJ && saw_user_obj)))
- {
- type = GROUP_OBJ;
- lacl[1].a_id = gid = id;
- owner_eq_group = true;
- }
- if (!(ace->Header.AceFlags & INHERIT_ONLY || type & ACL_DEFAULT))
- {
- if (type == USER_OBJ)
+ if (ace_sid == well_known_world_sid)
{
- /* If we get a second entry for the owner SID, it's either a
- GROUP_OBJ entry for the same SID, if owner SID == group SID,
- or it's an additional USER entry. The latter can happen
- when chown'ing a file. */
- if (saw_user_obj)
- {
- if (owner_eq_group && !saw_group_obj)
- {
- type = GROUP_OBJ;
- if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
- saw_group_obj = true;
- }
- else
- type = USER;
- }
- else if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
- saw_user_obj = true;
+ type = OTHER_OBJ;
+ id = ILLEGAL_GID;
}
- else if (type == GROUP_OBJ)
+ else if (ace_sid == owner_sid)
{
- /* Same for the primary group. */
- if (saw_group_obj)
- type = GROUP;
- if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
- saw_group_obj = true;
+ type = USER_OBJ;
+ id = uid;
+ }
+ else if (ace_sid == group_sid)
+ {
+ type = GROUP_OBJ;
+ id = gid;
+ }
+ else if (ace_sid == well_known_creator_group_sid)
+ {
+ type = DEF_GROUP_OBJ;
+ types_def |= type;
+ id = ILLEGAL_GID;
+ }
+ else if (ace_sid == well_known_creator_owner_sid)
+ {
+ type = DEF_USER_OBJ;
+ types_def |= type;
+ id = ILLEGAL_GID;
}
- if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
+ else
+ id = ace_sid.get_id (TRUE, &type, &cldap);
+
+ if (!type)
+ continue;
+ if (!(ace->Header.AceFlags & INHERIT_ONLY_ACE || type & ACL_DEFAULT))
{
- getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType,
- new_style && type & (USER | GROUP_OBJ | GROUP));
- if (!new_style)
+ if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
{
+ getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType);
/* Fix up CLASS_OBJ value. */
- if (type & (USER | GROUP))
+ if (type == USER || type == GROUP)
{
has_class_perm = true;
- /* Accommodate Windows: Never add SYSTEM and Admins to
- CLASS_OBJ. Unless (implicitly) if they are the
- GROUP_OBJ entry. */
- if (ace_sid != well_known_system_sid
- && ace_sid != well_known_admins_sid)
- class_perm |= lacl[pos].a_perm;
+ class_perm |= lacl[pos].a_perm;
}
}
- /* For a newly created file, we'd like to know if we're running
- with a standard ACL, one only consisting of POSIX perms, plus
- SYSTEM and Admins as maximum non-POSIX perms entries. If it's
- a standard ACL, we apply umask. That's not entirely correct,
- but it's probably the best we can do. */
- else if (type & (USER | GROUP)
- && just_created
- && standard_ACEs_only
- && ace_sid != well_known_system_sid
- && ace_sid != well_known_admins_sid)
- standard_ACEs_only = false;
}
- }
- if ((ace->Header.AceFlags & SUB_CONTAINERS_AND_OBJECTS_INHERIT))
- {
- if (type == USER_OBJ)
- {
- /* As above: If we get a second entry for the owner SID, it's
- a GROUP_OBJ entry for the same SID if owner SID == group SID,
- but this time only if the S_ISGID bit is set. Otherwise it's
- an additional USER entry. */
- if (saw_def_user_obj)
- {
- if (owner_eq_group && !saw_def_group_obj && attr & S_ISGID)
- type = GROUP_OBJ; /* This needs post-processing in the
- following GROUP_OBJ handling... */
- else
- type = USER;
- }
- else if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
- saw_def_user_obj = true;
- }
- if (type == GROUP_OBJ)
+ if ((ace->Header.AceFlags
+ & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))
+ && pc.isdir ())
{
- /* If the SGID bit is set, the inheritable entry for the
- primary group is, in fact, the DEF_GROUP_OBJ entry,
- so don't change the type to GROUP in this case. */
- if (!new_style || saw_def_group_obj || !(attr & S_ISGID))
+ if (type == USER_OBJ)
+ type = USER;
+ else if (type == GROUP_OBJ)
type = GROUP;
- else if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
- saw_def_group_obj = true;
- }
- type |= ACL_DEFAULT;
- types_def |= type;
- 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_OBJ | GROUP));
- if (!new_style)
+ type |= ACL_DEFAULT;
+ types_def |= type;
+ if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
{
+ getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType);
/* Fix up DEF_CLASS_OBJ value. */
- if (type & (USER | GROUP))
+ if (type == DEF_USER || type == DEF_GROUP)
{
has_def_class_perm = true;
- /* Accommodate Windows: Never add SYSTEM and Admins to
- CLASS_OBJ. Unless (implicitly) if they are the
- GROUP_OBJ entry. */
- 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. */
- if (type == DEF_GROUP_OBJ)
+ else if (type == DEF_GROUP_OBJ)
def_pgrp_pos = pos;
}
}
}
- }
- /* If this is an old-style or non-Cygwin ACL, and secondary user and group
- entries exist in the ACL, fake a matching CLASS_OBJ entry. The CLASS_OBJ
- permissions are the or'ed permissions of the primary group permissions
- and all secondary user and group permissions. */
- if (!new_style && has_class_perm
- && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
- {
- lacl[pos].a_type = CLASS_OBJ;
- lacl[pos].a_id = ILLEGAL_GID;
- 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 this is an ACL with only standard
- entries, or if standard POSIX permissions are missing (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 per POSIX 1003.1e draft 17. */
- if (just_created)
- {
- mode_t perms = (S_IRWXU | S_IRWXG | S_IRWXO) & ~cygheap->umask;
- if (standard_ACEs_only || !saw_user_obj)
- lacl[0].a_perm = (perms >> 6) & S_IRWXO;
- if (standard_ACEs_only || !saw_group_obj)
- lacl[1].a_perm = (perms >> 3) & S_IRWXO;
- if (standard_ACEs_only || !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)
- {
- if (!(types_def & USER_OBJ))
+ /* If secondary user and group entries exist in the ACL, fake a matching
+ CLASS_OBJ entry. The CLASS_OBJ permissions are the or'ed permissions
+ of the primary group permissions and all secondary user and group
+ permissions. */
+ if (has_class_perm && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
{
- lacl[pos].a_type = DEF_USER_OBJ;
- lacl[pos].a_id = uid;
- lacl[pos].a_perm = lacl[0].a_perm;
- pos++;
+ lacl[pos].a_type = CLASS_OBJ;
+ lacl[pos].a_id = ILLEGAL_GID;
+ lacl[pos].a_perm = class_perm | lacl[pgrp_pos].a_perm;
}
- if (!(types_def & GROUP_OBJ) && pos < MAX_ACL_ENTRIES)
+ /* 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)
{
- lacl[pos].a_type = DEF_GROUP_OBJ;
- lacl[pos].a_id = gid;
- lacl[pos].a_perm = lacl[1].a_perm;
- /* Note the position of the DEF_GROUP_OBJ entry. */
- def_pgrp_pos = pos;
- pos++;
+ if (!(types_def & USER_OBJ))
+ {
+ lacl[pos].a_type = DEF_USER_OBJ;
+ lacl[pos].a_id = uid;
+ lacl[pos].a_perm = lacl[0].a_perm;
+ pos++;
+ }
+ if (!(types_def & GROUP_OBJ) && pos < MAX_ACL_ENTRIES)
+ {
+ lacl[pos].a_type = DEF_GROUP_OBJ;
+ lacl[pos].a_id = gid;
+ lacl[pos].a_perm = lacl[1].a_perm;
+ /* Note the position of the DEF_GROUP_OBJ entry. */
+ def_pgrp_pos = pos;
+ pos++;
+ }
+ if (!(types_def & OTHER_OBJ) && pos < MAX_ACL_ENTRIES)
+ {
+ lacl[pos].a_type = DEF_OTHER_OBJ;
+ lacl[pos].a_id = ILLEGAL_GID;
+ lacl[pos].a_perm = lacl[2].a_perm;
+ pos++;
+ }
}
- if (!(types_def & OTHER_OBJ) && pos < MAX_ACL_ENTRIES)
+ /* If secondary user default and group default entries exist in the ACL,
+ fake a matching DEF_CLASS_OBJ entry. The DEF_CLASS_OBJ permissions are
+ the or'ed permissions of the primary group default permissions and all
+ secondary user and group default permissions. */
+ if (has_def_class_perm
+ && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
{
- lacl[pos].a_type = DEF_OTHER_OBJ;
+ lacl[pos].a_type = DEF_CLASS_OBJ;
lacl[pos].a_id = ILLEGAL_GID;
- lacl[pos].a_perm = lacl[2].a_perm;
- pos++;
+ lacl[pos].a_perm = def_class_perm;
+ if (def_pgrp_pos >= 0)
+ lacl[pos].a_perm |= lacl[def_pgrp_pos].a_perm;
}
}
- /* If this is an old-style or non-Cygwin ACL, and secondary user default
- and group default entries exist in the ACL, fake a matching DEF_CLASS_OBJ
- entry. The DEF_CLASS_OBJ permissions are the or'ed permissions of the
- primary group default permissions and all secondary user and group def.
- permissions. */
- if (!new_style && has_def_class_perm
- && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
- {
- lacl[pos].a_type = DEF_CLASS_OBJ;
- lacl[pos].a_id = ILLEGAL_GID;
- lacl[pos].a_perm = def_class_perm;
- if (def_pgrp_pos >= 0)
- lacl[pos].a_perm |= lacl[def_pgrp_pos].a_perm;
- }
-
- /* Make sure `pos' contains the number of used entries in lacl. */
if ((pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) < 0)
pos = MAX_ACL_ENTRIES;
-
- /* For old-style or non-Cygwin ACLs, check for merging permissions. */
- if (!new_style)
- for (idx = 0; idx < pos; ++idx)
- {
- /* Current user? If the user entry has a deny ACE, don't check. */
- if (lacl[idx].a_id == myself->uid
- && lacl[idx].a_type & (USER_OBJ | USER)
- && !(lacl[idx].a_type & ACL_DEFAULT)
- && !(lacl[idx].a_perm & DENY_RWX))
- {
- int gpos;
- gid_t grps[NGROUPS_MAX];
- cyg_ldap cldap;
-
- /* Sum up all permissions of groups the user is member of, plus
- everyone perms, and merge them to user perms. */
- mode_t grp_perm = lacl[2].a_perm & S_IRWXO;
- int gnum = internal_getgroups (NGROUPS_MAX, grps, &cldap);
- for (int g = 0; g < gnum && grp_perm != S_IRWXO; ++g)
- if ((gpos = 1, grps[g] == lacl[gpos].a_id)
- || (gpos = searchace (lacl, MAX_ACL_ENTRIES, GROUP, grps[g]))
- >= 0)
- grp_perm |= lacl[gpos].a_perm & S_IRWXO;
- lacl[idx].a_perm |= grp_perm;
- }
- /* For all groups, if everyone has more permissions, add everyone
- perms to group perms. Skip groups with deny ACE. */
- else if (lacl[idx].a_id & (GROUP_OBJ | GROUP)
- && !(lacl[idx].a_type & ACL_DEFAULT)
- && !(lacl[idx].a_perm & DENY_RWX))
- lacl[idx].a_perm |= lacl[2].a_perm & S_IRWXO;
- }
- /* If owner SID == group SID (Microsoft Accounts) merge group perms into
- user perms but leave group perms intact. That's a fake, but it allows
- to keep track of the POSIX group perms without much effort. */
- if (owner_eq_group)
- lacl[0].a_perm |= lacl[1].a_perm;
- /* Construct POSIX permission bits. Fortunately we know exactly where
- to fetch the affecting bits from, at least as long as the array
- hasn't been sorted. */
- attr |= (lacl[0].a_perm & S_IRWXO) << 6;
- attr |= (has_class_perm ? class_perm : (lacl[1].a_perm & S_IRWXO)) << 3;
- attr |= (lacl[2].a_perm & S_IRWXO);
-
-out:
- if (uid_ret)
- *uid_ret = uid;
- if (gid_ret)
- *gid_ret = gid;
- if (attr_ret)
- *attr_ret = attr;
if (aclbufp)
{
+#if 0
+ /* Disable owner/group permissions equivalence if owner SID == group SID.
+ It's technically not quite correct, but it helps in case a security
+ conscious application checks if a file has too open permissions. In
+ fact, since owner == group, there's no security issue here. */
+ if (owner_sid == group_sid)
+ lacl[1].a_perm = lacl[0].a_perm;
+#endif
if (pos > nentries)
{
set_errno (ENOSPC);
return -1;
}
memcpy (aclbufp, lacl, pos * sizeof (aclent_t));
- for (idx = 0; idx < pos; ++idx)
- aclbufp[idx].a_perm &= S_IRWXO;
+ for (i = 0; i < pos; ++i)
+ aclbufp[i].a_perm &= ~(DENY_R | DENY_W | DENY_X);
aclsort32 (pos, 0, aclbufp);
}
- return pos;
-}
-
-int
-getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
-{
- security_descriptor sd;
-
- if (get_file_sd (handle, pc, sd, false))
- return -1;
- int pos = get_posix_access (sd, NULL, NULL, NULL, aclbufp, nentries);
syscall_printf ("%R = getacl(%S)", pos, pc.get_nt_native_path ());
return pos;
}
diff --git a/winsup/cygwin/sec_helper.cc b/winsup/cygwin/sec_helper.cc
index 753f15673..679f3a858 100644
--- a/winsup/cygwin/sec_helper.cc
+++ b/winsup/cygwin/sec_helper.cc
@@ -616,21 +616,22 @@ _recycler_sd (void *buf, bool users, bool dir)
pre-Vista permissions the same way as on Vista and later. */
RtlCreateAcl (dacl, MAX_DACL_LEN (3), ACL_REVISION);
RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
- dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
+ dir ? CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
: NO_INHERITANCE,
FILE_ALL_ACCESS, well_known_admins_sid);
RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
- dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
+ dir ? CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
: NO_INHERITANCE,
FILE_ALL_ACCESS, well_known_system_sid);
if (users)
- RtlAddAccessAllowedAceEx (dacl, ACL_REVISION, INHERIT_NO_PROPAGATE,
+ RtlAddAccessAllowedAceEx (dacl, ACL_REVISION, NO_PROPAGATE_INHERIT_ACE,
FILE_GENERIC_READ | FILE_GENERIC_EXECUTE
| FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES,
well_known_users_sid);
else
RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
- dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
+ dir ? CONTAINER_INHERIT_ACE
+ | OBJECT_INHERIT_ACE
: NO_INHERITANCE,
FILE_ALL_ACCESS, cygheap->user.sid ());
LPVOID ace;
diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc
index bee9b06ae..86ebe2c0a 100644
--- a/winsup/cygwin/security.cc
+++ b/winsup/cygwin/security.cc
@@ -15,7 +15,6 @@ details. */
#include "winsup.h"
#include <unistd.h>
#include <stdlib.h>
-#include <sys/acl.h>
#include "cygerrno.h"
#include "security.h"
#include "path.h"
@@ -35,6 +34,7 @@ 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,46 +85,62 @@ get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd,
return -1;
}
}
- /* 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.
+ /* 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:
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 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. */
+ 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. */
if (justcreated)
{
+ SECURITY_DESCRIPTOR_CONTROL ctrl;
+ ULONG dummy;
PACL dacl;
BOOLEAN exists, def;
ACCESS_ALLOWED_ACE *ace;
UNICODE_STRING dirname;
- PSECURITY_DESCRIPTOR psd;
+ PSECURITY_DESCRIPTOR psd, nsd;
tmp_pathbuf tp;
- /* Open the parent directory with READ_CONTROL... */
+ /* 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... */
RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
NULL, NULL);
@@ -148,14 +164,12 @@ 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);
@@ -171,36 +185,6 @@ 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;
}
@@ -250,6 +234,194 @@ set_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd, bool is_chown)
return res;
}
+static void
+get_attribute_from_acl (mode_t *attribute, PACL acl, PSID owner_sid,
+ PSID group_sid, bool grp_member)
+{
+ ACCESS_ALLOWED_ACE *ace;
+ mode_t allow = 0;
+ mode_t deny = 0;
+ mode_t *flags, *anti;
+ bool isownergroup = RtlEqualSid (owner_sid, group_sid);
+
+ for (DWORD i = 0; i < acl->AceCount; ++i)
+ {
+ if (!NT_SUCCESS (RtlGetAce (acl, i, (PVOID *) &ace)))
+ continue;
+ if (ace->Header.AceFlags & INHERIT_ONLY_ACE)
+ continue;
+ switch (ace->Header.AceType)
+ {
+ case ACCESS_ALLOWED_ACE_TYPE:
+ flags = &allow;
+ anti = &deny;
+ break;
+ case ACCESS_DENIED_ACE_TYPE:
+ flags = &deny;
+ anti = &allow;
+ break;
+ default:
+ continue;
+ }
+
+ cygpsid ace_sid ((PSID) &ace->SidStart);
+ if (ace_sid == well_known_world_sid)
+ {
+ if (ace->Mask & FILE_READ_BITS)
+ *flags |= ((!(*anti & S_IROTH)) ? S_IROTH : 0)
+ | ((!isownergroup && !(*anti & S_IRGRP)) ? S_IRGRP : 0)
+ | ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
+ if (ace->Mask & FILE_WRITE_BITS)
+ *flags |= ((!(*anti & S_IWOTH)) ? S_IWOTH : 0)
+ | ((!isownergroup && !(*anti & S_IWGRP)) ? S_IWGRP : 0)
+ | ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
+ if (ace->Mask & FILE_EXEC_BITS)
+ *flags |= ((!(*anti & S_IXOTH)) ? S_IXOTH : 0)
+ | ((!isownergroup && !(*anti & S_IXGRP)) ? S_IXGRP : 0)
+ | ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
+ if ((S_ISDIR (*attribute)) &&
+ (ace->Mask & (FILE_WRITE_DATA | FILE_EXECUTE | FILE_DELETE_CHILD))
+ == (FILE_WRITE_DATA | FILE_EXECUTE))
+ *flags |= S_ISVTX;
+ }
+ else if (ace_sid == well_known_null_sid)
+ {
+ /* Read SUID, SGID and VTX bits from NULL ACE. */
+ if (ace->Mask & FILE_READ_DATA)
+ *flags |= S_ISVTX;
+ if (ace->Mask & FILE_WRITE_DATA)
+ *flags |= S_ISGID;
+ if (ace->Mask & FILE_APPEND_DATA)
+ *flags |= S_ISUID;
+ }
+ else if (ace_sid == owner_sid)
+ {
+ if (ace->Mask & FILE_READ_BITS)
+ *flags |= ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
+ if (ace->Mask & FILE_WRITE_BITS)
+ *flags |= ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
+ if (ace->Mask & FILE_EXEC_BITS)
+ *flags |= ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
+ /* Apply deny mask to group if group SID == owner SID. */
+ if (group_sid && isownergroup
+ && ace->Header.AceType == ACCESS_DENIED_ACE_TYPE)
+ {
+ if (ace->Mask & FILE_READ_BITS)
+ *flags |= ((!(*anti & S_IRUSR)) ? S_IRGRP : 0);
+ if (ace->Mask & FILE_WRITE_BITS)
+ *flags |= ((!(*anti & S_IWUSR)) ? S_IWGRP : 0);
+ if (ace->Mask & FILE_EXEC_BITS)
+ *flags |= ((!(*anti & S_IXUSR)) ? S_IXGRP : 0);
+ }
+ }
+ else if (ace_sid == group_sid)
+ {
+ if (ace->Mask & FILE_READ_BITS)
+ *flags |= ((!(*anti & S_IRGRP)) ? S_IRGRP : 0)
+ | ((grp_member && !(*anti & S_IRUSR)) ? S_IRUSR : 0);
+ if (ace->Mask & FILE_WRITE_BITS)
+ *flags |= ((!(*anti & S_IWGRP)) ? S_IWGRP : 0)
+ | ((grp_member && !(*anti & S_IWUSR)) ? S_IWUSR : 0);
+ if (ace->Mask & FILE_EXEC_BITS)
+ *flags |= ((!(*anti & S_IXGRP)) ? S_IXGRP : 0)
+ | ((grp_member && !(*anti & S_IXUSR)) ? S_IXUSR : 0);
+ }
+ else if (flags == &allow)
+ {
+ /* Simplified computation of additional group permissions based on
+ the CLASS_OBJ value. CLASS_OBJ represents the or'ed value of
+ the primary group permissions and all secondary user and group
+ permissions. FIXME: This only takes ACCESS_ALLOWED_ACEs into
+ account. The computation with additional ACCESS_DENIED_ACE
+ handling is much more complicated. */
+ if (ace->Mask & FILE_READ_BITS)
+ *flags |= S_IRGRP;
+ if (ace->Mask & FILE_WRITE_BITS)
+ *flags |= S_IWGRP;
+ if (ace->Mask & FILE_EXEC_BITS)
+ *flags |= S_IXGRP;
+ }
+ }
+ *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX | S_ISGID | S_ISUID);
+#if 0
+ /* Disable owner/group permissions equivalence if owner SID == group SID.
+ It's technically not quite correct, but it helps in case a security
+ conscious application checks if a file has too open permissions. In
+ fact, since owner == group, there's no security issue here. */
+ if (owner_sid && group_sid && RtlEqualSid (owner_sid, group_sid)
+ /* FIXME: temporary exception for /var/empty */
+ && well_known_system_sid != group_sid)
+ {
+ allow &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
+ allow |= (((allow & S_IRUSR) ? S_IRGRP : 0)
+ | ((allow & S_IWUSR) ? S_IWGRP : 0)
+ | ((allow & S_IXUSR) ? S_IXGRP : 0));
+ }
+#endif
+ *attribute |= allow;
+}
+
+static void
+get_info_from_sd (PSECURITY_DESCRIPTOR psd, mode_t *attribute,
+ uid_t *uidret, gid_t *gidret)
+{
+ if (!psd)
+ {
+ /* If reading the security descriptor failed, treat the object
+ as unreadable. */
+ if (attribute)
+ *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
+ if (uidret)
+ *uidret = ILLEGAL_UID;
+ if (gidret)
+ *gidret = ILLEGAL_GID;
+ return;
+ }
+
+ cygpsid owner_sid;
+ cygpsid group_sid;
+ NTSTATUS status;
+ BOOLEAN dummy;
+
+ status = RtlGetOwnerSecurityDescriptor (psd, (PSID *) &owner_sid, &dummy);
+ if (!NT_SUCCESS (status))
+ debug_printf ("RtlGetOwnerSecurityDescriptor: %y", status);
+ status = RtlGetGroupSecurityDescriptor (psd, (PSID *) &group_sid, &dummy);
+ if (!NT_SUCCESS (status))
+ debug_printf ("RtlGetGroupSecurityDescriptor: %y", status);
+
+ uid_t uid;
+ gid_t gid;
+ bool grp_member = get_sids_info (owner_sid, group_sid, &uid, &gid);
+ if (uidret)
+ *uidret = uid;
+ if (gidret)
+ *gidret = gid;
+
+ if (!attribute)
+ {
+ syscall_printf ("uid %u, gid %u", uid, gid);
+ return;
+ }
+
+ PACL acl;
+ BOOLEAN acl_exists;
+
+ status = RtlGetDaclSecurityDescriptor (psd, &acl_exists, &acl, &dummy);
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+ else if (!acl_exists || !acl)
+ *attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
+ else
+ get_attribute_from_acl (attribute, acl, owner_sid, group_sid, grp_member);
+
+ syscall_printf ("%sACL %y, uid %u, gid %u",
+ (!acl_exists || !acl)?"NO ":"", *attribute, uid, gid);
+}
+
static int
get_reg_sd (HANDLE handle, security_descriptor &sd_ret)
{
@@ -282,7 +454,7 @@ get_reg_attribute (HKEY hkey, mode_t *attribute, uid_t *uidret,
if (!get_reg_sd (hkey, sd))
{
- get_posix_access (sd, attribute, uidret, gidret, NULL, 0);
+ get_info_from_sd (sd, attribute, uidret, gidret);
return 0;
}
/* The entries are already set to default values */
@@ -299,7 +471,7 @@ get_file_attribute (HANDLE handle, path_conv &pc,
if (!get_file_sd (handle, pc, sd, false))
{
- get_posix_access (sd, attribute, uidret, gidret, NULL, 0);
+ get_info_from_sd (sd, attribute, uidret, gidret);
return 0;
}
/* ENOSYS is returned by get_file_sd if fetching the DACL from a remote
@@ -327,8 +499,8 @@ get_file_attribute (HANDLE handle, path_conv &pc,
}
bool
-add_access_allowed_ace (PACL acl, DWORD attributes, PSID sid, size_t &len_add,
- DWORD inherit)
+add_access_allowed_ace (PACL acl, int offset, DWORD attributes,
+ PSID sid, size_t &len_add, DWORD inherit)
{
NTSTATUS status = RtlAddAccessAllowedAceEx (acl, ACL_REVISION, inherit,
attributes, sid);
@@ -342,8 +514,8 @@ add_access_allowed_ace (PACL acl, DWORD attributes, PSID sid, size_t &len_add,
}
bool
-add_access_denied_ace (PACL acl, DWORD attributes, PSID sid, size_t &len_add,
- DWORD inherit)
+add_access_denied_ace (PACL acl, int offset, DWORD attributes,
+ PSID sid, size_t &len_add, DWORD inherit)
{
NTSTATUS status = RtlAddAccessDeniedAceEx (acl, ACL_REVISION, inherit,
attributes, sid);
@@ -356,6 +528,367 @@ 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);
+ int ace_off = 0;
+ /* 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, ace_off++, 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, ace_off++, group_deny,
+ group_sid, acl_len, NO_INHERITANCE))
+ return NULL;
+ /* Set allow ACE for owner. */
+ if (!add_access_allowed_ace (acl, ace_off++, 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, ace_off++, group_deny,
+ group_sid, acl_len, NO_INHERITANCE))
+ return NULL;
+ /* Set allow ACE for group. */
+ if (!isownergroup
+ && !add_access_allowed_ace (acl, ace_off++, 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, ace_off++, 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, ace_off++, 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, ace_off++, 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
+ & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)) == 0)
+ continue;
+ else
+ ace->Header.AceFlags |= INHERIT_ONLY_ACE;
+ }
+ 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 |= CONTAINER_INHERIT_ACE
+ | OBJECT_INHERIT_ACE;
+ }
+ 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;
+ }
+ ace_off++;
+ 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 = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
+ | INHERIT_ONLY_ACE;
+ /* Set allow ACE for owner. */
+ if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
+ well_known_creator_owner_sid, acl_len,
+ inherit))
+ return NULL;
+ /* Set allow ACE for group. */
+ if (!add_access_allowed_ace (acl, ace_off++, group_allow,
+ well_known_creator_group_sid, acl_len,
+ inherit))
+ return NULL;
+ /* Set allow ACE for everyone. */
+ if (!add_access_allowed_ace (acl, ace_off++, 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)
@@ -363,9 +896,8 @@ 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 = set_posix_access (attribute, geteuid32 (),
- getegid32 (), NULL, 0,
- sd, false);
+ psa->lpSecurityDescriptor = alloc_sd (pc, geteuid32 (), getegid32 (),
+ attribute, sd);
}
int
@@ -401,24 +933,22 @@ get_object_attribute (HANDLE handle, uid_t *uidret, gid_t *gidret,
mode_t *attribute)
{
security_descriptor sd;
- mode_t attr = S_IFCHR;
if (get_object_sd (handle, sd))
return -1;
- if (attribute)
- *attribute |= S_IFCHR;
- else
- attribute = &attr;
- return get_posix_access (sd, attribute, uidret, gidret, NULL, 0)
- >= 0 ? 0 : -1;
+ get_info_from_sd (sd, attribute, uidret, gidret);
+ return 0;
}
int
-create_object_sd_from_attribute (uid_t uid, gid_t gid, mode_t attribute,
- security_descriptor &sd)
+create_object_sd_from_attribute (HANDLE handle, uid_t uid, gid_t gid,
+ mode_t attribute, security_descriptor &sd)
{
- return set_posix_access (S_IFCHR | attribute, uid, gid, NULL, 0, sd, false)
- ? 0 : -1;
+ path_conv pc;
+ if ((handle && get_object_sd (handle, sd))
+ || !alloc_sd (pc, uid, gid, attribute, sd))
+ return -1;
+ return 0;
}
int
@@ -436,98 +966,36 @@ set_object_sd (HANDLE handle, security_descriptor &sd, bool chown)
}
int
-set_object_attribute (HANDLE handle, uid_t uid, gid_t gid, mode_t attribute)
+set_object_attribute (HANDLE handle, uid_t uid, gid_t gid,
+ mode_t attribute)
{
security_descriptor sd;
- if (create_object_sd_from_attribute (uid, gid, attribute, sd)
+ if (create_object_sd_from_attribute (handle, uid, gid, attribute, sd)
|| set_object_sd (handle, sd, uid != ILLEGAL_UID || gid != ILLEGAL_GID))
return -1;
return 0;
}
int
-set_created_file_access (HANDLE handle, path_conv &pc, mode_t attr)
+set_file_attribute (HANDLE handle, path_conv &pc,
+ uid_t uid, gid_t gid, mode_t attribute)
{
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 (!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)
- {
- if (S_ISLNK (attr))
- {
- /* Symlinks always get the request POSIX perms. */
- aclp[0].a_perm = (attr >> 6) & S_IRWXO;
- if ((idx = searchace (aclp, nentries, GROUP_OBJ)) >= 0)
- aclp[idx].a_perm = (attr >> 3) & S_IRWXO;
- if (nentries > MIN_ACL_ENTRIES
- && (idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0)
- aclp[idx].a_perm = (attr >> 3) & S_IRWXO;
- if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0)
- aclp[idx].a_perm = attr & S_IRWXO;
- }
- else
- {
- /* Overwrite ACL permissions as required by POSIX 1003.1e
- draft 17. */
- aclp[0].a_perm &= (attr >> 6) & S_IRWXO;
- /* Deliberate deviation from POSIX 1003.1e here. We're not
- writing CLASS_OBJ *or* GROUP_OBJ, but both. Otherwise we're
- going to be in constant trouble with user expectations. */
- if ((idx = searchace (aclp, nentries, GROUP_OBJ)) >= 0)
- aclp[idx].a_perm &= (attr >> 3) & S_IRWXO;
- if (nentries > MIN_ACL_ENTRIES
- && (idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0)
- aclp[idx].a_perm &= (attr >> 3) & S_IRWXO;
- if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0)
- aclp[idx].a_perm &= attr & 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);
- }
+
+ 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);
}
+ 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;
}
@@ -678,7 +1146,7 @@ convert_samba_sd (security_descriptor &sd_ret)
ace_sid.getfromgr (grp);
}
}
- if (!add_access_allowed_ace (acl, ace->Mask, ace_sid, acl_len,
+ if (!add_access_allowed_ace (acl, i, ace->Mask, ace_sid, acl_len,
ace->Header.AceFlags))
return;
}
diff --git a/winsup/cygwin/security.h b/winsup/cygwin/security.h
index 5378b2a45..867345774 100644
--- a/winsup/cygwin/security.h
+++ b/winsup/cygwin/security.h
@@ -436,19 +436,20 @@ class path_conv;
/* File manipulation */
int __reg3 get_file_attribute (HANDLE, path_conv &, mode_t *,
uid_t *, gid_t *);
-int __reg3 set_created_file_access (HANDLE, path_conv &, mode_t);
+int __reg3 set_file_attribute (HANDLE, path_conv &,
+ uid_t, gid_t, 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);
-int __reg3 create_object_sd_from_attribute (uid_t, gid_t, mode_t,
- security_descriptor &);
+int __reg3 create_object_sd_from_attribute (HANDLE, uid_t, gid_t,
+ mode_t, security_descriptor &);
int __reg3 set_object_sd (HANDLE, security_descriptor &, bool);
int __reg3 get_reg_attribute (HKEY hkey, mode_t *, uid_t *, gid_t *);
LONG __reg3 get_file_sd (HANDLE fh, path_conv &, security_descriptor &, bool);
LONG __reg3 set_file_sd (HANDLE fh, path_conv &, security_descriptor &, bool);
-bool __reg3 add_access_allowed_ace (PACL, DWORD, PSID, size_t &, DWORD);
-bool __reg3 add_access_denied_ace (PACL, DWORD, PSID, size_t &, DWORD);
+bool __reg3 add_access_allowed_ace (PACL, int, DWORD, PSID, size_t &, DWORD);
+bool __reg3 add_access_denied_ace (PACL, int, DWORD, PSID, size_t &, DWORD);
int __reg3 check_file_access (path_conv &, int, bool);
int __reg3 check_registry_access (HANDLE, int, bool);
@@ -462,11 +463,6 @@ 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 *);
int setacl (HANDLE, path_conv &, int, struct acl *, bool &);
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
index 86faa31e4..c08d12fa9 100644
--- a/winsup/cygwin/syscalls.cc
+++ b/winsup/cygwin/syscalls.cc
@@ -1434,7 +1434,8 @@ 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))
+ || !fh->open_with_arch (flags, (mode & 07777)
+ & ~cygheap->umask))
delete fh;
else
{
diff --git a/winsup/doc/ChangeLog b/winsup/doc/ChangeLog
index aaecb69e7..10f295840 100644
--- a/winsup/doc/ChangeLog
+++ b/winsup/doc/ChangeLog
@@ -3,14 +3,6 @@
* cygserver.xml (install-cygserver): Add new section. How to install
Cygserver.
-2015-04-16 Corinna Vinschen <corinna@vinschen.de>
-
- * utils.xml (setfacl): Show new option output.
-
-2015-04-16 Corinna Vinschen <corinna@vinschen.de>
-
- * utils.xml (getfacl): Show new option output.
-
2015-04-10 Corinna Vinschen <corinna@vinschen.de>
* new-features.xml (ov-new2.0): Rename from ov-new1.7.36 and change
diff --git a/winsup/doc/utils.xml b/winsup/doc/utils.xml
index 09df8e02b..adafc2b7d 100644
--- a/winsup/doc/utils.xml
+++ b/winsup/doc/utils.xml
@@ -519,14 +519,11 @@ Usage: getfacl [-adn] FILE [FILE2...]
Display file and directory access control lists (ACLs).
- -a, --access display the file access control list only
- -d, --default display the default access control list only
- -c, --omit-header do not display the comment header
- -e, --all-effective print all effective rights
- -E, --no-effective print no effective rights
- -n, --numeric print numeric user/group identifiers
- -V, --version print version and exit
- -h, --help this help text
+ -a, --access display the file access control list
+ -d, --default display the default access control list
+ -h, --help print help explaining the command line options
+ -n, --noname display user and group IDs instead of names
+ -V, --version output version information and exit
When multiple files are specified on the command line, a blank
line separates the ACLs for each file.
@@ -1698,31 +1695,34 @@ Example: regtool.exe get '\user\software\Microsoft\Clock\iFormat'
<title>setfacl</title>
<screen>
-Usage: %s {-f ACL_FILE | -s acl_entries} FILE...\n"
- %s {-b|[-x acl_entries] [-m acl_entries]} FILE...\n"
-
-Modify file and directory access control lists (ACLs)\n"
-
- -b, --remove-all remove all extended ACL entries\n"
- -x, --delete delete one or more specified ACL entries\n"
- -f, --file set ACL entries for FILE to ACL entries read\n"
- from ACL_FILE\n"
- -k, --remove-default remove all default ACL entries\n"
- -m, --modify modify one or more specified ACL entries\n"
- -n, --no-mask don't recalculate the effective rights mask\n"
- --mask do recalculate the effective rights mask\n"
- -s, --substitute substitute specified ACL entries on FILE\n"
- -V, --version print version and exit\n"
- -h, --help this help text\n"
-
-At least one of (-b, -x, -f, -k, -m, -s) must be specified\n"
+Usage: setfacl [-r] {-f ACL_FILE | -s acl_entries} FILE...
+ setfacl [-r] {-b|[-d acl_entries] [-m acl_entries]} FILE...
+
+
+Modify file and directory access control lists (ACLs)
+
+ -b, --remove-all remove all extended ACL entries
+ -d, --delete delete one or more specified ACL entries
+ -f, --file set ACL entries for FILE to ACL entries read
+ from a ACL_FILE
+ -k, --remove-default
+ remove all default ACL entries
+ -m, --modify modify one or more specified ACL entries
+ -r, --replace replace mask entry with maximum permissions
+ needed for the file group class
+ -s, --substitute substitute specified ACL entries for the
+ ACL of FILE
+ -h, --help output usage information and exit
+ -V, --version output version information and exit
+
+At least one of (-b, -d, -f, -k, -m, -s) must be specified
</screen>
<para> For each file given as parameter, <command>setfacl</command> will
either replace its complete ACL (<literal>-s</literal>,
<literal>-f</literal>), or it will add, modify, or delete ACL entries.
- For more information on Cygwin and Windows ACLs, see
- <xref linkend="ntsec"/> in the Cygwin User's Guide. </para>
+ For more information on Cygwin and Windows ACLs, see see <xref
+ linkend="ntsec"/> in the Cygwin User's Guide. </para>
<para> Acl_entries are one or more comma-separated ACL entries from the
following list:
@@ -1749,14 +1749,13 @@ At least one of (-b, -x, -f, -k, -m, -s) must be specified\n"
<para> The following options are supported: </para>
- <para> <literal>-b</literal>,<literal>--remove-all</literal> Remove all
- extended ACL entries. The base ACL entries of the owner, group and
- others are retained.</para>
+ <para> <literal>-b</literal> Remove all extended ACL entries. The base
+ ACL entries of the owner, group and others are retained.</para>
- <para> <literal>-x</literal>,<literal>--delete</literal> Delete one or
- more specified entries from the file's ACL. The owner, group and others
- entries must not be deleted. Acl_entries to be deleted should be
- specified without permissions, as in the following list:
+ <para> <literal>-d</literal> Delete one or more specified entries from the
+ file's ACL. The owner, group and others entries must not be deleted.
+ Acl_entries to be deleted should be specified without permissions, as in
+ the following list:
<screen>
u[ser]:uid[:]
g[roup]:gid[:]
@@ -1767,12 +1766,11 @@ At least one of (-b, -x, -f, -k, -m, -s) must be specified\n"
d[efault]:o[ther][:]
</screen> </para>
- <para> <literal>-f</literal>,<literal>--file</literal> Take the Acl_entries
- from ACL_FILE one per line. Whitespace characters are ignored, and the
- character "#" may be used to start a comment. The special filename "-"
- indicates reading from stdin. Note that you can use this with
- <command>getfacl</command> and <command>setfacl</command> to copy ACLs
- from one file to another:
+ <para> <literal>-f</literal> Take the Acl_entries from ACL_FILE one per
+ line. Whitespace characters are ignored, and the character "#" may be
+ used to start a comment. The special filename "-" indicates reading from
+ stdin. Note that you can use this with <command>getfacl</command> and
+ <command>setfacl</command> to copy ACLs from one file to another:
<screen>
$ getfacl source_file | setfacl -f - target_file
</screen> </para>
@@ -1788,31 +1786,22 @@ $ getfacl source_file | setfacl -f - target_file
file, one default group entry for the group of the file, one default mask
entry for the file group class, and one default other entry. </para>
- <para> <literal>-k</literal>,<literal>--remove-default</literal> Remove all
- default ACL entries. If no default ACL entries exist, no warnings are
- issued. </para>
+ <para> <literal>-k</literal> Remove all default ACL entries. If no default
+ ACL entries exist, no warnings are issued. </para>
- <para> <literal>-m</literal>,<literal>--modify</literal> Add or modify one
- or more specified ACL entries. Acl_entries is a comma-separated list of
- entries from the same list as above. </para>
+ <para> <literal>-m</literal> Add or modify one or more specified ACL
+ entries. Acl_entries is a comma-separated list of entries from the same
+ list as above. </para>
- <para> <literal>-n</literal>,<literal>--no-mask</literal> Valid in
- conjunction with -m. Do not recalculate the effective rights mask.
- The default behavior of setfacl is to recalculate the ACL mask entry,
- unless a mask entry was explicitly given. The mask entry is set to
- the union of all permissions of the owning group, and all named user
- and group entries. (These are exactly the entries affected by the
- mask entry). </para>
+ <para> <literal>-r</literal> Causes the permissions specified in the mask
+ entry to be ignored and replaced by the maximum permissions needed for
+ the file group class. </para>
- <para> <literal>--mask</literal> Valid in conjunction with -m. Do
- recalculate the effective rights mask, even if an ACL mask entry was
- explicitly given. (See the -n option.) </para>
+ <para> <literal>-s</literal> Like <literal>-f</literal>, but substitute the
+ file's ACL with Acl_entries specified in a comma-separated list on the
+ command line. </para>
- <para> <literal>-s</literal>,<literal>--substitute</literal> Like
- <literal>-f</literal>, but substitute the file's ACL with Acl_entries
- specified in a comma-separated list on the command line. </para>
-
- <para> While the <literal>-x</literal> and <literal>-m</literal> options
+ <para> While the <literal>-d</literal> and <literal>-m</literal> options
may be used in the same command, the <literal>-f</literal> and
<literal>-s</literal> options may be used only exclusively. </para>
diff --git a/winsup/utils/ChangeLog b/winsup/utils/ChangeLog
index a0fd0701c..bfdb42ae6 100644
--- a/winsup/utils/ChangeLog
+++ b/winsup/utils/ChangeLog
@@ -3,36 +3,6 @@
* tzmap-from-unicode.org: Convert Calcutta to Kolkata.
* tzmap.h: Regenerate.
-2015-04-20 Corinna Vinschen <corinna@vinschen.de>
-
- * getfacl.c (main): Special-case SYSTEM and Admins group. Add comments.
-
-2015-04-16 Corinna Vinschen <corinna@vinschen.de>
-
- * setfacl.c: Align more to Linux tool.
- (delacl): New function to delete acl entries only.
- (modacl): Drop delete functionality. Add handling of recomputing the
- mask and default mask values.
- (delallacl): Rename from delacl.
- (setfacl): Call delacl in Delete case. Call delallacl in DeleteAll
- and DeleteDef case.
- (usage): Accommodate new options. Rearrange and rephrase slightly.
- (longopts): Emit 'x' in --delete case. Add --no-mask and --mask
- options.
- (opts): Add -x and -n options.
- (main): Handle -d and -x the same. Handle -n and --mask options.
- Drop handling for -r option.
-
-2015-04-16 Corinna Vinschen <corinna@vinschen.de>
-
- * getfacl.c (usage): Align more closely to Linux version. Add new
- options -c, -e, -E. Change formatting to accommodate longer options.
- (longopts): Rename --noname to --numeric. Keep --noname for backward
- compatibility. Add --omit-header, --all-effective and --no-effective
- options.
- (opts): Add -c, -e and -E option.
- (main): Handle new -c, -e, and -E options.
-
2015-02-28 Corinna Vinschen <corinna@vinschen.de>
* getfacl.c (usage): Change --all to --access, --dir to --default.
diff --git a/winsup/utils/getfacl.c b/winsup/utils/getfacl.c
index 45e5e2090..0bc484877 100644
--- a/winsup/utils/getfacl.c
+++ b/winsup/utils/getfacl.c
@@ -66,68 +66,61 @@ static void
usage (FILE * stream)
{
fprintf (stream, "Usage: %s [-adn] FILE [FILE2...]\n"
- "\n"
- "Display file and directory access control lists (ACLs).\n"
- "\n"
- " -a, --access display the file access control list only\n"
- " -d, --default display the default access control list only\n"
- " -c, --omit-header do not display the comment header\n"
- " -e, --all-effective print all effective rights\n"
- " -E, --no-effective print no effective rights\n"
- " -n, --numeric print numeric user/group identifiers\n"
- " -V, --version print version and exit\n"
- " -h, --help this help text\n"
- "\n"
- "When multiple files are specified on the command line, a blank\n"
- "line separates the ACLs for each file.\n", prog_name);
+ "\n"
+ "Display file and directory access control lists (ACLs).\n"
+ "\n"
+ " -a, --access display the file access control list\n"
+ " -d, --default display the default access control list\n"
+ " -h, --help print help explaining the command line options\n"
+ " -n, --noname display user and group IDs instead of names\n"
+ " -V, --version output version information and exit\n"
+ "\n"
+ "When multiple files are specified on the command line, a blank\n"
+ "line separates the ACLs for each file.\n", prog_name);
if (stream == stdout)
{
fprintf (stream, ""
- "For each argument that is a regular file, special file or\n"
- "directory, getfacl displays the owner, the group, and the ACL.\n"
- "For directories getfacl displays additionally the default ACL.\n"
- "\n"
- "With no options specified, getfacl displays the filename, the\n"
- "owner, the group, the setuid (s), setgid (s), and sticky (t)\n"
- "bits if available, and both the ACL and the default ACL, if it\n"
- "exists.\n"
- "\n"
- "The format for ACL output is as follows:\n"
- " # file: filename\n"
- " # owner: name or uid\n"
- " # group: name or uid\n"
- " # flags: sst\n"
- " user::perm\n"
- " user:name or uid:perm\n"
- " group::perm\n"
- " group:name or gid:perm\n"
- " mask:perm\n"
- " other:perm\n"
- " default:user::perm\n"
- " default:user:name or uid:perm\n"
- " default:group::perm\n"
- " default:group:name or gid:perm\n"
- " default:mask:perm\n"
- " default:other:perm\n"
- "\n");
+ "For each argument that is a regular file, special file or\n"
+ "directory, getfacl displays the owner, the group, and the ACL.\n"
+ "For directories getfacl displays additionally the default ACL.\n"
+ "\n"
+ "With no options specified, getfacl displays the filename, the\n"
+ "owner, the group, the setuid (s), setgid (s), and sticky (t)\n"
+ "bits if available, and both the ACL and the default ACL, if it\n"
+ "exists.\n"
+ "\n"
+ "The format for ACL output is as follows:\n"
+ " # file: filename\n"
+ " # owner: name or uid\n"
+ " # group: name or uid\n"
+ " # flags: sst\n"
+ " user::perm\n"
+ " user:name or uid:perm\n"
+ " group::perm\n"
+ " group:name or gid:perm\n"
+ " mask:perm\n"
+ " other:perm\n"
+ " default:user::perm\n"
+ " default:user:name or uid:perm\n"
+ " default:group::perm\n"
+ " default:group:name or gid:perm\n"
+ " default:mask:perm\n"
+ " default:other:perm\n"
+ "\n");
}
}
struct option longopts[] = {
{"access", no_argument, NULL, 'a'},
{"all", no_argument, NULL, 'a'},
- {"omit-header", no_argument, NULL, 'c'},
- {"all-effective", no_argument, NULL, 'e'},
- {"no-effective", no_argument, NULL, 'E'},
{"default", no_argument, NULL, 'd'},
{"dir", no_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
- {"noname", no_argument, NULL, 'n'}, /* Backward compat */
- {"numeric", no_argument, NULL, 'n'},
+ {"noname", no_argument, NULL, 'n'},
{"version", no_argument, NULL, 'V'},
{0, no_argument, NULL, 0}
};
-const char *opts = "acdeEhnV";
+const char *opts = "adhnV";
static void
print_version ()
@@ -149,11 +142,8 @@ main (int argc, char **argv)
int c;
int ret = 0;
int aopt = 0;
- int copt = 0;
- int eopt = 0;
int dopt = 0;
int nopt = 0;
- int istty = isatty (fileno (stdout));
struct stat st;
aclent_t acls[MAX_ACL_ENTRIES];
@@ -165,18 +155,9 @@ main (int argc, char **argv)
case 'a':
aopt = 1;
break;
- case 'c':
- copt = 1;
- break;
case 'd':
dopt = 1;
break;
- case 'e':
- eopt = 1;
- break;
- case 'E':
- eopt = -1;
- break;
case 'h':
usage (stdout);
return 0;
@@ -198,8 +179,6 @@ main (int argc, char **argv)
for (; optind < argc; ++optind)
{
int i, num_acls;
- mode_t mask = S_IRWXO, def_mask = S_IRWXO;
-
if (stat (argv[optind], &st)
|| (num_acls = acl (argv[optind], GETACL, MAX_ACL_ENTRIES, acls)) < 0)
{
@@ -208,42 +187,28 @@ main (int argc, char **argv)
ret = 2;
continue;
}
- if (!copt)
+ printf ("# file: %s\n", argv[optind]);
+ if (nopt)
{
- printf ("# file: %s\n", argv[optind]);
- if (nopt)
- {
- printf ("# owner: %lu\n", (unsigned long)st.st_uid);
- printf ("# group: %lu\n", (unsigned long)st.st_gid);
- }
- else
- {
- printf ("# owner: %s\n", username (st.st_uid));
- printf ("# group: %s\n", groupname (st.st_gid));
- }
- if (st.st_mode & (S_ISUID | S_ISGID | S_ISVTX))
- printf ("# flags: %c%c%c\n", (st.st_mode & S_ISUID) ? 's' : '-',
- (st.st_mode & S_ISGID) ? 's' : '-',
- (st.st_mode & S_ISVTX) ? 't' : '-');
+ printf ("# owner: %lu\n", (unsigned long)st.st_uid);
+ printf ("# group: %lu\n", (unsigned long)st.st_gid);
}
- for (i = 0; i < num_acls; ++i)
+ else
{
- if (acls[i].a_type == CLASS_OBJ)
- mask = acls[i].a_perm;
- else if (acls[i].a_type == DEF_CLASS_OBJ)
- def_mask = acls[i].a_perm;
+ printf ("# owner: %s\n", username (st.st_uid));
+ printf ("# group: %s\n", groupname (st.st_gid));
}
+ if (st.st_mode & (S_ISUID | S_ISGID | S_ISVTX))
+ printf ("# flags: %c%c%c\n", (st.st_mode & S_ISUID) ? 's' : '-',
+ (st.st_mode & S_ISGID) ? 's' : '-',
+ (st.st_mode & S_ISVTX) ? 't' : '-');
for (i = 0; i < num_acls; ++i)
{
- int n = 0;
- int print_effective = 0;
- mode_t effective = acls[i].a_perm;
-
if (acls[i].a_type & ACL_DEFAULT)
{
if (aopt)
continue;
- n += printf ("default:");
+ printf ("default:");
}
else if (dopt)
continue;
@@ -254,18 +219,18 @@ main (int argc, char **argv)
break;
case USER:
if (nopt)
- n += printf ("user:%lu:", (unsigned long)acls[i].a_id);
+ printf ("user:%lu:", (unsigned long)acls[i].a_id);
else
- n += printf ("user:%s:", username (acls[i].a_id));
+ printf ("user:%s:", username (acls[i].a_id));
break;
case GROUP_OBJ:
- n += printf ("group::");
+ printf ("group::");
break;
case GROUP:
if (nopt)
- n += printf ("group:%lu:", (unsigned long)acls[i].a_id);
+ printf ("group:%lu:", (unsigned long)acls[i].a_id);
else
- n += printf ("group:%s:", groupname (acls[i].a_id));
+ printf ("group:%s:", groupname (acls[i].a_id));
break;
case CLASS_OBJ:
printf ("mask:");
@@ -274,53 +239,7 @@ main (int argc, char **argv)
printf ("other:");
break;
}
- n += printf ("%s", permstr (acls[i].a_perm));
- switch (acls[i].a_type)
- {
- case USER:
- case GROUP_OBJ:
- effective = acls[i].a_perm & mask;
- print_effective = 1;
- break;
- case GROUP:
- /* Special case SYSTEM and Admins group: The mask only
- applies to them as far as the execute bit is concerned. */
- if (acls[i].a_id == 18 || acls[i].a_id == 544)
- effective = acls[i].a_perm & (mask | S_IROTH | S_IWOTH);
- else
- effective = acls[i].a_perm & mask;
- print_effective = 1;
- break;
- case DEF_USER:
- case DEF_GROUP_OBJ:
- effective = acls[i].a_perm & def_mask;
- print_effective = 1;
- break;
- case DEF_GROUP:
- /* Special case SYSTEM and Admins group: The mask only
- applies to them as far as the execute bit is concerned. */
- if (acls[i].a_id == 18 || acls[i].a_id == 544)
- effective = acls[i].a_perm & (def_mask | S_IROTH | S_IWOTH);
- else
- effective = acls[i].a_perm & def_mask;
- print_effective = 1;
- break;
- }
- if (print_effective && eopt >= 0
- && (eopt > 0 || effective != acls[i].a_perm))
- {
- if (istty)
- {
- n = 40 - n;
- if (n <= 0)
- n = 1;
- printf ("%*s", n, " ");
- }
- else
- putchar ('\t');
- printf ("#effective:%s", permstr (effective));
- }
- putchar ('\n');
+ printf ("%s\n", permstr (acls[i].a_perm));
}
putchar ('\n');
}
diff --git a/winsup/utils/setfacl.c b/winsup/utils/setfacl.c
index 20a42f33c..ba674d009 100644
--- a/winsup/utils/setfacl.c
+++ b/winsup/utils/setfacl.c
@@ -1,7 +1,7 @@
/* setfacl.c
- Copyright 2000, 2001, 2002, 2003, 2006, 2008, 2009, 2010, 2011, 2014,
- 2015 Red Hat Inc.
+ Copyright 2000, 2001, 2002, 2003, 2006, 2008, 2009, 2010, 2011, 2014
+ Red Hat Inc.
Written by Corinna Vinschen <vinschen@redhat.com>
@@ -53,8 +53,6 @@ typedef enum {
SetFromFile
} action_t;
-int mask_opt = 0;
-
mode_t getperm (char *in)
{
if (isdigit ((unsigned char) *in) && !in[1])
@@ -241,7 +239,7 @@ searchace (aclent_t *aclp, int nentries, int type, int id)
}
int
-delacl (aclent_t *tgt, int tcnt, aclent_t *src, int scnt)
+modacl (aclent_t *tgt, int tcnt, aclent_t *src, int scnt)
{
int t, s, i;
@@ -251,112 +249,22 @@ delacl (aclent_t *tgt, int tcnt, aclent_t *src, int scnt)
(src[s].a_type & (USER | GROUP)) ? src[s].a_id : -1);
if (t < 0)
return -1;
- if (t < tcnt)
+ if (src[s].a_perm == ILLEGAL_MODE)
{
- for (i = t + 1; i < tcnt; ++i)
- tgt[i - 1] = tgt[i];
- --tcnt;
+ if (t < tcnt)
+ {
+ for (i = t + 1; i < tcnt; ++i)
+ tgt[i - 1] = tgt[i];
+ --tcnt;
+ }
}
- }
- return tcnt;
-}
-
-int
-modacl (aclent_t *tgt, int tcnt, aclent_t *src, int scnt)
-{
- int t, s;
- int recompute_mask = 0, recompute_def_mask = 0;
- int need_mask = 0, need_def_mask = 0;
- int has_mask = 0, has_def_mask = 0;
- int mask_idx = -1, def_mask_idx = -1;
- mode_t mask = 0, def_mask = 0;
-
- /* Replace or add given acl entries. */
- for (s = 0; s < scnt; ++s)
- {
- t = searchace (tgt, MAX_ACL_ENTRIES, src[s].a_type,
- (src[s].a_type & (USER | GROUP)) ? src[s].a_id : -1);
- if (t < 0)
- return -1;
- tgt[t] = src[s];
- if (t >= tcnt)
- ++tcnt;
- /* Note if CLASS_OBJ and/or DEF_CLASS_OBJ are present in input. */
- if (src[s].a_type == CLASS_OBJ)
- has_mask = 1;
- else if (src[s].a_type == DEF_CLASS_OBJ)
- has_def_mask = 1;
- else if (src[s].a_type & ACL_DEFAULT)
- recompute_def_mask = 1;
else
- recompute_mask = 1;
- }
- /* Now recompute mask, if requested (default) */
- for (t = 0; t < tcnt; ++t)
- {
- switch (tgt[t].a_type)
{
- case USER:
- case GROUP:
- /* Do we need a CLASS_OBJ at all? */
- need_mask = 1;
- /*FALLTHRU*/
- case GROUP_OBJ:
- /* Compute resulting maximum mask. */
- mask |= tgt[t].a_perm;
- break;
- case CLASS_OBJ:
- /* Do we already have a CLASS_OBJ? */
- mask_idx = t;
- break;
- case DEF_USER:
- case DEF_GROUP:
- /* Do we need a DEF_CLASS_OBJ at all? */
- need_def_mask = 1;
- /*FALLTHRU*/
- case DEF_GROUP_OBJ:
- /* Compute resulting maximum default mask. */
- def_mask |= tgt[t].a_perm;
- break;
- case DEF_CLASS_OBJ:
- /* Do we already have a DEF_CLASS_OBJ? */
- def_mask_idx = t;
- break;
+ tgt[t] = src[s];
+ if (t >= tcnt)
+ ++tcnt;
}
}
- /* Recompute mask, if requested */
- if (recompute_mask && need_mask && mask_opt >= 0
- && (mask_opt > 0 || !has_mask))
- {
- if (mask_idx >= 0)
- t = mask_idx;
- else
- t = searchace (tgt, MAX_ACL_ENTRIES, CLASS_OBJ, -1);
- if (t < 0)
- return -1;
- if (t >= tcnt)
- ++tcnt;
- tgt[t].a_type = CLASS_OBJ;
- tgt[t].a_id = -1;
- tgt[t].a_perm = mask;
- }
- /* Recompute default mask, if requested */
- if (recompute_def_mask && need_def_mask && mask_opt >= 0
- && (mask_opt > 0 || !has_def_mask))
- {
- if (def_mask_idx >= 0)
- t = def_mask_idx;
- else
- t = searchace (tgt, MAX_ACL_ENTRIES, DEF_CLASS_OBJ, -1);
- if (t < 0)
- return -1;
- if (t >= tcnt)
- ++tcnt;
- tgt[t].a_type = DEF_CLASS_OBJ;
- tgt[t].a_id = -1;
- tgt[t].a_perm = def_mask;
- }
-
return tcnt;
}
@@ -425,7 +333,7 @@ addmissing (aclent_t *tgt, int tcnt)
}
int
-delallacl (aclent_t *tgt, int tcnt, action_t action)
+delacl (aclent_t *tgt, int tcnt, action_t action)
{
int t;
@@ -461,7 +369,7 @@ setfacl (action_t action, const char *path, aclent_t *acls, int cnt)
break;
case Delete:
if ((lcnt = acl (path, GETACL, MAX_ACL_ENTRIES, lacl)) < 0
- || (lcnt = delacl (lacl, lcnt, acls, cnt)) < 0
+ || (lcnt = modacl (lacl, lcnt, acls, cnt)) < 0
|| (lcnt = acl (path, SETACL, lcnt, lacl)) < 0)
{
perror (prog_name);
@@ -471,7 +379,7 @@ setfacl (action_t action, const char *path, aclent_t *acls, int cnt)
case DeleteAll:
case DeleteDef:
if ((lcnt = acl (path, GETACL, MAX_ACL_ENTRIES, lacl)) < 0
- || (lcnt = delallacl (lacl, lcnt, action)) < 0
+ || (lcnt = delacl (lacl, lcnt, action)) < 0
|| (lcnt = acl (path, SETACL, lcnt, lacl)) < 0)
{
perror (prog_name);
@@ -496,126 +404,114 @@ static void
usage (FILE *stream)
{
fprintf (stream, ""
- "Usage: %s {-f ACL_FILE | -s acl_entries} FILE...\n"
- " %s {-b|[-x acl_entries] [-m acl_entries]} FILE...\n"
- "\n"
- "Modify file and directory access control lists (ACLs)\n"
- "\n"
- " -b, --remove-all remove all extended ACL entries\n"
- " -x, --delete delete one or more specified ACL entries\n"
- " -f, --file set ACL entries for FILE to ACL entries read\n"
- " from ACL_FILE\n"
- " -k, --remove-default remove all default ACL entries\n"
- " -m, --modify modify one or more specified ACL entries\n"
- " -n, --no-mask don't recalculate the effective rights mask\n"
- " --mask do recalculate the effective rights mask\n"
- " -s, --substitute substitute specified ACL entries on FILE\n"
- " -V, --version print version and exit\n"
- " -h, --help this help text\n"
- "\n"
- "At least one of (-b, -x, -f, -k, -m, -s) must be specified\n"
- "\n", prog_name, prog_name);
- if (stream == stdout)
- {
- printf(""
- " Acl_entries are one or more comma-separated ACL entries \n"
- " from the following list:\n"
- "\n"
- " u[ser]::perm\n"
- " u[ser]:uid:perm\n"
- " g[roup]::perm\n"
- " g[roup]:gid:perm\n"
- " m[ask]:perm\n"
- " o[ther]:perm\n"
- "\n"
- " Default entries are like the above with the additional\n"
- " default identifier. For example: \n"
- "\n"
- " d[efault]:u[ser]:uid:perm\n"
- "\n"
- " 'perm' is either a 3-char permissions string in the form\n"
- " \"rwx\" with the character - for no permission\n"
- " or it is the octal representation of the permissions, a\n"
- " value from 0 (equivalent to \"---\") to 7 (\"rwx\").\n"
- " 'uid' is a user name or a numerical uid.\n"
- " 'gid' is a group name or a numerical gid.\n"
- "\n"
- "\n"
- "For each file given as parameter, %s will either replace its\n"
- "complete ACL (-s, -f), or it will add, modify, or delete ACL\n"
- "entries.\n"
- "\n"
- "The following options are supported:\n"
- "\n"
- "-b, --remove-all\n"
- " Remove all extended ACL entries. The base ACL entries of the\n"
- " owner, group and others are retained.\n"
- "\n"
- "-x, --delete\n"
- " Delete one or more specified entries from the file's ACL.\n"
- " The owner, group and others entries must not be deleted.\n"
- " Acl_entries to be deleted should be specified without\n"
- " permissions, as in the following list:\n"
- "\n"
- " u[ser]:uid[:]\n"
- " g[roup]:gid[:]\n"
- " m[ask][:]\n"
- " d[efault]:u[ser][:uid]\n"
- " d[efault]:g[roup][:gid]\n"
- " d[efault]:m[ask][:]\n"
- " d[efault]:o[ther][:]\n"
- "\n"
- "-f, --file\n"
- " Take the Acl_entries from ACL_FILE one per line. Whitespace\n"
- " characters are ignored, and the character \"#\" may be used\n"
- " to start a comment. The special filename \"-\" indicates\n"
- " reading from stdin.\n"
- " Required entries are\n"
- " - One user entry for the owner of the file.\n"
- " - One group entry for the group of the file.\n"
- " - One other entry.\n"
- " If additional user and group entries are given:\n"
- " - A mask entry for the file group class of the file.\n"
- " - No duplicate user or group entries with the same uid/gid.\n"
- " If it is a directory:\n"
- " - One default user entry for the owner of the file.\n"
- " - One default group entry for the group of the file.\n"
- " - One default mask entry for the file group class.\n"
- " - One default other entry.\n"
- "\n"
- "-k, --remove-default\n"
- " Remove all default ACL entries. If no default ACL entries exist,\n"
- " no warnings are issued.\n"
- "\n"
- "-m, --modify\n"
- " Add or modify one or more specified ACL entries. Acl_entries is\n"
- " a comma-separated list of entries from the same list as above.\n"
- "\n"
- "-n, --no-mask\n"
- " Valid in conjunction with -m. Do not recalculate the effective\n"
- " rights mask. The default behavior of setfacl is to recalculate the\n"
- " ACL mask entry, unless a mask entry was explicitly given. The\n"
- " mask entry is set to the union of all permissions of the owning\n"
- " group, and all named user and group entries. (These are exactly\n"
- " the entries affected by the mask entry).\n"
- "\n"
- "--mask\n"
- " Valid in conjunction with -m. Do recalculate the effective rights\n"
- " mask, even if an ACL mask entry was explicitly given. (See the\n"
- " -n option.)\n"
- "\n"
- "-s, --substitute\n"
- " Like -f, but substitute the file's ACL with ACL entries\n"
- " specified in a comma-separated list on the command line.\n"
- "\n"
- "While the -x and -m options may be used in the same command, the\n"
- "-f and -s options may be used only exclusively.\n"
- "\n"
- "Directories may contain default ACL entries. Files created\n"
- "in a directory that contains default ACL entries will have\n"
- "permissions according to the combination of the current umask,\n"
- "the explicit permissions requested and the default ACL entries\n"
- "\n", prog_name);
+ "Usage: %s [-r] {-f ACL_FILE | -s acl_entries} FILE...\n"
+ " %s [-r] {-b|[-d acl_entries] [-m acl_entries]} FILE...\n"
+ "\n"
+ "Modify file and directory access control lists (ACLs)\n"
+ "\n"
+ " -b, --remove-all remove all extended ACL entries\n"
+ " -d, --delete delete one or more specified ACL entries\n"
+ " -f, --file set ACL entries for FILE to ACL entries read\n"
+ " from a ACL_FILE\n"
+ " -k, --remove-default\n"
+ " remove all default ACL entries\n"
+ " -m, --modify modify one or more specified ACL entries\n"
+ " -r, --replace replace mask entry with maximum permissions\n"
+ " needed for the file group class\n"
+ " -s, --substitute substitute specified ACL entries for the\n"
+ " ACL of FILE\n"
+ " -h, --help output usage information and exit\n"
+ " -V, --version output version information and exit\n"
+ "\n"
+ "At least one of (-b, -d, -f, -k, -m, -s) must be specified\n"
+ "\n", prog_name, prog_name);
+ if (stream == stdout)
+ {
+ printf(""
+ " Acl_entries are one or more comma-separated ACL entries \n"
+ " from the following list:\n"
+ "\n"
+ " u[ser]::perm\n"
+ " u[ser]:uid:perm\n"
+ " g[roup]::perm\n"
+ " g[roup]:gid:perm\n"
+ " m[ask]:perm\n"
+ " o[ther]:perm\n"
+ "\n"
+ " Default entries are like the above with the additional\n"
+ " default identifier. For example: \n"
+ "\n"
+ " d[efault]:u[ser]:uid:perm\n"
+ "\n"
+ " 'perm' is either a 3-char permissions string in the form\n"
+ " \"rwx\" with the character - for no permission\n"
+ " or it is the octal representation of the permissions, a\n"
+ " value from 0 (equivalent to \"---\") to 7 (\"rwx\").\n"
+ " 'uid' is a user name or a numerical uid.\n"
+ " 'gid' is a group name or a numerical gid.\n"
+ "\n"
+ "\n"
+ "For each file given as parameter, %s will either replace its\n"
+ "complete ACL (-s, -f), or it will add, modify, or delete ACL\n"
+ "entries.\n"
+ "\n"
+ "The following options are supported:\n"
+ "\n"
+ "-b Remove all extended ACL entries. The base ACL entries of the\n"
+ " owner, group and others are retained.\n"
+ "\n"
+ "-d Delete one or more specified entries from the file's ACL.\n"
+ " The owner, group and others entries must not be deleted.\n"
+ " Acl_entries to be deleted should be specified without\n"
+ " permissions, as in the following list:\n"
+ "\n"
+ " u[ser]:uid[:]\n"
+ " g[roup]:gid[:]\n"
+ " m[ask][:]\n"
+ " d[efault]:u[ser][:uid]\n"
+ " d[efault]:g[roup][:gid]\n"
+ " d[efault]:m[ask][:]\n"
+ " d[efault]:o[ther][:]\n"
+ "\n"
+ "-f Take the Acl_entries from ACL_FILE one per line. Whitespace\n"
+ " characters are ignored, and the character \"#\" may be used\n"
+ " to start a comment. The special filename \"-\" indicates\n"
+ " reading from stdin.\n"
+ " Required entries are\n"
+ " - One user entry for the owner of the file.\n"
+ " - One group entry for the group of the file.\n"
+ " - One other entry.\n"
+ " If additional user and group entries are given:\n"
+ " - A mask entry for the file group class of the file.\n"
+ " - No duplicate user or group entries with the same uid/gid.\n"
+ " If it is a directory:\n"
+ " - One default user entry for the owner of the file.\n"
+ " - One default group entry for the group of the file.\n"
+ " - One default mask entry for the file group class.\n"
+ " - One default other entry.\n"
+ "\n"
+ "-k Remove all default ACL entries. If no default ACL entries\n"
+ " exist, no warnings are issued.\n"
+ "\n"
+ "-m Add or modify one or more specified ACL entries.\n"
+ " Acl_entries is a comma-separated list of entries from the \n"
+ " same list as above.\n"
+ "\n"
+ "-r Causes the permissions specified in the mask entry to be\n"
+ " ignored and replaced by the maximum permissions needed for\n"
+ " the file group class.\n"
+ "\n"
+ "-s Like -f, but substitute the file's ACL with Acl_entries\n"
+ " specified in a comma-separated list on the command line.\n"
+ "\n"
+ "While the -d and -m options may be used in the same command, the\n"
+ "-f and -s options may be used only exclusively.\n"
+ "\n"
+ "Directories may contain default ACL entries. Files created\n"
+ "in a directory that contains default ACL entries will have\n"
+ "permissions according to the combination of the current umask,\n"
+ "the explicit permissions requested and the default ACL entries\n"
+ "\n", prog_name);
}
else
fprintf(stream, "Try '%s --help' for more information.\n", prog_name);
@@ -623,19 +519,17 @@ usage (FILE *stream)
struct option longopts[] = {
{"remove-all", no_argument, NULL, 'b'},
- {"delete", required_argument, NULL, 'x'},
+ {"delete", required_argument, NULL, 'd'},
{"file", required_argument, NULL, 'f'},
{"remove-default", no_argument, NULL, 'k'},
{"modify", required_argument, NULL, 'm'},
- {"no-mask", required_argument, NULL, 'n'},
- {"mask", required_argument, NULL, '\n'},
{"replace", no_argument, NULL, 'r'},
{"substitute", required_argument, NULL, 's'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{0, no_argument, NULL, 0}
};
-const char *opts = "bd:f:hkm:nrs:Vx";
+const char *opts = "bd:f:hkm:rs:V";
static void
print_version ()
@@ -656,6 +550,7 @@ main (int argc, char **argv)
{
int c;
action_t action = NoAction;
+ int ropt = 0;
aclent_t acls[MAX_ACL_ENTRIES];
int aclidx = 0;
int ret = 0;
@@ -675,8 +570,7 @@ main (int argc, char **argv)
return 1;
}
break;
- case 'd': /* Backward compat */
- case 'x':
+ case 'd':
if (action == NoAction)
action = Delete;
else if (action == Modify)
@@ -734,13 +628,14 @@ main (int argc, char **argv)
return 2;
}
break;
- case 'n':
- mask_opt = -1;
- break;
- case '\n':
- mask_opt = 1;
- break;
case 'r':
+ if (!ropt)
+ ropt = 1;
+ else
+ {
+ usage (stderr);
+ return 1;
+ }
break;
case 's':
if (action == NoAction)