From 9ddf063921f5202100f8e36bb451ae5ac9f76d37 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Wed, 6 Jan 2016 18:41:36 +0100 Subject: [PATCH] Implement POSIX.1e ACL functions * Makefile.in (DLL_OFILES): Add sec_posixacl.o. (SUBLIBS): Add libacl.a (libacl.a): New rule to create libacl.a. * common.din: Export POSIX ACL functions as well as most libacl.a extensions. * fhandler.h (fhander_base::acl_get): New prototype. (fhander_base::acl_set): Ditto. (fhandler_disk_file::acl_get): Ditto. (fhandler_disk_file::acl_set): Ditto. * include/acl/libacl.h: New file. * include/cygwin/version.h: Bump API minor version. * include/sys/acl.h: Drop including cygwin/acl.h. Accommodate throughout Cygwin. Add POSIX ACL definitions. * sec_acl.cc: Include sec_posixacl.h. Replace ILLEGAL_UID and ILLEGAL_GID with ACL_UNDEFINED_ID where sensible. (__aclcheck): New internal acl check function to be used for Solaris and POSIX ACLs. (aclcheck32): Call __aclcheck. (__aclcalcmask): New function to compute ACL_MASK value. (__aclsort): New internal acl sort function to be used for Solaris and POSIX ACLs. (aclsort32): Call __aclsort. (permtostr): Work directly on provided buffer. (__acltotext): New internal acltotext function to be used for Solaris and POSIX ACLs. (acltotext32): Call __acltotext. (__aclfromtext): New internal aclfromtext function to be used for Solaris and POSIX ACLs. (aclfromtext32): Call __aclfromtext. * sec_posixacl.cc: New file implemeting POSIX ACL functions. * sec_posixacl.h: New internal header. Signed-off-by: Corinna Vinschen --- winsup/cygwin/Makefile.in | 6 +- winsup/cygwin/common.din | 39 + winsup/cygwin/fhandler.cc | 2 +- winsup/cygwin/fhandler.h | 5 + winsup/cygwin/fhandler_disk_file.cc | 2 +- winsup/cygwin/fhandler_socket.cc | 2 +- winsup/cygwin/fhandler_tty.cc | 2 +- winsup/cygwin/fhandler_virtual.cc | 2 +- winsup/cygwin/include/acl/libacl.h | 55 ++ winsup/cygwin/include/cygwin/version.h | 3 +- winsup/cygwin/include/sys/acl.h | 85 +- winsup/cygwin/sec_acl.cc | 816 +++++++++++------- winsup/cygwin/sec_helper.cc | 2 +- winsup/cygwin/sec_posixacl.cc | 1052 ++++++++++++++++++++++++ winsup/cygwin/sec_posixacl.h | 68 ++ winsup/cygwin/security.cc | 2 +- winsup/utils/getfacl.c | 3 +- winsup/utils/setfacl.c | 3 +- 18 files changed, 1849 insertions(+), 300 deletions(-) create mode 100644 winsup/cygwin/include/acl/libacl.h create mode 100644 winsup/cygwin/sec_posixacl.cc create mode 100644 winsup/cygwin/sec_posixacl.h diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index 271a5be1b..fac9b3e0d 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -267,6 +267,7 @@ DLL_OFILES:= \ sec_acl.o \ sec_auth.o \ sec_helper.o \ + sec_posixacl.o \ security.o \ select.o \ sem.o \ @@ -443,7 +444,7 @@ endif API_VER:=$(srcdir)/include/cygwin/version.h LIB_NAME:=libcygwin.a -SUBLIBS:=libpthread.a libutil.a ${CURDIR}/libm.a ${CURDIR}/libc.a libdl.a libresolv.a librt.a +SUBLIBS:=libpthread.a libutil.a ${CURDIR}/libm.a ${CURDIR}/libc.a libdl.a libresolv.a librt.a libacl.a EXTRALIBS:=libautomode.a libbinmode.a libtextmode.a libtextreadmode.a INSTOBJS:=automode.o binmode.o textmode.o textreadmode.o TARGET_LIBS:=$(LIB_NAME) $(CYGWIN_START) $(GMON_START) $(LIBGMON_A) $(SUBLIBS) $(INSTOBJS) $(EXTRALIBS) @@ -643,6 +644,9 @@ libresolv.a: ${LIB_NAME} minires.o librt.a: ${LIB_NAME} posix_ipc.o ${speclib} ${@F} +libacl.a: ${LIB_NAME} sec_posixacl.o + ${speclib} ${@F} + ${EXTRALIBS}: lib%.a: %.o $(AR) cru $@ $? diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index c39d26505..fe714d874 100644 --- a/winsup/cygwin/common.din +++ b/winsup/cygwin/common.din @@ -117,6 +117,45 @@ accept = cygwin_accept SIGFE accept4 SIGFE access SIGFE acl SIGFE +acl_add_perm NOSIGFE +acl_calc_mask SIGFE +acl_check NOSIGFE +acl_clear_perms NOSIGFE +acl_cmp SIGFE +acl_copy_entry NOSIGFE +acl_copy_ext NOSIGFE +acl_copy_int NOSIGFE +acl_create_entry SIGFE +acl_delete_def_file SIGFE +acl_delete_entry NOSIGFE +acl_delete_perm NOSIGFE +acl_dup SIGFE +acl_entries NOSIGFE +acl_equiv_mode SIGFE +acl_error NOSIGFE +acl_extended_fd SIGFE +acl_extended_file SIGFE +acl_extended_file_nofollow SIGFE +acl_free SIGFE +acl_from_mode NOSIGFE +acl_from_text SIGFE +acl_get_entry NOSIGFE +acl_get_fd SIGFE +acl_get_file SIGFE +acl_get_perm NOSIGFE +acl_get_permset NOSIGFE +acl_get_qualifier SIGFE +acl_get_tag_type NOSIGFE +acl_init SIGFE +acl_set_fd SIGFE +acl_set_file SIGFE +acl_set_permset NOSIGFE +acl_set_qualifier NOSIGFE +acl_set_tag_type NOSIGFE +acl_size NOSIGFE +acl_to_any_text SIGFE +acl_to_text SIGFE +acl_valid NOSIGFE aclcheck NOSIGFE aclfrommode SIGFE aclfrompbits SIGFE diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc index 7e4d996ce..33743d44d 100644 --- a/winsup/cygwin/fhandler.cc +++ b/winsup/cygwin/fhandler.cc @@ -13,7 +13,7 @@ details. */ #include #include #include -#include +#include #include #include "cygerrno.h" #include "perprocess.h" diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index d94f38d98..134fd716b 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -56,6 +56,7 @@ typedef struct __DIR DIR; struct dirent; struct iovec; struct acl; +struct __acl_t; enum dirent_states { @@ -355,6 +356,8 @@ public: virtual int __reg1 fchmod (mode_t mode); virtual int __reg2 fchown (uid_t uid, gid_t gid); virtual int __reg3 facl (int, int, struct acl *); + virtual struct __acl_t * __reg2 acl_get (uint32_t); + virtual int __reg3 acl_set (struct __acl_t *, uint32_t); virtual ssize_t __reg3 fgetxattr (const char *, void *, size_t); virtual int __reg3 fsetxattr (const char *, const void *, size_t, int); virtual int __reg3 fadvise (off_t, off_t, int); @@ -1011,6 +1014,8 @@ class fhandler_disk_file: public fhandler_base int __reg1 fchmod (mode_t mode); int __reg2 fchown (uid_t uid, gid_t gid); int __reg3 facl (int, int, struct acl *); + struct __acl_t * __reg2 acl_get (uint32_t); + int __reg3 acl_set (struct __acl_t *, uint32_t); ssize_t __reg3 fgetxattr (const char *, void *, size_t); int __reg3 fsetxattr (const char *, const void *, size_t, int); int __reg3 fadvise (off_t, off_t, int); diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index 7d729e3ad..470dae872 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -13,7 +13,7 @@ details. */ #include #include #include -#include +#include #include #include "cygerrno.h" #include "security.h" diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index 7d3efad9b..990cdc713 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -41,7 +41,7 @@ #include "wininfo.h" #include #include -#include +#include #include "cygtls.h" #include #include "ntdll.h" diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc index ee37ed48c..a39a56674 100644 --- a/winsup/cygwin/fhandler_tty.cc +++ b/winsup/cygwin/fhandler_tty.cc @@ -12,7 +12,7 @@ details. */ #include "winsup.h" #include #include -#include +#include #include #include "cygerrno.h" #include "security.h" diff --git a/winsup/cygwin/fhandler_virtual.cc b/winsup/cygwin/fhandler_virtual.cc index 2d56d7413..8f302a758 100644 --- a/winsup/cygwin/fhandler_virtual.cc +++ b/winsup/cygwin/fhandler_virtual.cc @@ -10,7 +10,7 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" -#include +#include #include #include "cygerrno.h" #include "path.h" diff --git a/winsup/cygwin/include/acl/libacl.h b/winsup/cygwin/include/acl/libacl.h new file mode 100644 index 000000000..b93d686b1 --- /dev/null +++ b/winsup/cygwin/include/acl/libacl.h @@ -0,0 +1,55 @@ +/* acl/libacl.h: Non-POSIX extensions of libacl + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifndef _ACL_LIBACL_H +#define _ACL_LIBACL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Sync'd with cygwin/acl.h values. */ +#define ACL_MULTI_ERROR (0x4) +#define ACL_DUPLICATE_ERROR (0x5) +#define ACL_ENTRY_ERROR (0x6) +#define ACL_MISS_ERROR (0x7) + +/* acl_to_any_text options. */ +#define TEXT_ABBREVIATE (0x01) +#define TEXT_NUMERIC_IDS (0x02) +#define TEXT_SOME_EFFECTIVE (0x04) +#define TEXT_ALL_EFFECTIVE (0x08) +#define TEXT_SMART_INDENT (0x10) + +extern int acl_check (acl_t __acl, int *__last); +extern int acl_cmp (acl_t __acl1, acl_t __acl2); +extern int acl_entries (acl_t __acl); +extern int acl_equiv_mode (acl_t __acl, mode_t *__mode_p); +extern const char *acl_error (int __code); +extern int acl_extended_fd (int __fd); +extern int acl_extended_file (const char *__path_p); +extern int acl_extended_file_nofollow (const char *__path_p); +extern acl_t acl_from_mode (mode_t __mode); +extern int acl_get_perm (acl_permset_t __permset_d, acl_perm_t __perm); +extern char *acl_to_any_text (acl_t __acl, const char *__prefix, + char __separator, int __options); + +#if 0 +/* TODO */ +struct error_context; +extern int perm_copy_file (const char *, const char *, struct error_context *); +extern int perm_copy_fd (const char *, int, const char *, int, + struct error_context *); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* _ACL_LIBACL_H */ diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index be85ce14f..4edb8dbc8 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -476,13 +476,14 @@ details. */ 292: Export rpmatch. 293: Convert utmpname/utmpxname to int. 294: Export clog10, clog10f. + 295: Export POSIX ACL functions. */ /* Note that we forgot to bump the api for ualarm, strtoll, strtoull, sigaltstack, sethostname. */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 294 +#define CYGWIN_VERSION_API_MINOR 295 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible diff --git a/winsup/cygwin/include/sys/acl.h b/winsup/cygwin/include/sys/acl.h index 89c38bc0f..c3a9fc1af 100644 --- a/winsup/cygwin/include/sys/acl.h +++ b/winsup/cygwin/include/sys/acl.h @@ -12,6 +12,89 @@ details. */ #ifndef _SYS_ACL_H #define _SYS_ACL_H -#include +#include <_ansi.h> +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* POSIX ACL types and functions. The implementation is based on the + internal original Solaris implementation as defined in cygwin/acl.h. + However, we don't include cygwin/acl.h from here to avoid poisoning + the namespace. */ + +/* acl_perm_t constants */ +#define ACL_READ (0x4) +#define ACL_WRITE (0x2) +#define ACL_EXECUTE (0x1) + +/* acl_tag_t constants, in sync with values from cygwin/acl.h */ +#define ACL_UNDEFINED_TAG (0x0000) +#define ACL_USER_OBJ (0x0001) +#define ACL_USER (0x0002) +#define ACL_GROUP_OBJ (0x0004) +#define ACL_GROUP (0x0008) +#define ACL_MASK (0x0010) +#define ACL_OTHER (0x0020) + +/* acl_type_t constants */ +#define ACL_TYPE_ACCESS (0x0) +#define ACL_TYPE_DEFAULT (0x1) + +/* qualifier constant */ +#define ACL_UNDEFINED_ID ((id_t) -1) + +/* entry_id constants */ +#define ACL_FIRST_ENTRY (0x0) +#define ACL_NEXT_ENTRY (0x1) + +/* types */ +typedef uint32_t acl_perm_t, acl_type_t, acl_tag_t; +typedef uint64_t acl_permset_t; +typedef uint64_t acl_entry_t; + +struct __acl_t; +typedef struct __acl_t *acl_t; + +extern int acl_add_perm (acl_permset_t __permset_d, acl_perm_t __perm); +extern int acl_calc_mask (acl_t *__acl_p); +extern int acl_clear_perms (acl_permset_t __permset_d); +extern int acl_copy_entry (acl_entry_t __dest_d, acl_entry_t __src_d); +extern ssize_t acl_copy_ext (void *__buf_p, acl_t __acl, ssize_t __size); +extern acl_t acl_copy_int (const void *__buf_p); +extern int acl_create_entry (acl_t *__acl_p, acl_entry_t *__entry_p); +extern int acl_delete_def_file (const char *__path_p); +extern int acl_delete_entry (acl_t __acl, acl_entry_t __entry_d); +extern int acl_delete_perm (acl_permset_t __permset_d, acl_perm_t __perm); +extern acl_t acl_dup (acl_t __acl); +extern int acl_free (void *__obj_p); +extern acl_t acl_from_text (const char *__buf_p); +extern int acl_get_entry (acl_t __acl, int __entry_id, + acl_entry_t *__entry_p); +extern acl_t acl_get_fd (int __fd); +extern acl_t acl_get_file (const char *__path_p, acl_type_t __type); +extern int acl_get_permset (acl_entry_t __entry_d, + acl_permset_t *__permset_p); +extern void *acl_get_qualifier (acl_entry_t __entry_d); +extern int acl_get_tag_type (acl_entry_t __entry_d, + acl_tag_t *__tag_type_p); +extern acl_t acl_init (int __count); +extern int acl_set_fd (int __fd, acl_t __acl); +extern int acl_set_file (const char *__path_p, acl_type_t __type, + acl_t __acl); +extern int acl_set_permset (acl_entry_t __entry_d, + acl_permset_t __permset_d); +extern int acl_set_qualifier (acl_entry_t __entry_d, + const void *__tag_qualifier_p); +extern int acl_set_tag_type (acl_entry_t __entry_d, acl_tag_t __tag_type); +extern ssize_t acl_size (acl_t __acl); +extern char *acl_to_text (acl_t __acl, ssize_t *__len_p); +extern int acl_valid (acl_t __acl); + +#ifdef __cplusplus +} +#endif #endif /* _SYS_ACL_H */ diff --git a/winsup/cygwin/sec_acl.cc b/winsup/cygwin/sec_acl.cc index de40717dc..052856ffd 100644 --- a/winsup/cygwin/sec_acl.cc +++ b/winsup/cygwin/sec_acl.cc @@ -13,7 +13,6 @@ details. */ #include "winsup.h" #include -#include #include #include "cygerrno.h" #include "security.h" @@ -23,6 +22,7 @@ details. */ #include "cygheap.h" #include "ntdll.h" #include "tls_pbuf.h" +#include "sec_posixacl.h" /* How does a correctly constructed new-style Windows ACL claiming to be a POSIX ACL look like? @@ -118,7 +118,8 @@ searchace (aclent_t *aclp, int nentries, int type, uid_t id) int i; for (i = 0; i < nentries; ++i) - if ((aclp[i].a_type == type && (id == ILLEGAL_UID || aclp[i].a_id == id)) + if ((aclp[i].a_type == type + && (id == ACL_UNDEFINED_ID || aclp[i].a_id == id)) || !aclp[i].a_type) return i; return -1; @@ -186,25 +187,25 @@ set_posix_access (mode_t attr, uid_t uid, gid_t gid, { aclbufp = (aclent_t *) tp.c_get (); aclbufp[0].a_type = USER_OBJ; - aclbufp[0].a_id = ILLEGAL_UID; + aclbufp[0].a_id = ACL_UNDEFINED_ID; aclbufp[0].a_perm = (attr >> 6) & S_IRWXO; aclbufp[1].a_type = GROUP_OBJ; - aclbufp[1].a_id = ILLEGAL_GID; + aclbufp[1].a_id = ACL_UNDEFINED_ID; aclbufp[1].a_perm = (attr >> 3) & S_IRWXO; aclbufp[2].a_type = OTHER_OBJ; - aclbufp[2].a_id = ILLEGAL_GID; + aclbufp[2].a_id = ACL_UNDEFINED_ID; 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_id = ACL_UNDEFINED_ID; aclbufp[3].a_perm = (attr >> 6) & S_IRWXO; aclbufp[4].a_type = GROUP_OBJ; - aclbufp[4].a_id = ILLEGAL_GID; + aclbufp[4].a_id = ACL_UNDEFINED_ID; aclbufp[4].a_perm = (attr >> 3) & S_IRWXO; aclbufp[5].a_type = OTHER_OBJ; - aclbufp[5].a_id = ILLEGAL_GID; + aclbufp[5].a_id = ACL_UNDEFINED_ID; aclbufp[5].a_perm = attr & S_IRWXO; nentries += MIN_ACL_ENTRIES; } @@ -618,19 +619,19 @@ get_posix_access (PSECURITY_DESCRIPTOR psd, if (attr_ret) *attr_ret &= S_IFMT; if (uid_ret) - *uid_ret = ILLEGAL_UID; + *uid_ret = ACL_UNDEFINED_ID; if (gid_ret) - *gid_ret = ILLEGAL_GID; + *gid_ret = ACL_UNDEFINED_ID; if (aclbufp) { aclbufp[0].a_type = USER_OBJ; - aclbufp[0].a_id = ILLEGAL_UID; + aclbufp[0].a_id = ACL_UNDEFINED_ID; aclbufp[0].a_perm = 0; aclbufp[1].a_type = GROUP_OBJ; - aclbufp[1].a_id = ILLEGAL_GID; + aclbufp[1].a_id = ACL_UNDEFINED_ID; aclbufp[1].a_perm = 0; aclbufp[2].a_type = OTHER_OBJ; - aclbufp[2].a_id = ILLEGAL_GID; + aclbufp[2].a_id = ACL_UNDEFINED_ID; aclbufp[2].a_perm = 0; return MIN_ACL_ENTRIES; } @@ -674,7 +675,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd, lacl[1].a_type = GROUP_OBJ; lacl[1].a_id = gid; lacl[2].a_type = OTHER_OBJ; - lacl[2].a_id = ILLEGAL_GID; + lacl[2].a_id = ACL_UNDEFINED_ID; /* Create array to collect SIDs of all entries in lacl. */ aclsid = (cygpsid *) tp.w_get (); aclsid[0] = owner_sid; @@ -730,7 +731,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd, >= 0) { lacl[pos].a_type = CLASS_OBJ; - lacl[pos].a_id = ILLEGAL_GID; + lacl[pos].a_id = ACL_UNDEFINED_ID; lacl[pos].a_perm = CYG_ACE_MASK_TO_POSIX (ace->Mask); aclsid[pos] = well_known_null_sid; } @@ -743,7 +744,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd, DEF_CLASS_OBJ)) >= 0) { lacl[pos].a_type = DEF_CLASS_OBJ; - lacl[pos].a_id = ILLEGAL_GID; + lacl[pos].a_id = ACL_UNDEFINED_ID; lacl[pos].a_perm = CYG_ACE_MASK_TO_POSIX (ace->Mask); aclsid[pos] = well_known_null_sid; } @@ -767,7 +768,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd, else if (ace_sid == well_known_world_sid) { type = OTHER_OBJ; - id = ILLEGAL_GID; + id = ACL_UNDEFINED_ID; if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE && !(ace->Header.AceFlags & INHERIT_ONLY)) saw_other_obj = true; @@ -776,14 +777,14 @@ get_posix_access (PSECURITY_DESCRIPTOR psd, { type = DEF_USER_OBJ; types_def |= type; - id = ILLEGAL_GID; + id = ACL_UNDEFINED_ID; saw_def_user_obj = true; } else if (ace_sid == well_known_creator_group_sid) { type = DEF_GROUP_OBJ; types_def |= type; - id = ILLEGAL_GID; + id = ACL_UNDEFINED_ID; saw_def_group_obj = true; } else @@ -888,10 +889,10 @@ get_posix_access (PSECURITY_DESCRIPTOR psd, { if (owner_eq_group && !saw_def_group_obj && attr & S_ISGID) { - /* This needs post-processing in the following GROUP_OBJ - handling... Set id to ILLEGAL_GID to play it safe. */ + /* Needs post-processing in the following GROUP_OBJ block. + Set id to ACL_UNDEFINED_ID to play it safe. */ type = GROUP_OBJ; - id = ILLEGAL_GID; + id = ACL_UNDEFINED_ID; } else type = USER; @@ -944,7 +945,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd, && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0) { lacl[pos].a_type = CLASS_OBJ; - lacl[pos].a_id = ILLEGAL_GID; + lacl[pos].a_id = ACL_UNDEFINED_ID; class_perm |= lacl[1].a_perm; lacl[pos].a_perm = class_perm; aclsid[pos] = well_known_null_sid; @@ -960,7 +961,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd, && (pos = searchace (lacl, MAX_ACL_ENTRIES, CLASS_OBJ)) >= 0) { lacl[pos].a_type = CLASS_OBJ; - lacl[pos].a_id = ILLEGAL_GID; + lacl[pos].a_id = ACL_UNDEFINED_ID; lacl[pos].a_perm = lacl[1].a_perm; /* == group perms */ aclsid[pos] = well_known_null_sid; } @@ -1004,7 +1005,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd, 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_id = ACL_UNDEFINED_ID; lacl[pos].a_perm = lacl[2].a_perm; aclsid[pos] = well_known_world_sid; pos++; @@ -1019,7 +1020,7 @@ get_posix_access (PSECURITY_DESCRIPTOR psd, && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0) { lacl[pos].a_type = DEF_CLASS_OBJ; - lacl[pos].a_id = ILLEGAL_GID; + lacl[pos].a_id = ACL_UNDEFINED_ID; lacl[pos].a_perm = def_class_perm; if (def_pgrp_pos >= 0) lacl[pos].a_perm |= lacl[def_pgrp_pos].a_perm; @@ -1175,130 +1176,151 @@ facl32 (int fd, int cmd, int nentries, aclent_t *aclbufp) return res; } -extern "C" int -aclcheck32 (aclent_t *aclbufp, int nentries, int *which) +int +__aclcheck (aclent_t *aclbufp, int nentries, int *which, bool posix) { bool has_user_obj = false; bool has_group_obj = false; bool has_other_obj = false; bool has_class_obj = false; - bool has_ug_objs __attribute__ ((unused)) = false; - bool has_def_objs __attribute__ ((unused)) = false; - bool has_def_user_obj __attribute__ ((unused)) = false; + bool has_ug_objs = false; + bool has_def_objs = false; + bool has_def_user_obj = false; bool has_def_group_obj = false; bool has_def_other_obj = false; bool has_def_class_obj = false; - bool has_def_ug_objs __attribute__ ((unused)) = false; + bool has_def_ug_objs = false; int pos2; for (int pos = 0; pos < nentries; ++pos) - switch (aclbufp[pos].a_type) - { - case USER_OBJ: - if (has_user_obj) - { - if (which) - *which = pos; - return USER_ERROR; - } - has_user_obj = true; - break; - case GROUP_OBJ: - if (has_group_obj) - { - if (which) - *which = pos; - return GRP_ERROR; - } - has_group_obj = true; - break; - case OTHER_OBJ: - if (has_other_obj) - { - if (which) - *which = pos; - return OTHER_ERROR; - } - has_other_obj = true; - break; - case CLASS_OBJ: - if (has_class_obj) - { - if (which) - *which = pos; - return CLASS_ERROR; - } - has_class_obj = true; - break; - case USER: - case GROUP: - if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1, - aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0) - { - if (which) - *which = pos2; - return DUPLICATE_ERROR; - } - has_ug_objs = true; - break; - case DEF_USER_OBJ: - if (has_def_user_obj) - { - if (which) - *which = pos; - return USER_ERROR; - } - has_def_objs = has_def_user_obj = true; - break; - case DEF_GROUP_OBJ: - if (has_def_group_obj) - { - if (which) - *which = pos; - return GRP_ERROR; - } - has_def_objs = has_def_group_obj = true; - break; - case DEF_OTHER_OBJ: - if (has_def_other_obj) - { - if (which) - *which = pos; - return OTHER_ERROR; - } - has_def_objs = has_def_other_obj = true; - break; - case DEF_CLASS_OBJ: - if (has_def_class_obj) - { - if (which) - *which = pos; - return CLASS_ERROR; - } - has_def_objs = has_def_class_obj = true; - break; - case DEF_USER: - case DEF_GROUP: - if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1, - aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0) - { - if (which) - *which = pos2; - return DUPLICATE_ERROR; - } - has_def_objs = has_def_ug_objs = true; - break; - default: - return ENTRY_ERROR; - } + { + /* POSIX ACLs may contain deleted entries. Just ignore them. */ + if (posix && aclbufp[pos].a_type == ACL_DELETED_TAG) + continue; + /* POSIX defines two sorts of ACLs, access and default, none of which + is supposed to have the ACL_DEFAULT flag set. */ + if (posix && (aclbufp[pos].a_type & ACL_DEFAULT)) + { + if (which) + *which = pos; + return ENTRY_ERROR; + } + switch (aclbufp[pos].a_type) + { + case USER_OBJ: + if (has_user_obj) + { + if (which) + *which = pos; + return USER_ERROR; + } + has_user_obj = true; + break; + case GROUP_OBJ: + if (has_group_obj) + { + if (which) + *which = pos; + return GRP_ERROR; + } + has_group_obj = true; + break; + case OTHER_OBJ: + if (has_other_obj) + { + if (which) + *which = pos; + return OTHER_ERROR; + } + has_other_obj = true; + break; + case CLASS_OBJ: + if (has_class_obj) + { + if (which) + *which = pos; + return CLASS_ERROR; + } + has_class_obj = true; + break; + case USER: + case GROUP: + if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1, + aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0) + { + if (which) + *which = pos2; + return DUPLICATE_ERROR; + } + has_ug_objs = true; + break; + case DEF_USER_OBJ: + if (has_def_user_obj) + { + if (which) + *which = pos; + return USER_ERROR; + } + has_def_objs = has_def_user_obj = true; + break; + case DEF_GROUP_OBJ: + if (has_def_group_obj) + { + if (which) + *which = pos; + return GRP_ERROR; + } + has_def_objs = has_def_group_obj = true; + break; + case DEF_OTHER_OBJ: + if (has_def_other_obj) + { + if (which) + *which = pos; + return OTHER_ERROR; + } + has_def_objs = has_def_other_obj = true; + break; + case DEF_CLASS_OBJ: + if (has_def_class_obj) + { + if (which) + *which = pos; + return CLASS_ERROR; + } + has_def_objs = has_def_class_obj = true; + break; + case DEF_USER: + case DEF_GROUP: + if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1, + aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0) + { + if (which) + *which = pos2; + return DUPLICATE_ERROR; + } + has_def_objs = has_def_ug_objs = true; + break; + default: + if (which) + *which = pos; + return ENTRY_ERROR; + } + } if (!has_user_obj || !has_group_obj || !has_other_obj - || (has_def_objs - && (!has_def_user_obj || !has_def_group_obj || !has_def_other_obj)) - || (has_ug_objs && !has_class_obj) - || (has_def_ug_objs && !has_def_class_obj) - ) + || (has_ug_objs && !has_class_obj)) + { + if (which) + *which = -1; + return MISS_ERROR; + } + /* Check for missing default entries only on Solaris ACLs. */ + if (!posix && + ((has_def_objs + && !(has_def_user_obj && has_def_group_obj && has_def_other_obj)) + || (has_def_ug_objs && !has_def_class_obj))) { if (which) *which = -1; @@ -1307,22 +1329,44 @@ aclcheck32 (aclent_t *aclbufp, int nentries, int *which) return 0; } -void +extern "C" int +aclcheck32 (aclent_t *aclbufp, int nentries, int *which) +{ + return __aclcheck (aclbufp, nentries, which, false); +} + +/* For the sake of acl_calc_mask, return -1 if the ACL doesn't need a mask + or if a mask entry already exists (__aclcalcmask sets the mask by itself). + Otherwise return the mask value so acl_calc_mask can create a mask entry. + This doesn't matter when called from aclsort. */ +mode_t __aclcalcmask (aclent_t *aclbufp, int nentries) { mode_t mask = 0; + bool need_mask = false; int mask_idx = -1; for (int idx = 0; idx < nentries; ++idx) - { - if (aclbufp[idx].a_type == CLASS_OBJ) - mask_idx = idx; - else if (aclbufp[idx].a_type - & (USER | GROUP_OBJ | GROUP)) + switch (aclbufp[idx].a_type) + { + case USER: + case GROUP: + need_mask = true; + /*FALLTHRU*/ + case GROUP_OBJ: mask |= aclbufp[idx].a_perm; - } + break; + case CLASS_OBJ: + mask_idx = idx; + break; + default: + break; + } if (mask_idx != -1) aclbufp[mask_idx].a_perm = mask; + if (need_mask && mask_idx == -1) + return mask; + return (acl_perm_t) -1; } static int @@ -1336,15 +1380,25 @@ acecmp (const void *a1, const void *a2) #undef ace } -extern "C" int -aclsort32 (int nentries, int calclass, aclent_t *aclbufp) +/* Sorts any acl. Called from sec_posixacl.cc. */ +int +__aclsort (int nentries, aclent_t *aclbufp) { - if (aclcheck32 (aclbufp, nentries, NULL)) + if (!aclbufp || nentries < 0) { set_errno (EINVAL); return -1; } - if (!aclbufp || nentries < 1) + if (nentries > 0) + qsort ((void *) aclbufp, nentries, sizeof (aclent_t), acecmp); + return 0; +} + +extern "C" int +aclsort32 (int nentries, int calclass, aclent_t *aclbufp) +{ + if (!aclbufp || nentries < MIN_ACL_ENTRIES + || aclcheck32 (aclbufp, nentries, NULL)) { set_errno (EINVAL); return -1; @@ -1444,79 +1498,224 @@ aclfrompbits32 (aclent_t *aclbufp, int nentries, mode_t *pbitsp) } static char * -permtostr (mode_t perm) +permtostr (char *bufp, mode_t perm) { - static char pbuf[4]; + *bufp++ = (perm & S_IROTH) ? 'r' : '-'; + *bufp++ = (perm & S_IWOTH) ? 'w' : '-'; + *bufp++ = (perm & S_IXOTH) ? 'x' : '-'; + return bufp; +} - pbuf[0] = (perm & S_IROTH) ? 'r' : '-'; - pbuf[1] = (perm & S_IWOTH) ? 'w' : '-'; - pbuf[2] = (perm & S_IXOTH) ? 'x' : '-'; - pbuf[3] = '\0'; - return pbuf; +#define _OPT(o) (options & (o)) + +#define _CHK(l) \ + if (bufp + (l) >= buf + 2 * NT_MAX_PATH - 1) \ + { \ + set_errno (ENOMEM); \ + return NULL; \ + } +#define _CPY(s) ({ \ + const char *_s = (s); \ + _CHK (strlen (_s)); \ + bufp = stpcpy (bufp, _s); \ + }) +#define _PTS(p) { \ + _CHK (3); \ + bufp = permtostr (bufp, p); \ + } + +#define _CMP(s) (!strncmp (bufp, acl_part[s].str, acl_part[s].len)) + +struct _acl_part +{ + const char *str; + size_t len; +}; + +static _acl_part acl_part_l[] = +{ + { "default:", 8 }, + { "user:", 5 }, + { "group:", 6 }, + { "mask:", 5 }, + { "other:", 6 } +}; + +static _acl_part acl_part_s[] = +{ + { "d:", 2 }, + { "u:", 2 }, + { "g:", 2 }, + { "m:", 2 }, + { "o:", 2 } +}; + +enum _acl_type { + default_s, + user_s, + group_s, + mask_s, + other_s, + none_s +}; + +char * +__acltotext (aclent_t *aclbufp, int aclcnt, const char *prefix, char separator, + int options) +{ + if (!aclbufp || aclcnt < 1 || aclcnt > MAX_ACL_ENTRIES + || aclsort32 (aclcnt, 0, aclbufp)) + { + set_errno (EINVAL); + return NULL; + } + cyg_ldap cldap; + tmp_pathbuf tp; + char *buf = tp.t_get (); + char *bufp = buf; + char *entry_start; + bool first = true; + struct passwd *pw; + struct group *gr; + mode_t mask = S_IRWXO; + mode_t def_mask = S_IRWXO; + mode_t effective; + int pos; + _acl_part *acl_part = _OPT (TEXT_ABBREVIATE) ? acl_part_s : acl_part_l; + + *bufp = '\0'; + /* If effective rights are requested, fetch mask values. */ + if (_OPT (TEXT_SOME_EFFECTIVE | TEXT_ALL_EFFECTIVE)) + { + if ((pos = searchace (aclbufp, aclcnt, CLASS_OBJ)) >= 0) + mask = aclbufp[pos].a_perm; + if ((pos = searchace (aclbufp, aclcnt, DEF_CLASS_OBJ)) >= 0) + def_mask = aclbufp[pos].a_perm; + } + for (pos = 0; pos < aclcnt; ++pos) + { + if (!first) + { + _CHK (1); + *bufp++ = separator; + } + first = false; + /* Rememeber start position of entry to compute TEXT_SMART_INDENT tabs. */ + entry_start = bufp; + /* prefix */ + if (prefix) + _CPY (prefix); + /* Solaris default acl? */ + if (!_OPT (TEXT_IS_POSIX) && aclbufp[pos].a_type & ACL_DEFAULT) + _CPY (acl_part[default_s].str); + /* acl type */ + switch (aclbufp[pos].a_type & ~ACL_DEFAULT) + { + case USER_OBJ: + case USER: + _CPY (acl_part[user_s].str); + break; + case GROUP_OBJ: + case GROUP: + _CPY (acl_part[group_s].str); + break; + case CLASS_OBJ: + _CPY (acl_part[mask_s].str); + break; + case OTHER_OBJ: + _CPY (acl_part[other_s].str); + break; + } + /* id, if any */ + switch (aclbufp[pos].a_type & ~ACL_DEFAULT) + { + case USER: + if (_OPT (TEXT_NUMERIC_IDS) + || !(pw = internal_getpwuid (aclbufp[pos].a_id, &cldap))) + { + _CHK (11); + bufp += __small_sprintf (bufp, "%u:", aclbufp[pos].a_id); + } + else + { + _CHK (strlen (pw->pw_name + 1)); + bufp += __small_sprintf (bufp, "%s:", pw->pw_name); + } + break; + case GROUP: + if (_OPT (TEXT_NUMERIC_IDS) + || !(gr = internal_getgrgid (aclbufp[pos].a_id, &cldap))) + { + _CHK (11); + bufp += __small_sprintf (bufp, "%u:", aclbufp[pos].a_id); + } + else + { + _CHK (strlen (gr->gr_name)); + bufp += __small_sprintf (bufp, "%s:", gr->gr_name); + } + break; + default: + _CPY (":"); + break; + } + /* real permissions */ + _PTS (aclbufp[pos].a_perm); + if (!_OPT (TEXT_SOME_EFFECTIVE | TEXT_ALL_EFFECTIVE)) + continue; + /* effective permissions */ + switch (aclbufp[pos].a_type) + { + case USER: + case GROUP_OBJ: + case GROUP: + effective = aclbufp[pos].a_perm & mask; + break; + case DEF_USER: + case DEF_GROUP_OBJ: + case DEF_GROUP: + effective = aclbufp[pos].a_perm & def_mask; + break; + default: + continue; + } + if (_OPT (TEXT_ALL_EFFECTIVE) || effective != aclbufp[pos].a_perm) + { + if (_OPT (TEXT_SMART_INDENT)) + { + int tabs = 3 - (bufp - entry_start) / 8; + if (tabs-- > 0) + { + _CHK (tabs); + while (tabs-- > 0) + *bufp++ = '\t'; + } + } + _CPY ("\t#effective:"); + _PTS (effective); + } + } + if (_OPT (TEXT_END_SEPARATOR)) + { + _CHK (1); + *bufp++ = separator; + *bufp++ = '\0'; + } + return strdup (buf); } extern "C" char * acltotext32 (aclent_t *aclbufp, int aclcnt) { - if (!aclbufp || aclcnt < 1 || aclcnt > MAX_ACL_ENTRIES - || aclcheck32 (aclbufp, aclcnt, NULL)) - { - set_errno (EINVAL); - return NULL; - } - tmp_pathbuf tp; - char *buf = tp.c_get (); - buf[0] = '\0'; - bool first = true; - - for (int pos = 0; pos < aclcnt; ++pos) - { - if (!first) - strcat (buf, ","); - first = false; - if (aclbufp[pos].a_type & ACL_DEFAULT) - strcat (buf, "default"); - switch (aclbufp[pos].a_type & ~ACL_DEFAULT) - { - case USER_OBJ: - __small_sprintf (buf + strlen (buf), "user::%s", - permtostr (aclbufp[pos].a_perm)); - break; - case USER: - __small_sprintf (buf + strlen (buf), "user:%d:%s", - aclbufp[pos].a_id, permtostr (aclbufp[pos].a_perm)); - break; - case GROUP_OBJ: - __small_sprintf (buf + strlen (buf), "group::%s", - permtostr (aclbufp[pos].a_perm)); - break; - case GROUP: - __small_sprintf (buf + strlen (buf), "group:%d:%s", - aclbufp[pos].a_id, permtostr (aclbufp[pos].a_perm)); - break; - case CLASS_OBJ: - __small_sprintf (buf + strlen (buf), "mask::%s", - permtostr (aclbufp[pos].a_perm)); - break; - case OTHER_OBJ: - __small_sprintf (buf + strlen (buf), "other::%s", - permtostr (aclbufp[pos].a_perm)); - break; - default: - set_errno (EINVAL); - return NULL; - } - } - return strdup (buf); + return __acltotext (aclbufp, aclcnt, NULL, ',', 0); } static mode_t -permfromstr (char *perm) +permfromstr (char *perm, bool posix_long) { mode_t mode = 0; + int idx; - if (strlen (perm) != 3) - return 01000; if (perm[0] == 'r') mode |= S_IROTH; else if (perm[0] != '-') @@ -1529,125 +1728,170 @@ permfromstr (char *perm) mode |= S_IXOTH; else if (perm[2] != '-') return 01000; - return mode; + idx = 3; + /* In posix long mode, only tabs up to a hash sign allowed. */ + if (posix_long) + while (perm[idx] == '\t') + ++idx; + if (perm[idx] == '\0' || (posix_long && perm[idx] == '#')) + return mode; + return 01000; } -extern "C" aclent_t * -aclfromtext32 (const char *acltextp, int *aclcnt) +void * +__aclfromtext (const char *acltextp, int *aclcnt, bool posix) { - if (!acltextp || strlen (acltextp) > NT_MAX_PATH) + if (!acltextp || strlen (acltextp) >= 2 * NT_MAX_PATH) { set_errno (EINVAL); return NULL; } + cyg_ldap cldap; tmp_pathbuf tp; - aclent_t lacl[MAX_ACL_ENTRIES]; - memset (lacl, 0, sizeof lacl); + const char *delim; + _acl_part *acl_part; + char *bufp, *lasts, *qualifier; int pos = 0; + int acl_type; + + aclent_t *lacl = (aclent_t *) tp.c_get (); + memset (lacl, 0, MAX_ACL_ENTRIES * sizeof (aclent_t *)); char *buf = tp.t_get (); stpcpy (buf, acltextp); - char *lasts; - cyg_ldap cldap; - for (char *c = strtok_r (buf, ",", &lasts); - c; - c = strtok_r (NULL, ",", &lasts)) + + if (posix) { - if (!strncmp (c, "default", 7)) + /* Posix long or short form. Any \n in the string means long form. */ + if (strchr (buf, '\n')) + { + delim = "\n"; + acl_part = acl_part_l; + } + else + { + delim = ","; + acl_part = acl_part_s; + } + } + else + { + /* Solaris aclfromtext format. */ + delim = ","; + acl_part = acl_part_l; + } + + for (bufp = strtok_r (buf, delim, &lasts); + bufp; + bufp = strtok_r (NULL, delim, &lasts)) + { + /* Handle default acl entries only for Solaris ACLs. */ + if (!posix && _CMP (default_s)) { lacl[pos].a_type |= ACL_DEFAULT; - c += 7; + bufp += acl_part[default_s].len; } - if (!strncmp (c, "user:", 5)) + lacl[pos].a_id = ACL_UNDEFINED_ID; + for (acl_type = user_s; acl_type < none_s; ++acl_type) + if (_CMP (acl_type)) + break; + if (acl_type == none_s) { - if (c[5] == ':') - lacl[pos].a_type |= USER_OBJ; - else + set_errno (EINVAL); + return NULL; + } + bufp += acl_part[acl_type].len; + switch (acl_type) + { + case user_s: + case group_s: + qualifier = bufp; + bufp = strchrnul (bufp, ':'); + *bufp++ = '\0'; + /* No qualifier? USER_OBJ or GROUP_OBJ */ + if (!*qualifier) { - lacl[pos].a_type |= USER; - c += 5; - if (isalpha (*c)) + lacl[pos].a_type |= (acl_type == user_s) ? USER_OBJ : GROUP_OBJ; + break; + } + /* Some qualifier, USER or GROUP */ + lacl[pos].a_type |= (acl_type == user_s) ? USER : GROUP; + if (isdigit (*qualifier)) + { + char *ep; + + id_t id = strtol (qualifier, &ep, 10); + if (*ep == '\0') { - struct passwd *pw = internal_getpwnam (c, &cldap); - if (!pw) - { - set_errno (EINVAL); - return NULL; - } - lacl[pos].a_id = pw->pw_uid; - c = strchrnul (c, ':'); - } - else if (isdigit (*c)) - lacl[pos].a_id = strtol (c, &c, 10); - if (*c != ':') - { - set_errno (EINVAL); - return NULL; + lacl[pos].a_id = id; + break; } } - } - else if (!strncmp (c, "group:", 6)) - { - if (c[5] == ':') - lacl[pos].a_type |= GROUP_OBJ; + if (acl_type == user_s) + { + struct passwd *pw = internal_getpwnam (qualifier, &cldap); + if (pw) + lacl[pos].a_id = pw->pw_uid; + } else { - lacl[pos].a_type |= GROUP; - c += 5; - if (isalpha (*c)) - { - struct group *gr = internal_getgrnam (c, &cldap); - if (!gr) - { - set_errno (EINVAL); - return NULL; - } - lacl[pos].a_id = gr->gr_gid; - c = strchrnul (c, ':'); - } - else if (isdigit (*c)) - lacl[pos].a_id = strtol (c, &c, 10); - if (*c != ':') - { - set_errno (EINVAL); - return NULL; - } + struct group *gr = internal_getgrnam (qualifier, &cldap); + if (gr) + lacl[pos].a_id = gr->gr_gid; } - } - else if (!strncmp (c, "mask:", 5)) - { - if (c[5] == ':') - lacl[pos].a_type |= CLASS_OBJ; - else + if (lacl[pos].a_id == ACL_UNDEFINED_ID) { set_errno (EINVAL); return NULL; } - } - else if (!strncmp (c, "other:", 6)) - { - if (c[5] == ':') - lacl[pos].a_type |= OTHER_OBJ; - else + break; + case mask_s: + case other_s: + if (*bufp++ != ':') { set_errno (EINVAL); return NULL; } + lacl[pos].a_type |= (acl_type == mask_s) ? CLASS_OBJ : OTHER_OBJ; + break; } - if ((lacl[pos].a_perm = permfromstr (c)) == 01000) + /* In posix long mode, the next char after the permissions may be a tab + followed by effective permissions we can ignore here. */ + if ((lacl[pos].a_perm = permfromstr (bufp, *delim == '\n')) == 01000) { set_errno (EINVAL); return NULL; } ++pos; } - aclent_t *aclp = (aclent_t *) malloc (pos * sizeof (aclent_t)); - if (aclp) + if (posix) { - memcpy (aclp, lacl, pos * sizeof (aclent_t)); - if (aclcnt) - *aclcnt = pos; + acl_t acl = (acl_t) acl_init (pos); + if (acl) + { + memcpy (acl->entry, lacl, pos * sizeof (aclent_t)); + acl->count = pos; + if (aclcnt) + *aclcnt = pos; + } + return (void *) acl; } - return aclp; + else + { + aclent_t *aclp = (aclent_t *) malloc (pos * sizeof (aclent_t)); + if (aclp) + { + memcpy (aclp, lacl, pos * sizeof (aclent_t)); + if (aclcnt) + *aclcnt = pos; + } + return (void *) aclp; + } +} + +extern "C" aclent_t * +aclfromtext32 (char *acltextp, int *aclcnt) +{ + return (aclent_t *) __aclfromtext (acltextp, aclcnt, false); } #ifdef __x86_64__ diff --git a/winsup/cygwin/sec_helper.cc b/winsup/cygwin/sec_helper.cc index af3307eb4..0c37ad2b7 100644 --- a/winsup/cygwin/sec_helper.cc +++ b/winsup/cygwin/sec_helper.cc @@ -13,7 +13,7 @@ details. */ #include "winsup.h" #include -#include +#include #include #include #include diff --git a/winsup/cygwin/sec_posixacl.cc b/winsup/cygwin/sec_posixacl.cc new file mode 100644 index 000000000..54bac8f69 --- /dev/null +++ b/winsup/cygwin/sec_posixacl.cc @@ -0,0 +1,1052 @@ +/* sec_posixacl.cc: POSIX ACL functions based on Solaris ACLs. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "winsup.h" +#include +#include "cygerrno.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "cygheap.h" +#include "tls_pbuf.h" +#include "sec_posixacl.h" +#include + +#define _ENTRY_SIZE(_cnt) ((_cnt) * sizeof (aclent_t)) +#define _ACL_SIZE(_cnt) (sizeof (__acl_ext_t) + _ENTRY_SIZE (_cnt)) +#define ACL_SIZE(_acl) ({ acl_t __acl = _acl; \ + _ACL_SIZE((__acl)->count - (__acl)->deleted); \ + }) +#define ACL_PERM_MASK (ACL_READ | ACL_WRITE | ACL_EXECUTE) + +extern "C" acl_t +acl_init (int count) +{ + acl_t acl; + + if (count < 0 || count > UINT16_MAX) + { + set_errno (EINVAL); + return NULL; + } + acl = (acl_t) calloc (1, sizeof (__acl_t)); + if (!acl) + return NULL; + acl->entry = (aclent_t *) calloc (count, sizeof (aclent_t)); + if (!acl->entry) + { + free (acl); + return NULL; + } + acl->magic = ACL_MAGIC; + acl->max_count = count; + return acl; +} + +static acl_t +__acl_dup (acl_t acl, int max) +{ + __try + { + acl_t new_acl = acl_init (max); + if (new_acl) + { + int new_idx = 0; + + for (uint16_t idx = 0; idx < acl->count; ++idx) + if (acl->entry[idx].a_type != ACL_DELETED_TAG) + new_acl->entry[new_idx++] = acl->entry[idx]; + new_acl->magic = ACL_MAGIC; + new_acl->count = new_idx; + new_acl->max_count = max; + return new_acl; + } + } + __except (EINVAL) {} + __endtry + return NULL; +} + +extern "C" acl_t +acl_dup (acl_t acl) +{ + return __acl_dup (acl, acl->max_count); +} + +extern "C" int +acl_free (void *obj_p) +{ + __try + { + acl_t acl; + + if (obj_p) + { + if (malloc_usable_size (obj_p) >= sizeof (__acl_t)) + { + acl = (acl_t) obj_p; + if (acl->magic == ACL_MAGIC) + free (acl->entry); + } + free (obj_p); + return 0; + } + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_valid (acl_t acl) +{ + __try + { + if (!(__aclcheck (acl->entry, acl->count, NULL, true))) + return 0; + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_copy_entry (acl_entry_t dest_d, acl_entry_t src_d) +{ + __try + { + uint16_t d_idx, s_idx; + acl_t d_acl, s_acl; + + d_acl = __from_entry (dest_d, d_idx); + s_acl = __from_entry (src_d, s_idx); + if (d_acl && s_acl) + { + d_acl->entry[d_idx] = s_acl->entry[s_idx]; + return 0; + } + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_create_entry (acl_t *acl_p, acl_entry_t *entry_p) +{ + __try + { + acl_t acl = *acl_p; + uint16_t idx; + + if (acl->deleted > 0) + { + for (idx = 0; idx < acl->count; ++idx) + if (acl->entry[idx].a_type == ACL_DELETED_TAG) + { + *entry_p = __to_entry (acl, idx); + --acl->deleted; + goto fill_entry; + } + } + if (acl->count >= acl->max_count) + { + acl_t new_acl = __acl_dup (acl, acl->count + 1); + if (!new_acl) + __leave; + *acl_p = new_acl; + acl_free (acl); + acl = *acl_p; + } + idx = acl->count++; + *entry_p = __to_entry (acl, idx); + fill_entry: + acl->entry[idx].a_type = ACL_UNDEFINED_TAG; + acl->entry[idx].a_id = ACL_UNDEFINED_ID; + acl->entry[idx].a_perm = 0; + return 0; + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_delete_entry (acl_t acl, acl_entry_t entry_d) +{ + __try + { + acl_t acl_p; + uint16_t idx; + + acl_p = __from_entry (entry_d, idx); + + if (acl_p == acl) + { + acl_p->entry[idx].a_type = ACL_DELETED_TAG; + acl_p->entry[idx].a_id = ACL_UNDEFINED_ID; + acl_p->entry[idx].a_perm = 0; + return 0; + } + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_get_entry (acl_t acl, int entry_id, acl_entry_t *entry_p) +{ + __try + { + uint16_t idx; + + if (entry_id == ACL_FIRST_ENTRY) + acl->next = 0; + else if (entry_id != ACL_NEXT_ENTRY) + { + set_errno (EINVAL); + __leave; + } + do + { + if (acl->next >= acl->count) + return 0; + idx = acl->next++; + } + while (acl->entry[idx].a_type == ACL_DELETED_TAG); + *entry_p = __to_entry (acl, idx); + return 1; + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_calc_mask (acl_t *acl_p) +{ + __try + { + acl_t acl = *acl_p; + mode_t mask = 0; + + mask = __aclcalcmask (acl->entry, acl->count); + /* If __aclcalcmask returns -1 we're done. Otherwise create a + mask entry here. */ + if (mask != (acl_perm_t) -1) + { + acl_entry_t entry_d; + uint16_t mask_idx; + + if (acl_create_entry (&acl, &entry_d) < 0) + __leave; + if (!__from_entry (entry_d, mask_idx)) + { + set_errno (EINVAL); + __leave; + } + acl->entry[mask_idx].a_type = ACL_MASK; + acl->entry[mask_idx].a_id = ACL_UNDEFINED_ID; + acl->entry[mask_idx].a_perm = mask; + *acl_p = acl; + } + return 0; + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_clear_perms (acl_permset_t permset_d) +{ + __try + { + acl_t acl; + uint16_t idx; + + acl = __from_permset (permset_d, idx); + if (acl) + { + acl->entry[idx].a_perm = 0; + return 0; + } + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_add_perm (acl_permset_t permset_d, acl_perm_t perm) +{ + __try + { + acl_t acl; + uint16_t idx; + + acl = __from_permset (permset_d, idx); + if (acl && !(perm & ~ACL_PERM_MASK)) + { + acl->entry[idx].a_perm |= perm; + return 0; + } + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_delete_perm (acl_permset_t permset_d, acl_perm_t perm) +{ + __try + { + acl_t acl; + uint16_t idx; + + acl = __from_permset (permset_d, idx); + if (acl && !(perm & ~ACL_PERM_MASK)) + { + acl->entry[idx].a_perm &= ~perm; + return 0; + } + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_get_permset (acl_entry_t entry_d, acl_permset_t *permset_p) +{ + __try + { + acl_t acl; + uint16_t idx; + + acl = __from_entry (entry_d, idx); + if (acl) + { + *permset_p = (acl_permset_t) entry_d; + return 0; + } + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_set_permset (acl_entry_t entry_d, acl_permset_t permset_d) +{ + __try + { + acl_t acl_e, acl_p; + uint16_t idx_e, idx_p; + + acl_e = __from_entry (entry_d, idx_e); + acl_p = __from_permset (permset_d, idx_p); + if (acl_e && acl_p && !(acl_p->entry[idx_p].a_perm & ~ACL_PERM_MASK)) + { + acl_e->entry[idx_e].a_perm = acl_p->entry[idx_p].a_perm; + return 0; + } + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" void * +acl_get_qualifier (acl_entry_t entry_d) +{ + __try + { + acl_t acl; + uint16_t idx; + + acl = __from_entry (entry_d, idx); + if (acl && (acl->entry[idx].a_type & (ACL_USER | ACL_GROUP))) + { + id_t *id = (id_t *) malloc (sizeof (id_t)); + if (id) + { + *id = acl->entry[idx].a_id; + return (void *) id; + } + } + else + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return NULL; +} + +extern "C" int +acl_set_qualifier (acl_entry_t entry_d, const void *qualifier_p) +{ + __try + { + acl_t acl; + uint16_t idx; + + acl = __from_entry (entry_d, idx); + if (acl && (acl->entry[idx].a_type & (ACL_USER | ACL_GROUP))) + { + acl->entry[idx].a_id = *(id_t *) qualifier_p; + return 0; + } + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_get_tag_type (acl_entry_t entry_d, acl_tag_t *tag_type_p) +{ + __try + { + acl_t acl; + uint16_t idx; + + acl = __from_entry (entry_d, idx); + if (acl) + { + *tag_type_p = acl->entry[idx].a_type; + return 0; + } + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_set_tag_type (acl_entry_t entry_d, acl_tag_t tag_type) +{ + __try + { + acl_t acl; + uint16_t idx; + + acl = __from_entry (entry_d, idx); + if (acl) + switch (tag_type) + { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + acl->entry[idx].a_id = ACL_UNDEFINED_ID; + /*FALLTHRU*/ + case ACL_USER: + case ACL_GROUP: + acl->entry[idx].a_type = tag_type; + return 0; + default: + break; + } + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" ssize_t +acl_size (acl_t acl) +{ + __try + { + return (ssize_t) ACL_SIZE (acl); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" ssize_t +acl_copy_ext (void *buf_p, acl_t acl, ssize_t size) +{ + __try + { + ssize_t ext_size = (ssize_t) ACL_SIZE (acl); + + if (size <= 0) + set_errno (EINVAL); + else if (ext_size > size) + set_errno (ERANGE); + else + { + uint16_t ext_idx = 0; + __acl_ext_t *acl_ext = (__acl_ext_t *) buf_p; + + acl_ext->count = acl->count - acl->deleted; + for (uint16_t idx = 0; idx < acl->count; ++idx) + if (acl->entry[idx].a_type != ACL_DELETED_TAG) + acl_ext->entry[ext_idx++] = acl->entry[idx]; + return ext_size; + } + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" acl_t +acl_copy_int (const void *buf_p) +{ + __try + { + acl_t acl; + __acl_ext_t *acl_ext = (__acl_ext_t *) buf_p; + + acl = acl_init (acl_ext->count); + if (acl) + { + memcpy (acl->entry, acl_ext->entry, _ENTRY_SIZE (acl_ext->count)); + acl->count = acl_ext->count; + return acl; + } + } + __except (EINVAL) {} + __endtry + return NULL; +} + +extern "C" acl_t +acl_from_text (const char *buf_p) +{ + __try + { + return (acl_t) __aclfromtext (buf_p, NULL, true); + } + __except (EINVAL) {} + __endtry + return NULL; +} + +extern "C" char * +acl_to_text (acl_t acl, ssize_t *len_p) +{ + __try + { + char *ret = __acltotext (acl->entry, acl->count, NULL, '\n', + TEXT_IS_POSIX + | TEXT_SOME_EFFECTIVE + | TEXT_END_SEPARATOR); + if (ret && len_p) + *len_p = strlen (ret); + return ret; + } + __except (EINVAL) {} + __endtry + return NULL; +} + +acl_t __reg2 +fhandler_base::acl_get (acl_type_t type) +{ + set_errno (ENOTSUP); + return NULL; +} + +acl_t __reg2 +fhandler_disk_file::acl_get (acl_type_t type) +{ + acl_t acl = NULL; + int oret = 0; + + __try + { + tmp_pathbuf tp; + aclent_t *aclbufp; + uint16_t cnt, access_cnt; + + if (!pc.has_acls ()) + { + set_errno (ENOTSUP); + __leave; + } + if (type == ACL_TYPE_DEFAULT && !pc.isdir ()) + { + set_errno (ENOTDIR); + __leave; + } + aclbufp = (aclent_t *) tp.c_get (); + if (!get_handle ()) + { + query_open (query_read_control); + if (!(oret = open (O_BINARY, 0))) + __leave; + } + cnt = facl (GETACL, MAX_ACL_ENTRIES, aclbufp); + if (cnt < 0) + __leave; + /* Set access_cnt to number of non-default entries from file ACL. */ + if (!pc.isdir ()) + access_cnt = cnt; + else + for (access_cnt = 0; access_cnt < cnt; ++access_cnt) + if (aclbufp[access_cnt].a_type & ACL_DEFAULT) + break; + if (type == ACL_TYPE_ACCESS) + { + acl = acl_init (access_cnt); + if (!acl) + __leave; + memcpy (acl->entry, aclbufp, _ENTRY_SIZE (access_cnt)); + acl->count = access_cnt; + } + else + { + cnt -= access_cnt; + acl = acl_init (cnt); + if (acl && cnt) + { + memcpy (acl->entry, aclbufp + access_cnt, _ENTRY_SIZE (cnt)); + acl->count = cnt; + for (cnt = 0; cnt < acl->count; ++cnt) + acl->entry[cnt].a_type &= ~ACL_DEFAULT; + } + } + } + __except (EINVAL) {} + __endtry + if (oret) + close_fs (); + return acl; +} + +extern "C" acl_t +acl_get_fd (int fd) +{ + cygheap_fdget cfd (fd); + if (cfd < 0) + return NULL; + return cfd->acl_get (ACL_TYPE_ACCESS); +} + +extern "C" acl_t +acl_get_file (const char *path_p, acl_type_t type) +{ + if (type != ACL_TYPE_ACCESS && type != ACL_TYPE_DEFAULT) + { + set_errno (EINVAL); + return NULL; + } + fhandler_base *fh; + if (!(fh = build_fh_name (path_p, PC_SYM_FOLLOW, stat_suffixes))) + return NULL; + if (fh->error ()) + { + set_errno (fh->error ()); + return NULL; + } + acl_t acl = fh->acl_get (type); + delete fh; + return acl; +} + +int __reg3 +fhandler_base::acl_set (acl_t acl, acl_type_t type) +{ + set_errno (ENOTSUP); + return -1; +} + +int __reg3 +fhandler_disk_file::acl_set (acl_t acl, acl_type_t type) +{ + int ret = -1; + int oret = 0; + + __try + { + tmp_pathbuf tp; + aclent_t *aclbufp, *aclbuf_from_file; + uint16_t cnt, cnt_from_file, access_cnt; + + if (!pc.has_acls ()) + { + set_errno (ENOTSUP); + __leave; + } + if (type == ACL_TYPE_DEFAULT && !pc.isdir ()) + { + set_errno (ENOTDIR); + __leave; + } + if (acl->count > MAX_ACL_ENTRIES) + { + set_errno (EINVAL); + __leave; + } + aclbuf_from_file = (aclent_t *) tp.c_get (); + if (!get_handle ()) + { + query_open (query_write_dac); + if (!(oret = open (O_BINARY, 0))) + __leave; + } + cnt_from_file = facl (GETACL, MAX_ACL_ENTRIES, aclbuf_from_file); + if (cnt_from_file < 0) + __leave; + aclbufp = (aclent_t *) tp.c_get (); + /* Set access_cnt to number of non-default entries from file ACL. */ + if (!pc.isdir ()) + access_cnt = cnt_from_file; + else + for (access_cnt = 0; access_cnt < cnt_from_file; ++access_cnt) + if (aclbuf_from_file[access_cnt].a_type & ACL_DEFAULT) + break; + if (type == ACL_TYPE_ACCESS) + { + /* Check if the number of ACEs fits into the buffer. */ + if (acl->count - acl->deleted + cnt_from_file - access_cnt + > MAX_ACL_ENTRIES) + { + set_errno (EINVAL); + __leave; + } + /* Copy the new ACL entries. */ + cnt = 0; + for (uint16_t idx = 0; idx < acl->count; ++idx) + if (acl->entry[idx].a_type != ACL_DELETED_TAG) + aclbufp[cnt++] = acl->entry[idx]; + /* Append default ACL from file, if any. */ + if (access_cnt < cnt_from_file) + { + memcpy (aclbufp + cnt, aclbuf_from_file + access_cnt, + _ENTRY_SIZE (cnt_from_file - access_cnt)); + cnt += cnt_from_file - access_cnt; + } + } + else + { + /* Check if the number of ACEs fits into the buffer. */ + if (acl->count - acl->deleted + access_cnt > MAX_ACL_ENTRIES) + { + set_errno (EINVAL); + __leave; + } + /* Copy non-default entries from file. */ + memcpy (aclbufp, aclbuf_from_file, _ENTRY_SIZE (access_cnt)); + cnt = access_cnt; + /* Append new default ACL entries (and add ACL_DEFAULT flag). */ + for (uint16_t idx = 0; idx < acl->count; ++idx) + if (acl->entry[idx].a_type != ACL_DELETED_TAG) + { + aclbufp[cnt] = acl->entry[idx]; + aclbufp[cnt++].a_type |= ACL_DEFAULT; + } + } + ret = facl (SETACL, cnt, aclbufp); + } + __except (EINVAL) {} + __endtry + if (oret) + close_fs (); + return ret; +} + +extern "C" int +acl_set_fd (int fd, acl_t acl) +{ + cygheap_fdget cfd (fd); + if (cfd < 0) + return -1; + return cfd->acl_set (acl, ACL_TYPE_ACCESS); +} + +extern "C" int +acl_set_file(const char *path_p, acl_type_t type, acl_t acl) +{ + if (type != ACL_TYPE_ACCESS && type != ACL_TYPE_DEFAULT) + { + set_errno (EINVAL); + return -1; + } + fhandler_base *fh; + if (!(fh = build_fh_name (path_p, PC_SYM_FOLLOW, stat_suffixes))) + return -1; + if (fh->error ()) + { + set_errno (fh->error ()); + return -1; + } + int ret = fh->acl_set (acl, type); + delete fh; + return ret; +} + +extern "C" int +acl_delete_def_file (const char *path_p) +{ + acl_t acl = (acl_t) alloca (sizeof (struct __acl_t)); + acl->count = acl->max_count = acl->next = 0; + if (!acl) + return -1; + return acl_set_file(path_p, ACL_TYPE_DEFAULT, acl); +} + +/* libacl extensions */ + +extern "C" int +acl_check (acl_t acl, int *last) +{ + + __try + { + int ret = 0; + + if (acl->count != 0) + { + ret = __aclcheck (acl->entry, acl->count, last, true); + switch (ret) + { + case GRP_ERROR: + case USER_ERROR: + case CLASS_ERROR: + case OTHER_ERROR: + ret = ACL_MULTI_ERROR; + break; + default: + break; + } + } + return ret; + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_cmp (acl_t acl1, acl_t acl2) +{ + int ret = -1; + + __try + { + tmp_pathbuf tp; + + __acl_ext_t *acl1d = (__acl_ext_t *) tp.c_get (); + __acl_ext_t *acl2d = (__acl_ext_t *) tp.c_get (); + if (acl_copy_ext (acl1d, acl1, NT_MAX_PATH) < 0) + __leave; + if (acl_copy_ext (acl2d, acl2, NT_MAX_PATH) < 0) + __leave; + if (acl1d->count != acl2d->count) + return 1; + if (__aclsort (acl1d->count, acl1d->entry)) + __leave; + if (__aclsort (acl2d->count, acl2d->entry)) + __leave; + for (int idx = 0; idx < acl1d->count; ++idx) + { + if (acl1d->entry[idx].a_type != acl2d->entry[idx].a_type) + { + ret = 1; + __leave; + } + if ((acl1d->entry[idx].a_perm & ACL_PERM_MASK) + != (acl2d->entry[idx].a_perm & ACL_PERM_MASK)) + { + ret = 1; + __leave; + } + if ((acl1d->entry[idx].a_type & (ACL_USER | ACL_GROUP)) + && acl1d->entry[idx].a_id != acl2d->entry[idx].a_id) + { + ret = 1; + __leave; + } + } + ret = 0; + } + __except (EINVAL) {} + __endtry + return ret; +} + +extern "C" int +acl_entries (acl_t acl) +{ + __try + { + return acl->count - acl->deleted; + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" int +acl_equiv_mode (acl_t acl, mode_t *mode_p) +{ + __try + { + if (acl->count != 3) + { + set_errno (EINVAL); + __leave; + } + int u_idx = -1, g_idx = -1, o_idx = -1; + for (int idx = 0; idx < 3; ++idx) + switch (acl->entry[idx].a_type) + { + case ACL_USER_OBJ: + u_idx = idx; + break; + case ACL_GROUP_OBJ: + g_idx = idx; + break; + case ACL_OTHER: + o_idx = idx; + break; + } + if (u_idx == -1 || g_idx == -1 || o_idx == -1) + { + set_errno (EINVAL); + __leave; + } + if (mode_p) + *mode_p = ((acl->entry[u_idx].a_perm & ACL_PERM_MASK) << 6) + | ((acl->entry[g_idx].a_perm & ACL_PERM_MASK) << 3) + | (acl->entry[o_idx].a_perm & ACL_PERM_MASK); + return 0; + } + __except (EINVAL) {} + __endtry + return -1; +} + +static const char *acl_err_txt[] = +{ + "Multiple entries", + "Duplicate entries", + "Invalid entry type", + "Missing or wrong entry" +}; + +extern "C" const char * +acl_error (int code) +{ + if (code < ACL_MULTI_ERROR || code > ACL_MISS_ERROR) + return NULL; + return acl_err_txt[code - ACL_MULTI_ERROR]; +} + +extern "C" int +acl_extended_fd (int fd) +{ + __try + { + cygheap_fdget cfd (fd); + if (cfd < 0) + __leave; + if (!cfd->pc.has_acls ()) + { + set_errno (ENOTSUP); + __leave; + } + return cfd->facl (GETACLCNT, 0, NULL); + } + __except (EBADF) {} + __endtry + return -1; +} + +static int +__acl_extended_file (const char *path_p, mode_t follow) +{ + int fd = open (path_p, O_RDONLY | O_CLOEXEC | follow); + if (fd < 0) + return -1; + int ret = acl_extended_fd (fd); + close (fd); + return ret; +} + +extern "C" int +acl_extended_file (const char *path_p) +{ + return __acl_extended_file (path_p, 0); +} + +extern "C" int +acl_extended_file_nofollow (const char *path_p) +{ + return __acl_extended_file (path_p, O_NOFOLLOW); +} + +extern "C" acl_t +acl_from_mode (mode_t mode) +{ + acl_t acl = acl_init (MIN_ACL_ENTRIES); + if (!acl) + return NULL; + acl->count = 3; + acl->entry[0].a_type = USER_OBJ; + acl->entry[0].a_id = ACL_UNDEFINED_ID; + acl->entry[0].a_perm = (mode >> 6) & ACL_PERM_MASK; + acl->entry[1].a_type = GROUP_OBJ; + acl->entry[1].a_id = ACL_UNDEFINED_ID; + acl->entry[1].a_perm = (mode >> 3) & ACL_PERM_MASK; + acl->entry[2].a_type = OTHER_OBJ; + acl->entry[2].a_id = ACL_UNDEFINED_ID; + acl->entry[2].a_perm = mode & ACL_PERM_MASK; + return acl; +} + +extern "C" int +acl_get_perm (acl_permset_t permset_d, acl_perm_t perm) +{ + __try + { + acl_t acl; + uint16_t idx; + + acl = __from_permset (permset_d, idx); + if (acl && !(perm & ~ACL_PERM_MASK)) + return (~acl->entry[idx].a_perm & perm) ? 0 : 1; + set_errno (EINVAL); + } + __except (EINVAL) {} + __endtry + return -1; +} + +extern "C" char * +acl_to_any_text (acl_t acl, const char *prefix, char separator, int options) +{ + __try + { + return __acltotext (acl->entry, acl->count, prefix, separator, + TEXT_IS_POSIX | options); + } + __except (EINVAL) {} + __endtry + return NULL; +} diff --git a/winsup/cygwin/sec_posixacl.h b/winsup/cygwin/sec_posixacl.h new file mode 100644 index 000000000..a3790a52b --- /dev/null +++ b/winsup/cygwin/sec_posixacl.h @@ -0,0 +1,68 @@ +/* sec_posixacl.h: Internal definitions for POSIX ACLs. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include +#include +#include + +/* Magic marker for acl_t. */ +#define ACL_MAGIC (0xacdccdcadcaccacdULL) + +/* Only used internally as a_type for deleted entries. */ +#define ACL_DELETED_TAG (0xffff) + +/* Only used internally from acl_to_text/acl_to_any_text. */ +#define TEXT_END_SEPARATOR (0x1000) +#define TEXT_IS_POSIX (0x2000) + +/* Internal ACL representation. */ +struct __acl_t +{ + uint64_t magic; /* Must be ACL_MAGIC. */ + uint16_t max_count; /* Max. number of entries. */ + uint16_t count; /* Number of used entries. */ + uint16_t deleted; /* Number of used but deleted entries. */ + uint16_t next; /* Next entry to be returned by acl_get_entry. */ + aclent_t *entry; /* Pointer to variable array of ACL entries. */ +}; + +inline acl_entry_t +__to_entry (acl_t acl, uint16_t idx) +{ + return ((uint64_t) idx << 48) | (uint64_t) acl; +} +#define __to_permset(a,i) ((acl_permset_t)__to_entry((a),(i))) + +inline acl_t +__from_entry (acl_entry_t entry_d, uint16_t &idx) +{ + idx = entry_d >> 48; + acl_t acl = (acl_t) (entry_d & ~((uint64_t) 0xffff << 48)); + if (acl->magic != ACL_MAGIC) + return NULL; + if (idx >= acl->count) + return NULL; + if (acl->entry[idx].a_type == ACL_DELETED_TAG) + return NULL; + return acl; +} +#define __from_permset(p,i) __from_entry((acl_permset_t)(p),(i)) + +/* External (but opaque) ACL representation. */ +struct __acl_ext_t +{ + uint16_t count; /* Number of used entries. */ + aclent_t entry[0]; /* Variable array of ACL entries. */ +}; + +/* Shared functions defined in sec_acl.cc. */ +mode_t __aclcalcmask (aclent_t *, int); +int __aclsort (int, aclent_t *); +int __aclcheck (aclent_t *, int, int *, bool); +char *__acltotext (aclent_t *, int, const char *, char, int); +void *__aclfromtext (const char *, int *, bool); diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc index 4e02bca2e..b7330717a 100644 --- a/winsup/cygwin/security.cc +++ b/winsup/cygwin/security.cc @@ -15,7 +15,7 @@ details. */ #include "winsup.h" #include #include -#include +#include #include "cygerrno.h" #include "security.h" #include "path.h" diff --git a/winsup/utils/getfacl.c b/winsup/utils/getfacl.c index 45e5e2090..b3a3b9ac7 100644 --- a/winsup/utils/getfacl.c +++ b/winsup/utils/getfacl.c @@ -15,8 +15,7 @@ details. */ #include #include #include -#include -#include +#include #include #include #include diff --git a/winsup/utils/setfacl.c b/winsup/utils/setfacl.c index cd0edc5e3..ea9447ca6 100644 --- a/winsup/utils/setfacl.c +++ b/winsup/utils/setfacl.c @@ -20,8 +20,7 @@ details. */ #include #include #include -#include -#include +#include #include #ifndef BOOL