aboutsummaryrefslogtreecommitdiff
path: root/winsup/cygwin/security.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/security.cc')
-rw-r--r--winsup/cygwin/security.cc2084
1 files changed, 2084 insertions, 0 deletions
diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc
new file mode 100644
index 0000000..df62f1a
--- /dev/null
+++ b/winsup/cygwin/security.cc
@@ -0,0 +1,2084 @@
+/* security.cc: NT security functions
+
+ Copyright 1997, 1998, 1999, 2000 Cygnus Solutions.
+
+ Originaly written by Gunther Ebert, gunther.ebert@ixos-leipzig.de
+ Extensions by Corinna Vinschen <corinna.vinschen@cityweb.de>
+
+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 <grp.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/acl.h>
+#include "winsup.h"
+#include <ctype.h>
+
+#define MAX_SID_LEN 40
+
+extern BOOL allow_ntea;
+BOOL allow_ntsec = FALSE;
+
+SID_IDENTIFIER_AUTHORITY sid_auth[] = {
+ {SECURITY_NULL_SID_AUTHORITY},
+ {SECURITY_WORLD_SID_AUTHORITY},
+ {SECURITY_LOCAL_SID_AUTHORITY},
+ {SECURITY_CREATOR_SID_AUTHORITY},
+ {SECURITY_NON_UNIQUE_AUTHORITY},
+ {SECURITY_NT_AUTHORITY}
+};
+
+#define DONT_INHERIT (0)
+#define INHERIT_ALL (CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE)
+#define INHERIT_ONLY (INHERIT_ONLY_ACE|CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE)
+
+PSID
+get_sid (PSID psid, DWORD s, DWORD cnt, DWORD *r)
+{
+ DWORD i;
+
+ if (! psid || s > 5 || cnt < 1 || cnt > 8)
+ return NULL;
+
+ InitializeSid(psid, &sid_auth[s], cnt);
+ for (i = 0; i < cnt; ++i)
+ memcpy ((char *) psid + 8 + sizeof (DWORD) * i, &r[i], sizeof (DWORD));
+ return psid;
+}
+
+PSID
+get_ssid (PSID psid, const char *sid_str)
+{
+ char sid_buf[256];
+ char *t;
+ DWORD cnt = 0;
+ DWORD s = 0;
+ DWORD i, r[8];
+
+ if (! sid_str || strncmp (sid_str, "S-1-", 4))
+ return NULL;
+
+ strcpy (sid_buf, sid_str);
+
+ for (t = sid_buf + 4, i = 0; cnt < 8 && (t = strtok (t, "-")); t = NULL, ++i)
+ if (i == 0)
+ s = strtoul (t, NULL, 10);
+ else
+ r[cnt++] = strtoul (t, NULL, 10);
+
+ return get_sid (psid, s, cnt, r);
+}
+
+BOOL
+get_pw_sid (PSID sid, struct passwd *pw)
+{
+ char *sp = strrchr (pw->pw_gecos, ',');
+
+ if (!sp)
+ return FALSE;
+ return get_ssid (sid, ++sp) != NULL;
+}
+
+BOOL
+get_gr_sid (PSID sid, struct group *gr)
+{
+ return get_ssid (sid, gr->gr_passwd) != NULL;
+}
+
+PSID
+get_admin_sid ()
+{
+ static NO_COPY char admin_sid_buf[MAX_SID_LEN];
+ static NO_COPY PSID admin_sid = NULL;
+
+ if (!admin_sid)
+ {
+ admin_sid = (PSID) admin_sid_buf;
+ get_ssid (admin_sid, "S-1-5-32-544");
+ }
+ return admin_sid;
+}
+
+PSID
+get_system_sid ()
+{
+ static NO_COPY char system_sid_buf[MAX_SID_LEN];
+ static NO_COPY PSID system_sid = NULL;
+
+ if (!system_sid)
+ {
+ system_sid = (PSID) system_sid_buf;
+ get_ssid (system_sid, "S-1-5-18");
+ }
+ return system_sid;
+}
+
+PSID
+get_creator_owner_sid ()
+{
+ static NO_COPY char owner_sid_buf[MAX_SID_LEN];
+ static NO_COPY PSID owner_sid = NULL;
+
+ if (!owner_sid)
+ {
+ owner_sid = (PSID) owner_sid_buf;
+ get_ssid (owner_sid, "S-1-3-0");
+ }
+ return owner_sid;
+}
+
+PSID
+get_world_sid ()
+{
+ static NO_COPY char world_sid_buf[MAX_SID_LEN];
+ static NO_COPY PSID world_sid = NULL;
+
+ if (!world_sid)
+ {
+ world_sid = (PSID) world_sid_buf;
+ get_ssid (world_sid, "S-1-1-0");
+ }
+ return world_sid;
+}
+
+int passwd_sem = 0;
+int group_sem = 0;
+
+static int
+get_id_from_sid (PSID psid, BOOL search_grp, int *type)
+{
+ if (!IsValidSid (psid))
+ {
+ __seterrno ();
+ small_printf ("IsValidSid failed with %E");
+ return -1;
+ }
+
+ /* First try to get SID from passwd or group entry */
+ if (allow_ntsec)
+ {
+ char sidbuf[MAX_SID_LEN];
+ PSID sid = (PSID) sidbuf;
+ int id = -1;
+
+ if (! search_grp)
+ {
+ if (passwd_sem > 0)
+ return 0;
+ ++passwd_sem;
+
+ struct passwd *pw;
+ while ((pw = getpwent ()) != NULL)
+ {
+ if (get_pw_sid (sid, pw) && EqualSid (psid, sid))
+ {
+ id = pw->pw_uid;
+ break;
+ }
+ }
+ endpwent ();
+ --passwd_sem;
+ if (id >= 0)
+ {
+ if (type)
+ *type = USER;
+ return id;
+ }
+ }
+ if (search_grp || type)
+ {
+ if (group_sem > 0)
+ return 0;
+ ++group_sem;
+
+ struct group *gr;
+ while ((gr = getgrent ()) != NULL)
+ {
+ if (get_gr_sid (sid, gr) && EqualSid (psid, sid))
+ {
+ id = gr->gr_gid;
+ break;
+ }
+ }
+ endgrent ();
+ --group_sem;
+ if (id >= 0)
+ {
+ if (type)
+ *type = GROUP;
+ return id;
+ }
+ }
+ }
+
+ /* We use the RID as default UID/GID */
+ int id = *GetSidSubAuthority(psid, *GetSidSubAuthorityCount(psid) - 1);
+
+ /*
+ * The RID maybe -1 if accountname == computername.
+ * In this case we search for the accountname in the passwd and group files.
+ * If type is needed, we search in each case.
+ */
+ if (id == -1 || type)
+ {
+ char account[MAX_USER_NAME];
+ char domain[MAX_COMPUTERNAME_LENGTH+1];
+ DWORD acc_len = MAX_USER_NAME;
+ DWORD dom_len = MAX_COMPUTERNAME_LENGTH+1;
+ SID_NAME_USE acc_type;
+
+ if (!LookupAccountSid (NULL, psid, account, &acc_len,
+ domain, &dom_len, &acc_type))
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ switch (acc_type)
+ {
+ case SidTypeGroup:
+ case SidTypeAlias:
+ case SidTypeWellKnownGroup:
+ if (type)
+ *type = GROUP;
+ if (id == -1)
+ {
+ struct group *gr = getgrnam (account);
+ if (gr)
+ id = gr->gr_gid;
+ }
+ break;
+ case SidTypeUser:
+ if (type)
+ *type = USER;
+ if (id == -1)
+ {
+ struct passwd *pw = getpwnam (account);
+ if (pw)
+ id = pw->pw_uid;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (id == -1)
+ id = getuid ();
+ return id;
+}
+
+int
+get_id_from_sid (PSID psid, BOOL search_grp)
+{
+ return get_id_from_sid (psid, search_grp, NULL);
+}
+
+static BOOL
+legal_sid_type (SID_NAME_USE type)
+{
+ return type == SidTypeUser || type == SidTypeGroup
+ || SidTypeAlias || SidTypeWellKnownGroup;
+}
+
+BOOL
+is_grp_member (uid_t uid, gid_t gid)
+{
+ extern int getgroups (int, gid_t *, gid_t, const char *);
+ BOOL grp_member = TRUE;
+
+ if (!group_sem && !passwd_sem)
+ {
+ struct passwd *pw = getpwuid (uid);
+ gid_t grps[NGROUPS_MAX];
+ int cnt = getgroups (NGROUPS_MAX, grps,
+ pw ? pw->pw_gid : myself->gid,
+ pw ? pw->pw_name : myself->username);
+ int i;
+ for (i = 0; i < cnt; ++i)
+ if (grps[i] == gid)
+ break;
+ grp_member = (i < cnt);
+ }
+ return grp_member;
+}
+
+BOOL
+lookup_name (const char *name, const char *logsrv, PSID ret_sid)
+{
+ char sidbuf[MAX_SID_LEN];
+ PSID sid = (PSID) sidbuf;
+ DWORD sidlen;
+ char domuser[MAX_COMPUTERNAME_LENGTH+MAX_USER_NAME+1];
+ char dom[MAX_COMPUTERNAME_LENGTH+1];
+ DWORD domlen;
+ SID_NAME_USE acc_type;
+
+ debug_printf ("name : %s", name ? name : "NULL");
+
+ if (! name)
+ return FALSE;
+
+ if (logsrv && *logsrv)
+ {
+ if (LookupAccountName (logsrv, name,
+ sid, (sidlen = MAX_SID_LEN, &sidlen),
+ dom, (domlen = MAX_COMPUTERNAME_LENGTH, &domlen),
+ &acc_type)
+ && legal_sid_type (acc_type))
+ goto got_it;
+ if (acc_type == SidTypeDomain)
+ {
+ strcat (strcat (strcpy (domuser, dom), "\\"), name);
+ if (LookupAccountName (logsrv, domuser,
+ sid,(sidlen = MAX_SID_LEN, &sidlen),
+ dom,(domlen = MAX_COMPUTERNAME_LENGTH,&domlen),
+ &acc_type))
+ goto got_it;
+ }
+ }
+ if (LookupAccountName (NULL, name,
+ sid, (sidlen = MAX_SID_LEN, &sidlen),
+ dom, (domlen = 100, &domlen),
+ &acc_type)
+ && legal_sid_type (acc_type))
+ goto got_it;
+ if (acc_type == SidTypeDomain)
+ {
+ strcat (strcat (strcpy (domuser, dom), "\\"), name);
+ if (LookupAccountName (NULL, domuser,
+ sid, (sidlen = MAX_SID_LEN, &sidlen),
+ dom, (domlen = MAX_COMPUTERNAME_LENGTH, &domlen),
+ &acc_type))
+ goto got_it;
+ }
+ debug_printf ("LookupAccountName(%s) %E", name);
+ __seterrno ();
+ return FALSE;
+
+got_it:
+ debug_printf ("sid : [%d]", *GetSidSubAuthority((PSID) sid,
+ *GetSidSubAuthorityCount((PSID) sid) - 1));
+
+ if (ret_sid)
+ memcpy (ret_sid, sid, sidlen);
+
+ return TRUE;
+}
+
+/* ReadSD reads a security descriptor from a file.
+ In case of error, -1 is returned and errno is set.
+ If the file doesn't have a SD, 0 is returned.
+ Otherwise, the size of the SD is returned and
+ the SD is copied to the buffer, pointed to by sdBuf.
+ sdBufSize contains the size of the buffer. If
+ it's too small, to contain the complete SD, 0 is
+ returned and sdBufSize is set to the needed size
+ of the buffer.
+*/
+
+LONG
+ReadSD(const char *file, PSECURITY_DESCRIPTOR sdBuf, LPDWORD sdBufSize)
+{
+ /* Check parameters */
+ if (! sdBufSize)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* Open file for read */
+ HANDLE hFile = CreateFile (file, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sec_none_nih, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ /* step through the backup streams and search for the security data */
+ WIN32_STREAM_ID header;
+ DWORD bytes_read = 0;
+ LPVOID context = NULL;
+ PSECURITY_DESCRIPTOR psd = NULL;
+ DWORD datasize;
+ LONG ret = 0;
+
+ while (BackupRead (hFile, (LPBYTE) &header,
+ 3 * sizeof (DWORD) + sizeof (LARGE_INTEGER),
+ &bytes_read, FALSE, TRUE, &context))
+ {
+ if (header.dwStreamId != BACKUP_SECURITY_DATA)
+ continue;
+
+ /* security data found */
+ datasize = header.Size.LowPart + header.dwStreamNameSize;
+ char b[datasize];
+
+ if (! BackupRead (hFile, (LPBYTE) b, datasize, &bytes_read,
+ FALSE, TRUE, &context))
+ {
+ __seterrno ();
+ ret = -1;
+ break;
+ }
+
+ /* Check validity of the SD */
+ psd = (PSECURITY_DESCRIPTOR) &b[header.dwStreamNameSize];
+ if (! IsValidSecurityDescriptor (psd))
+ continue;
+
+ /* It's a valid SD */
+ datasize -= header.dwStreamNameSize;
+ debug_printf ("SD-Size: %d", datasize);
+
+ /* buffer to small? */
+ if (*sdBufSize < datasize)
+ {
+ *sdBufSize = datasize;
+ ret = 0;
+ break;
+ }
+
+ if (sdBuf)
+ memcpy (sdBuf, psd, datasize);
+
+ ret = *sdBufSize = datasize;
+ break;
+
+ }
+ BackupRead (hFile, NULL, 0, &bytes_read, TRUE, TRUE, &context);
+ CloseHandle (hFile);
+ return ret;
+}
+
+LONG
+WriteSD(const char *file, PSECURITY_DESCRIPTOR sdBuf, DWORD sdBufSize)
+{
+ /* Check parameters */
+ if (! sdBuf || ! sdBufSize)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ HANDLE hFile = CreateFile (file,
+ WRITE_OWNER | WRITE_DAC,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sec_none_nih,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ LPVOID context = NULL;
+ DWORD bytes_written = 0;
+ WIN32_STREAM_ID header;
+
+ memset (&header, 0, sizeof (header));
+ /* write new security info header */
+ header.dwStreamId = BACKUP_SECURITY_DATA;
+ header.dwStreamAttributes = STREAM_CONTAINS_SECURITY;
+ header.Size.HighPart = 0;
+ header.Size.LowPart = sdBufSize;
+ header.dwStreamNameSize = 0;
+ if (!BackupWrite (hFile, (LPBYTE) &header,
+ 3 * sizeof (DWORD) + sizeof (LARGE_INTEGER),
+ &bytes_written, FALSE, TRUE, &context))
+ {
+ __seterrno ();
+ CloseHandle (hFile);
+ return -1;
+ }
+
+ /* write new security descriptor */
+ if (!BackupWrite (hFile, (LPBYTE) sdBuf,
+ header.Size.LowPart + header.dwStreamNameSize,
+ &bytes_written, FALSE, TRUE, &context))
+ {
+ /* Samba returns ERROR_NOT_SUPPORTED.
+ FAT returns ERROR_INVALID_SECURITY_DESCR.
+ This shouldn't return as error, but better be ignored. */
+ DWORD ret = GetLastError ();
+ if (ret != ERROR_NOT_SUPPORTED && ret != ERROR_INVALID_SECURITY_DESCR)
+ {
+ __seterrno ();
+ BackupWrite (hFile, NULL, 0, &bytes_written, TRUE, TRUE, &context);
+ CloseHandle (hFile);
+ return -1;
+ }
+ }
+
+ /* terminate the restore process */
+ BackupWrite (hFile, NULL, 0, &bytes_written, TRUE, TRUE, &context);
+ CloseHandle (hFile);
+ return 0;
+}
+
+static int
+set_process_privileges ()
+{
+ HANDLE hProcess = NULL;
+ HANDLE hToken = NULL;
+ LUID restore_priv;
+ LUID backup_priv;
+ char buf[sizeof (TOKEN_PRIVILEGES) + 2 * sizeof (LUID_AND_ATTRIBUTES)];
+ TOKEN_PRIVILEGES *new_priv = (TOKEN_PRIVILEGES *) buf;
+ int ret = -1;
+
+ hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId ());
+ if (! hProcess)
+ {
+ __seterrno ();
+ goto out;
+ }
+
+ if (! OpenProcessToken (hProcess,
+ TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
+ &hToken))
+ {
+ __seterrno ();
+ goto out;
+ }
+
+ if (! LookupPrivilegeValue (NULL, SE_RESTORE_NAME, &restore_priv))
+ {
+ __seterrno ();
+ goto out;
+ }
+ if (! LookupPrivilegeValue (NULL, SE_BACKUP_NAME, &backup_priv))
+ {
+ __seterrno ();
+ goto out;
+ }
+
+ new_priv->PrivilegeCount = 2;
+ new_priv->Privileges[0].Luid = restore_priv;
+ new_priv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ new_priv->Privileges[1].Luid = backup_priv;
+ new_priv->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
+
+ if (! AdjustTokenPrivileges (hToken, FALSE, new_priv, 0, NULL, NULL))
+ {
+ __seterrno ();
+ goto out;
+ }
+
+ ret = 0;
+
+ if (ret == -1)
+ __seterrno ();
+
+out:
+ if (hToken)
+ CloseHandle (hToken);
+ if (hProcess)
+ CloseHandle (hProcess);
+
+ syscall_printf ("%d = set_process_privileges ()", ret);
+ return ret;
+}
+
+static int
+get_nt_attribute (const char *file, int *attribute)
+{
+ if (os_being_run != winNT)
+ return 0;
+
+ syscall_printf ("file: %s", file);
+
+ if (set_process_privileges () < 0)
+ return -1;
+
+ /* Yeah, sounds too much, but I've seen SDs of 2100 bytes! */
+ DWORD sd_size = 4096;
+ char sd_buf[4096];
+ PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) sd_buf;
+
+ int ret;
+ if ((ret = ReadSD (file, psd, &sd_size)) <= 0)
+ {
+ debug_printf ("ReadSD %E");
+ return ret;
+ }
+
+ PSID owner_sid;
+ PSID group_sid;
+ BOOL dummy;
+
+ if (! GetSecurityDescriptorOwner (psd, &owner_sid, &dummy))
+ debug_printf ("GetSecurityDescriptorOwner %E");
+ if (! GetSecurityDescriptorGroup (psd, &group_sid, &dummy))
+ debug_printf ("GetSecurityDescriptorGroup %E");
+
+ PACL acl;
+ BOOL acl_exists;
+
+ if (! GetSecurityDescriptorDacl (psd, &acl_exists, &acl, &dummy))
+ {
+ __seterrno ();
+ debug_printf ("GetSecurityDescriptorDacl %E");
+ return -1;
+ }
+
+ if (! acl_exists || ! acl)
+ {
+ *attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
+ syscall_printf ("file: %s No ACL = %x", file, *attribute);
+ return 0;
+ }
+
+ BOOL grp_member = is_grp_member (get_uid_from_sid (owner_sid),
+ get_gid_from_sid (group_sid));
+
+ ACCESS_ALLOWED_ACE *ace;
+ int allow = 0;
+ int deny = 0;
+ int *flags, *anti;
+
+ for (DWORD i = 0; i < acl->AceCount; ++i)
+ {
+ if (!GetAce (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;
+ }
+
+ PSID ace_sid = (PSID) &ace->SidStart;
+ if (owner_sid && EqualSid (ace_sid, owner_sid))
+ {
+ if (ace->Mask & FILE_READ_DATA)
+ *flags |= S_IRUSR;
+ if (ace->Mask & FILE_WRITE_DATA)
+ *flags |= S_IWUSR;
+ if (ace->Mask & FILE_EXECUTE)
+ *flags |= S_IXUSR;
+ }
+ else if (group_sid && EqualSid (ace_sid, group_sid))
+ {
+ if (ace->Mask & FILE_READ_DATA)
+ *flags |= S_IRGRP
+ | ((grp_member && !(*anti & S_IRUSR)) ? S_IRUSR : 0);
+ if (ace->Mask & FILE_WRITE_DATA)
+ *flags |= S_IWGRP
+ | ((grp_member && !(*anti & S_IWUSR)) ? S_IWUSR : 0);
+ if (ace->Mask & FILE_EXECUTE)
+ *flags |= S_IXGRP
+ | ((grp_member && !(*anti & S_IXUSR)) ? S_IXUSR : 0);
+ }
+ else if (EqualSid (ace_sid, get_world_sid ()))
+ {
+ if (ace->Mask & FILE_READ_DATA)
+ *flags |= S_IROTH
+ | ((!(*anti & S_IRGRP)) ? S_IRGRP : 0)
+ | ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
+ if (ace->Mask & FILE_WRITE_DATA)
+ *flags |= S_IWOTH
+ | ((!(*anti & S_IWGRP)) ? S_IWGRP : 0)
+ | ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
+ if (ace->Mask & FILE_EXECUTE)
+ {
+ *flags |= S_IXOTH
+ | ((!(*anti & S_IXGRP)) ? S_IXGRP : 0)
+ | ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
+ // Sticky bit for directories according to linux rules.
+ // No sense for files.
+ if (! (ace->Mask & FILE_DELETE_CHILD)
+ && S_ISDIR(*attribute)
+ && !(*anti & S_ISVTX))
+ *flags |= S_ISVTX;
+ }
+ }
+ }
+ *attribute &= ~(S_IRWXU|S_IRWXG|S_IRWXO|S_ISVTX);
+ *attribute |= allow;
+ *attribute &= ~deny;
+ syscall_printf ("file: %s %x", file, *attribute);
+ return 0;
+}
+
+int
+get_file_attribute (int use_ntsec, const char *file, int *attribute)
+{
+ if (!attribute)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ int res;
+
+ if (use_ntsec && allow_ntsec)
+ {
+ res = get_nt_attribute (file, attribute);
+ if (!res)
+ return 0;
+ }
+
+ res = NTReadEA (file, ".UNIXATTR", (char *) attribute, sizeof (*attribute));
+
+ // symlinks are anything for everyone!
+ if ((*attribute & S_IFLNK) == S_IFLNK)
+ *attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
+
+ if (res > 0)
+ return 0;
+ set_errno (ENOSYS);
+ return -1;
+}
+
+BOOL add_access_allowed_ace (PACL acl, int offset, DWORD attributes,
+ PSID sid, size_t &len_add, DWORD inherit)
+{
+ if (! AddAccessAllowedAce (acl, ACL_REVISION, attributes, sid))
+ {
+ __seterrno ();
+ return FALSE;
+ }
+ ACCESS_ALLOWED_ACE *ace;
+ if (GetAce(acl, offset, (PVOID *) &ace))
+ ace->Header.AceFlags |= inherit;
+ len_add += sizeof (ACCESS_DENIED_ACE) - sizeof (DWORD)
+ + GetLengthSid (sid);
+ return TRUE;
+}
+
+BOOL add_access_denied_ace (PACL acl, int offset, DWORD attributes,
+ PSID sid, size_t &len_add, DWORD inherit)
+{
+ if (! AddAccessDeniedAce (acl, ACL_REVISION, attributes, sid))
+ {
+ __seterrno ();
+ return FALSE;
+ }
+ ACCESS_DENIED_ACE *ace;
+ if (GetAce(acl, offset, (PVOID *) &ace))
+ ace->Header.AceFlags |= inherit;
+ len_add += sizeof (ACCESS_DENIED_ACE) - sizeof (DWORD)
+ + GetLengthSid (sid);
+ return TRUE;
+}
+
+PSECURITY_DESCRIPTOR
+alloc_sd (uid_t uid, gid_t gid, const char *logsrv, int attribute,
+ PSECURITY_DESCRIPTOR sd_ret, DWORD *sd_size_ret)
+{
+ BOOL dummy;
+
+ if (os_being_run != winNT)
+ return NULL;
+
+ if (! sd_ret || ! sd_size_ret)
+ {
+ set_errno (EINVAL);
+ return NULL;
+ }
+
+ // Get SID and name of new owner
+ char owner[MAX_USER_NAME];
+ char *owner_sid_buf[MAX_SID_LEN];
+ PSID owner_sid = NULL;
+ struct passwd *pw = getpwuid (uid);
+ strcpy (owner, pw ? pw->pw_name : getlogin ());
+ owner_sid = (PSID) owner_sid_buf;
+ if ((! pw || ! get_pw_sid (owner_sid, pw))
+ && ! lookup_name (owner, logsrv, owner_sid))
+ return NULL;
+ debug_printf ("owner: %s [%d]", owner,
+ *GetSidSubAuthority((PSID) owner_sid,
+ *GetSidSubAuthorityCount((PSID) owner_sid) - 1));
+
+ // Get SID and name of new group
+ char *group_sid_buf[MAX_SID_LEN];
+ PSID group_sid = NULL;
+ struct group *grp = getgrgid (gid);
+ if (grp)
+ {
+ group_sid = (PSID) group_sid_buf;
+ if ((! grp || ! get_gr_sid (group_sid, grp))
+ && ! lookup_name (grp->gr_name, logsrv, group_sid))
+ return NULL;
+ }
+ else
+ debug_printf ("no group");
+
+ // Initialize local security descriptor
+ SECURITY_DESCRIPTOR sd;
+ PSECURITY_DESCRIPTOR psd = NULL;
+ if (! InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION))
+ {
+ __seterrno ();
+ return NULL;
+ }
+
+ if (! SetSecurityDescriptorOwner(&sd, owner_sid, FALSE))
+ {
+ __seterrno ();
+ return NULL;
+ }
+ if (group_sid
+ && ! SetSecurityDescriptorGroup(&sd, group_sid, FALSE))
+ {
+ __seterrno ();
+ return NULL;
+ }
+
+ // Initialize local access control list
+ char acl_buf[3072];
+ PACL acl = (PACL) acl_buf;
+ if (! InitializeAcl (acl, 3072, ACL_REVISION))
+ {
+ __seterrno ();
+ return NULL;
+ }
+
+ // VTX bit may only be set if executable for `other' is set.
+ // For correct handling under WinNT, FILE_DELETE_CHILD has to
+ // be (un)set in each ACE.
+ if (! (attribute & S_IXOTH))
+ attribute &= ~S_ISVTX;
+
+ // From here fill ACL
+ size_t acl_len = sizeof (ACL);
+ int ace_off = 0;
+
+ // Construct allow attribute for owner
+ DWORD owner_allow = (STANDARD_RIGHTS_ALL & ~DELETE)
+ | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA;
+ if (attribute & S_IRUSR)
+ owner_allow |= FILE_GENERIC_READ;
+ if (attribute & S_IWUSR)
+ owner_allow |= FILE_GENERIC_WRITE | DELETE;
+ if (attribute & S_IXUSR)
+ owner_allow |= FILE_GENERIC_EXECUTE;
+ if (! (attribute & S_ISVTX))
+ owner_allow |= FILE_DELETE_CHILD;
+
+ // Construct allow attribute for group
+ DWORD group_allow = STANDARD_RIGHTS_READ
+ | FILE_READ_ATTRIBUTES | FILE_READ_EA;
+ if (attribute & S_IRGRP)
+ group_allow |= FILE_GENERIC_READ;
+ if (attribute & S_IWGRP)
+ group_allow |= STANDARD_RIGHTS_ALL | FILE_GENERIC_WRITE | DELETE;
+ if (attribute & S_IXGRP)
+ group_allow |= FILE_GENERIC_EXECUTE;
+ if (! (attribute & S_ISVTX))
+ group_allow |= FILE_DELETE_CHILD;
+
+ // Construct allow attribute for everyone
+ DWORD other_allow = STANDARD_RIGHTS_READ
+ | FILE_READ_ATTRIBUTES | FILE_READ_EA;
+ if (attribute & S_IROTH)
+ other_allow |= FILE_GENERIC_READ;
+ if (attribute & S_IWOTH)
+ other_allow |= STANDARD_RIGHTS_ALL | FILE_GENERIC_WRITE | DELETE;
+ if (attribute & S_IXOTH)
+ other_allow |= FILE_GENERIC_EXECUTE;
+ if (! (attribute & S_ISVTX))
+ other_allow |= FILE_DELETE_CHILD;
+
+ // Construct deny attributes for owner and group
+ DWORD owner_deny = 0;
+ if (is_grp_member (uid, gid))
+ owner_deny = ~owner_allow & (group_allow | other_allow);
+ else
+ owner_deny = ~owner_allow & other_allow;
+ owner_deny &= ~(STANDARD_RIGHTS_READ
+ | FILE_READ_ATTRIBUTES | FILE_READ_EA
+ | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA);
+ DWORD group_deny = ~group_allow & other_allow;
+ group_deny &= ~(STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | FILE_READ_EA);
+
+ // Set deny ACE for owner
+ if (owner_deny
+ && ! add_access_denied_ace (acl, ace_off++, owner_deny,
+ owner_sid, acl_len, INHERIT_ALL))
+ return NULL;
+ // Set allow ACE for owner
+ if (! add_access_allowed_ace (acl, ace_off++, owner_allow,
+ owner_sid, acl_len, INHERIT_ALL))
+ return NULL;
+ // Set deny ACE for group
+ if (group_deny
+ && ! add_access_denied_ace (acl, ace_off++, group_deny,
+ group_sid, acl_len, INHERIT_ALL))
+ return NULL;
+ // Set allow ACE for group
+ if (! add_access_allowed_ace (acl, ace_off++, group_allow,
+ group_sid, acl_len, INHERIT_ALL))
+ return NULL;
+
+ // Get owner and group from current security descriptor
+ PSID cur_owner_sid = NULL;
+ PSID cur_group_sid = NULL;
+ if (! GetSecurityDescriptorOwner (sd_ret, &cur_owner_sid, &dummy))
+ debug_printf ("GetSecurityDescriptorOwner %E");
+ if (! GetSecurityDescriptorGroup (sd_ret, &cur_group_sid, &dummy))
+ debug_printf ("GetSecurityDescriptorGroup %E");
+
+ // Fill ACL with unrelated ACEs from current security descriptor
+ PACL oacl;
+ BOOL acl_exists;
+ ACCESS_ALLOWED_ACE *ace;
+ if (GetSecurityDescriptorDacl (sd_ret, &acl_exists, &oacl, &dummy)
+ && acl_exists && oacl)
+ for (DWORD i = 0; i < oacl->AceCount; ++i)
+ if (GetAce (oacl, i, (PVOID *) &ace))
+ {
+ PSID ace_sid = (PSID) &ace->SidStart;
+ // Check for related ACEs
+ if ((cur_owner_sid && EqualSid (ace_sid, cur_owner_sid))
+ || (owner_sid && EqualSid (ace_sid, owner_sid))
+ || (cur_group_sid && EqualSid (ace_sid, cur_group_sid))
+ || (group_sid && EqualSid (ace_sid, group_sid))
+ || (EqualSid (ace_sid, get_world_sid ())))
+ continue;
+ // Add unrelated ACCESS_DENIED_ACE to the beginning but
+ // behind the owner_deny, ACCESS_ALLOWED_ACE to the end
+ // but in front of the `everyone' ACE.
+ if (! AddAce(acl, ACL_REVISION,
+ ace->Header.AceType == ACCESS_DENIED_ACE_TYPE ?
+ (owner_deny ? 1 : 0) : MAXDWORD,
+ (LPVOID) ace, ace->Header.AceSize))
+ {
+ __seterrno ();
+ return NULL;
+ }
+ acl_len += ace->Header.AceSize;
+ ++ace_off;
+ }
+
+ // Set allow ACE for everyone
+ if (! add_access_allowed_ace (acl, ace_off++, other_allow,
+ get_world_sid (), acl_len, INHERIT_ALL))
+ return NULL;
+
+ // Set AclSize to computed value
+ acl->AclSize = acl_len;
+ debug_printf ("ACL-Size: %d", acl_len);
+
+ // Create DACL for local security descriptor
+ if (! SetSecurityDescriptorDacl (&sd, TRUE, acl, FALSE))
+ {
+ __seterrno ();
+ return NULL;
+ }
+
+ // Make self relative security descriptor
+ *sd_size_ret = 0;
+ MakeSelfRelativeSD (&sd, sd_ret, sd_size_ret);
+ if (*sd_size_ret <= 0)
+ {
+ __seterrno ();
+ return NULL;
+ }
+ if (! MakeSelfRelativeSD (&sd, sd_ret, sd_size_ret))
+ {
+ __seterrno ();
+ return NULL;
+ }
+ psd = sd_ret;
+ debug_printf ("Created SD-Size: %d", *sd_size_ret);
+
+ return psd;
+}
+
+static int
+set_nt_attribute (const char *file, uid_t uid, gid_t gid,
+ const char *logsrv, int attribute)
+{
+ if (os_being_run != winNT)
+ return 0;
+
+ if (set_process_privileges () < 0)
+ return -1;
+
+ DWORD sd_size = 4096;
+ char sd_buf[4096];
+ PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) sd_buf;
+
+ int ret;
+ if ((ret = ReadSD (file, psd, &sd_size)) <= 0)
+ {
+ debug_printf ("ReadSD %E");
+ return ret;
+ }
+
+ sd_size = 4096;
+ if (! (psd = alloc_sd (uid, gid, logsrv, attribute, psd, &sd_size)))
+ return -1;
+
+ return WriteSD (file, psd, sd_size);
+}
+
+int
+set_file_attribute (int use_ntsec, const char *file,
+ uid_t uid, gid_t gid,
+ int attribute, const char *logsrv)
+{
+ // symlinks are anything for everyone!
+ if ((attribute & S_IFLNK) == S_IFLNK)
+ attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
+
+ BOOL ret = NTWriteEA (file, ".UNIXATTR",
+ (char *) &attribute, sizeof (attribute));
+ if (!use_ntsec || !allow_ntsec)
+ {
+ if (! ret)
+ {
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+ }
+
+ int ret2 = set_nt_attribute (file, uid, gid, logsrv, attribute);
+ syscall_printf ("%d = set_file_attribute (%s, %d, %d, %p)",
+ ret2, file, uid, gid, attribute);
+ return ret2;
+}
+
+int
+set_file_attribute (int use_ntsec, const char *file, int attribute)
+{
+ return set_file_attribute (use_ntsec, file,
+ myself->uid, myself->gid,
+ attribute, myself->logsrv);
+}
+
+static int
+searchace (aclent_t *aclp, int nentries, int type, int id = -1)
+{
+ int i;
+
+ for (i = 0; i < nentries; ++i)
+ if ((aclp[i].a_type == type && (id < 0 || aclp[i].a_id == id))
+ || !aclp[i].a_type)
+ return i;
+ return -1;
+}
+
+static int
+setacl (const char *file, int nentries, aclent_t *aclbufp)
+{
+ DWORD sd_size = 4096;
+ char sd_buf[4096];
+ PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) sd_buf;
+
+ if (ReadSD (file, psd, &sd_size) <= 0)
+ {
+ debug_printf ("ReadSD %E");
+ return -1;
+ }
+
+ BOOL dummy;
+
+ // Get owner SID
+ PSID owner_sid = NULL;
+ if (! GetSecurityDescriptorOwner (psd, &owner_sid, &dummy))
+ {
+ __seterrno ();
+ return -1;
+ }
+ char owner_buf[MAX_SID_LEN];
+ if (!CopySid (MAX_SID_LEN, (PSID) owner_buf, owner_sid))
+ {
+ __seterrno ();
+ return -1;
+ }
+ owner_sid = (PSID) owner_buf;
+
+ // Get group SID
+ PSID group_sid = NULL;
+ if (! GetSecurityDescriptorGroup (psd, &group_sid, &dummy))
+ {
+ __seterrno ();
+ return -1;
+ }
+ char group_buf[MAX_SID_LEN];
+ if (!CopySid (MAX_SID_LEN, (PSID) group_buf, group_sid))
+ {
+ __seterrno ();
+ return -1;
+ }
+ group_sid = (PSID) group_buf;
+
+ // Initialize local security descriptor
+ SECURITY_DESCRIPTOR sd;
+ if (! InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION))
+ {
+ __seterrno ();
+ return -1;
+ }
+ if (! SetSecurityDescriptorOwner(&sd, owner_sid, FALSE))
+ {
+ __seterrno ();
+ return -1;
+ }
+ if (group_sid
+ && ! SetSecurityDescriptorGroup(&sd, group_sid, FALSE))
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ // Fill access control list
+ char acl_buf[3072];
+ PACL acl = (PACL) acl_buf;
+ size_t acl_len = sizeof (ACL);
+ int ace_off = 0;
+
+ char sidbuf[MAX_SID_LEN];
+ PSID sid = (PSID) sidbuf;
+ struct passwd *pw;
+ struct group *gr;
+ int pos;
+
+ if (! InitializeAcl (acl, 3072, ACL_REVISION))
+ {
+ __seterrno ();
+ return -1;
+ }
+ for (int i = 0; i < nentries; ++i)
+ {
+ DWORD allow = STANDARD_RIGHTS_READ
+ | FILE_READ_ATTRIBUTES | FILE_READ_EA;
+ if (aclbufp[i].a_perm & S_IROTH)
+ allow |= FILE_GENERIC_READ;
+ if (aclbufp[i].a_perm & S_IWOTH)
+ allow |= STANDARD_RIGHTS_ALL | FILE_GENERIC_WRITE
+ | DELETE | FILE_DELETE_CHILD;
+ if (aclbufp[i].a_perm & S_IXOTH)
+ allow |= FILE_GENERIC_EXECUTE;
+ // Set inherit property
+ DWORD inheritance = (aclbufp[i].a_type & ACL_DEFAULT)
+ ? INHERIT_ONLY : DONT_INHERIT;
+ // 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)
+ && (pos = searchace (aclbufp, nentries,
+ aclbufp[i].a_type | ACL_DEFAULT,
+ (aclbufp[i].a_type & (USER|GROUP))
+ ? aclbufp[i].a_id : -1)) >= 0
+ && pos < nentries
+ && aclbufp[i].a_perm == aclbufp[pos].a_perm)
+ {
+ inheritance = INHERIT_ALL;
+ // This eliminates the corresponding default entry.
+ aclbufp[pos].a_type = 0;
+ }
+ switch (aclbufp[i].a_type)
+ {
+ case USER_OBJ:
+ case DEF_USER_OBJ:
+ allow |= STANDARD_RIGHTS_ALL & ~DELETE;
+ if (! add_access_allowed_ace (acl, ace_off++, allow,
+ owner_sid, acl_len, inheritance))
+ return -1;
+ break;
+ case USER:
+ case DEF_USER:
+ if (!(pw = getpwuid (aclbufp[i].a_id))
+ || ! get_pw_sid (sid, pw)
+ || ! add_access_allowed_ace (acl, ace_off++, allow,
+ sid, acl_len, inheritance))
+ return -1;
+ break;
+ case GROUP_OBJ:
+ case DEF_GROUP_OBJ:
+ if (! add_access_allowed_ace (acl, ace_off++, allow,
+ group_sid, acl_len, inheritance))
+ return -1;
+ break;
+ case GROUP:
+ case DEF_GROUP:
+ if (!(gr = getgrgid (aclbufp[i].a_id))
+ || ! get_gr_sid (sid, gr)
+ || ! add_access_allowed_ace (acl, ace_off++, allow,
+ sid, acl_len, inheritance))
+ return -1;
+ break;
+ case OTHER_OBJ:
+ case DEF_OTHER_OBJ:
+ if (! add_access_allowed_ace (acl, ace_off++, allow,
+ get_world_sid(), acl_len, inheritance))
+ return -1;
+ break;
+ }
+ }
+ // Set AclSize to computed value
+ acl->AclSize = acl_len;
+ debug_printf ("ACL-Size: %d", acl_len);
+ // Create DACL for local security descriptor
+ if (! SetSecurityDescriptorDacl (&sd, TRUE, acl, FALSE))
+ {
+ __seterrno ();
+ return -1;
+ }
+ // Make self relative security descriptor in psd
+ sd_size = 0;
+ MakeSelfRelativeSD (&sd, psd, &sd_size);
+ if (sd_size <= 0)
+ {
+ __seterrno ();
+ return -1;
+ }
+ if (! MakeSelfRelativeSD (&sd, psd, &sd_size))
+ {
+ __seterrno ();
+ return -1;
+ }
+ debug_printf ("Created SD-Size: %d", sd_size);
+ return WriteSD (file, psd, sd_size);
+}
+
+static void
+getace (aclent_t &acl, int type, int id, DWORD win_ace_mask, DWORD win_ace_type)
+{
+ acl.a_type = type;
+ acl.a_id = id;
+
+ if (win_ace_mask & FILE_READ_DATA)
+ if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
+ acl.a_perm |= (acl.a_perm & S_IRGRP) ? 0 : S_IRUSR;
+ else if (win_ace_type == ACCESS_DENIED_ACE_TYPE)
+ acl.a_perm &= ~S_IRGRP;
+
+ if (win_ace_mask & FILE_WRITE_DATA)
+ if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
+ acl.a_perm |= (acl.a_perm & S_IWGRP) ? 0 : S_IWUSR;
+ else if (win_ace_type == ACCESS_DENIED_ACE_TYPE)
+ acl.a_perm &= ~S_IWGRP;
+
+ if (win_ace_mask & FILE_EXECUTE)
+ if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
+ acl.a_perm |= (acl.a_perm & S_IXGRP) ? 0 : S_IXUSR;
+ else if (win_ace_type == ACCESS_DENIED_ACE_TYPE)
+ acl.a_perm &= ~S_IXGRP;
+}
+
+static int
+getacl (const char *file, DWORD attr, int nentries, aclent_t *aclbufp)
+{
+ DWORD sd_size = 4096;
+ char sd_buf[4096];
+ PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) sd_buf;
+
+ int ret;
+ if ((ret = ReadSD (file, psd, &sd_size)) <= 0)
+ {
+ debug_printf ("ReadSD %E");
+ return ret;
+ }
+
+ PSID owner_sid;
+ PSID group_sid;
+ BOOL dummy;
+ uid_t uid;
+ gid_t gid;
+
+ if (! GetSecurityDescriptorOwner (psd, &owner_sid, &dummy))
+ {
+ debug_printf ("GetSecurityDescriptorOwner %E");
+ __seterrno ();
+ return -1;
+ }
+ uid = get_uid_from_sid (owner_sid);
+
+ if (! GetSecurityDescriptorGroup (psd, &group_sid, &dummy))
+ {
+ debug_printf ("GetSecurityDescriptorGroup %E");
+ __seterrno ();
+ return -1;
+ }
+ gid = get_gid_from_sid (group_sid);
+
+ 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;
+ lacl[1].a_id = gid;
+ lacl[2].a_type = OTHER_OBJ;
+
+ PACL acl;
+ BOOL acl_exists;
+
+ if (! GetSecurityDescriptorDacl (psd, &acl_exists, &acl, &dummy))
+ {
+ __seterrno ();
+ debug_printf ("GetSecurityDescriptorDacl %E");
+ return -1;
+ }
+
+ int pos, i;
+
+ if (! acl_exists || ! acl)
+ {
+ for (pos = 0; pos < MIN_ACL_ENTRIES; ++pos)
+ lacl[pos].a_perm = S_IRWXU | S_IRWXG | S_IRWXO;
+ pos = nentries < MIN_ACL_ENTRIES ? nentries : MIN_ACL_ENTRIES;
+ memcpy (aclbufp, lacl, pos * sizeof (aclent_t));
+ return pos;
+ }
+
+ for (i = 0; i < acl->AceCount && (!nentries || i < nentries); ++i)
+ {
+ ACCESS_ALLOWED_ACE *ace;
+
+ if (!GetAce (acl, i, (PVOID *) &ace))
+ continue;
+
+ PSID ace_sid = (PSID) &ace->SidStart;
+ int id;
+ int type = 0;
+
+ if (EqualSid (ace_sid, owner_sid))
+ {
+ type = USER_OBJ;
+ id = uid;
+ }
+ else if (EqualSid (ace_sid, group_sid))
+ {
+ type = GROUP_OBJ;
+ id = gid;
+ }
+ else if (EqualSid (ace_sid, get_world_sid ()))
+ {
+ type = OTHER_OBJ;
+ id = 0;
+ }
+ else
+ {
+ id = get_id_from_sid (ace_sid, FALSE, &type);
+ if (type != GROUP)
+ {
+ int type2 = 0;
+ int id2 = get_id_from_sid (ace_sid, TRUE, &type2);
+ if (type2 == GROUP)
+ {
+ id = id2;
+ type = GROUP;
+ }
+ }
+ }
+ if (!type)
+ continue;
+ if (!(ace->Header.AceFlags & INHERIT_ONLY_ACE))
+ {
+ if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
+ getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType);
+ }
+ if ((ace->Header.AceFlags & INHERIT_ALL)
+ && (attr & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ type |= ACL_DEFAULT;
+ if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
+ getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType);
+ }
+ }
+ if ((pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) < 0)
+ pos = MAX_ACL_ENTRIES;
+ for (i = 0; i < pos; ++i)
+ {
+ lacl[i].a_perm = (lacl[i].a_perm & S_IRWXU)
+ & ~((lacl[i].a_perm & S_IRWXG) << 3);
+ lacl[i].a_perm |= (lacl[i].a_perm & S_IRWXU) >> 3
+ | (lacl[i].a_perm & S_IRWXU) >> 6;
+ }
+ if ((searchace (lacl, MAX_ACL_ENTRIES, USER) >= 0
+ || searchace (lacl, MAX_ACL_ENTRIES, GROUP) >= 0)
+ && (pos = searchace (lacl, MAX_ACL_ENTRIES, CLASS_OBJ)) >= 0)
+ {
+ lacl[pos].a_type = CLASS_OBJ;
+ lacl[pos].a_perm =
+ lacl[searchace (lacl, MAX_ACL_ENTRIES, GROUP_OBJ)].a_perm;
+ }
+ int dgpos;
+ if ((searchace (lacl, MAX_ACL_ENTRIES, DEF_USER) >= 0
+ || searchace (lacl, MAX_ACL_ENTRIES, DEF_GROUP) >= 0)
+ && (dgpos = searchace (lacl, MAX_ACL_ENTRIES, DEF_GROUP_OBJ)) >= 0
+ && (pos = searchace (lacl, MAX_ACL_ENTRIES, DEF_CLASS_OBJ)) >= 0
+ && (attr & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ lacl[pos].a_type = DEF_CLASS_OBJ;
+ lacl[pos].a_perm = lacl[dgpos].a_perm;
+ }
+ if ((pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) < 0)
+ pos = MAX_ACL_ENTRIES;
+ if (pos > nentries)
+ pos = nentries;
+ if (aclbufp)
+ memcpy (aclbufp, lacl, pos * sizeof (aclent_t));
+ aclsort (pos, 0, aclbufp);
+ syscall_printf ("%d = getacl (%s)", pos, file);
+ return pos;
+}
+
+int
+acl_access (const char *path, int flags)
+{
+ aclent_t acls[MAX_ACL_ENTRIES];
+ int cnt;
+
+ if ((cnt = acl (path, GETACL, MAX_ACL_ENTRIES, acls)) < 1)
+ return -1;
+
+ // Only check existance.
+ if (!(flags & (R_OK|W_OK|X_OK)))
+ return 0;
+
+ for (int i = 0; i < cnt; ++i)
+ {
+ switch (acls[i].a_type)
+ {
+ case USER_OBJ:
+ case USER:
+ if (acls[i].a_id != myself->uid)
+ {
+ // Check if user is a NT group:
+ // Take SID from passwd, search SID in group, check is_grp_member
+ char owner_sidbuf[MAX_SID_LEN];
+ PSID owner_sid = (PSID) owner_sidbuf;
+ char group_sidbuf[MAX_SID_LEN];
+ PSID group_sid = (PSID) group_sidbuf;
+ struct passwd *pw;
+ struct group *gr = NULL;
+
+ if (group_sem > 0)
+ continue;
+ ++group_sem;
+ if ((pw = getpwuid (acls[i].a_id)) != NULL
+ && get_pw_sid (owner_sid, pw))
+ {
+ while ((gr = getgrent ()))
+ if (get_gr_sid (group_sid, gr)
+ && EqualSid (owner_sid, group_sid)
+ && is_grp_member (myself->uid, gr->gr_gid))
+ break;
+ endgrent ();
+ }
+ --group_sem;
+ if (! gr)
+ continue;
+ }
+ break;
+ case GROUP_OBJ:
+ case GROUP:
+ if (acls[i].a_id != myself->gid &&
+ !is_grp_member (myself->uid, acls[i].a_id))
+ continue;
+ break;
+ case OTHER_OBJ:
+ break;
+ default:
+ continue;
+ }
+ if ((!(flags & R_OK) || (acls[i].a_perm & S_IREAD))
+ && (!(flags & W_OK) || (acls[i].a_perm & S_IWRITE))
+ && (!(flags & X_OK) || (acls[i].a_perm & S_IEXEC)))
+ return 0;
+ }
+ set_errno (EACCES);
+ return -1;
+}
+
+extern "C"
+int
+acl (const char *path, int cmd, int nentries, aclent_t *aclbufp)
+{
+ if (set_process_privileges () < 0)
+ return -1;
+
+ path_conv real_path (path);
+ if (real_path.error)
+ {
+ set_errno (real_path.error);
+ syscall_printf ("-1 = acl (%s)", path);
+ return -1;
+ }
+ if (!real_path.has_acls ())
+ {
+ struct stat st;
+ int ret = -1;
+
+ switch (cmd)
+ {
+ case SETACL:
+ set_errno (ENOSYS);
+ break;
+ case GETACL:
+ if (nentries < 1)
+ set_errno (EINVAL);
+ else if (! stat (path, &st))
+ {
+ aclent_t lacl[4];
+ if (nentries > 0)
+ {
+ lacl[0].a_type = USER_OBJ;
+ lacl[0].a_id = st.st_uid;
+ lacl[0].a_perm = (st.st_mode & S_IRWXU)
+ | (st.st_mode & S_IRWXU) >> 3
+ | (st.st_mode & S_IRWXU) >> 6;
+ }
+ if (nentries > 1)
+ {
+ lacl[1].a_type = GROUP_OBJ;
+ lacl[1].a_id = st.st_gid;
+ lacl[1].a_perm = (st.st_mode & S_IRWXG)
+ | (st.st_mode & S_IRWXG) << 3
+ | (st.st_mode & S_IRWXG) >> 3;
+ }
+ if (nentries > 2)
+ {
+ lacl[2].a_type = OTHER_OBJ;
+ lacl[2].a_id = 0;
+ lacl[2].a_perm = (st.st_mode & S_IRWXO)
+ | (st.st_mode & S_IRWXO) << 6
+ | (st.st_mode & S_IRWXO) << 3;
+ }
+ if (nentries > 3)
+ {
+ lacl[3].a_type = CLASS_OBJ;
+ lacl[3].a_id = 0;
+ lacl[3].a_perm = (st.st_mode & S_IRWXG)
+ | (st.st_mode & S_IRWXG) << 3
+ | (st.st_mode & S_IRWXG) >> 3;
+ }
+ if (nentries > 4)
+ nentries = 4;
+ if (aclbufp)
+ memcpy (aclbufp, lacl, nentries * sizeof (aclent_t));
+ ret = nentries;
+ }
+ break;
+ case GETACLCNT:
+ ret = 4;
+ break;
+ }
+ syscall_printf ("%d = acl (%s)", ret, path);
+ return ret;
+ }
+ switch (cmd)
+ {
+ case SETACL:
+ if (!aclsort(nentries, 0, aclbufp))
+ return setacl (real_path.get_win32 (),
+ nentries, aclbufp);
+ break;
+ case GETACL:
+ if (nentries < 1)
+ break;
+ return getacl (real_path.get_win32 (),
+ real_path.file_attributes (),
+ nentries, aclbufp);
+ case GETACLCNT:
+ return getacl (real_path.get_win32 (),
+ real_path.file_attributes (),
+ 0, NULL);
+ default:
+ break;
+ }
+ set_errno (EINVAL);
+ syscall_printf ("-1 = acl (%s)", path);
+ return -1;
+}
+
+extern "C"
+int
+facl (int fd, int cmd, int nentries, aclent_t *aclbufp)
+{
+ if (dtable.not_open (fd))
+ {
+ syscall_printf ("-1 = facl (%d)", fd);
+ set_errno (EBADF);
+ return -1;
+ }
+ const char *path = dtable[fd]->get_name ();
+ if (path == NULL)
+ {
+ syscall_printf ("-1 = facl (%d) (no name)", fd);
+ set_errno (ENOSYS);
+ return -1;
+ }
+ syscall_printf ("facl (%d): calling acl (%s)", fd, path);
+ return acl (path, cmd, nentries, aclbufp);
+}
+
+extern "C"
+int
+aclcheck (aclent_t *aclbufp, int nentries, int *which)
+{
+ BOOL has_user_obj = FALSE;
+ BOOL has_group_obj = FALSE;
+ BOOL has_other_obj = FALSE;
+ BOOL has_class_obj = FALSE;
+ BOOL has_ug_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 = 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_user_obj = TRUE;
+ break;
+ case DEF_GROUP_OBJ:
+ if (has_def_group_obj)
+ {
+ if (which)
+ *which = pos;
+ return GRP_ERROR;
+ }
+ has_def_group_obj = TRUE;
+ break;
+ case DEF_OTHER_OBJ:
+ if (has_def_other_obj)
+ {
+ if (which)
+ *which = pos;
+ return OTHER_ERROR;
+ }
+ has_def_other_obj = TRUE;
+ break;
+ case DEF_CLASS_OBJ:
+ if (has_def_class_obj)
+ {
+ if (which)
+ *which = pos;
+ return CLASS_ERROR;
+ }
+ 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_ug_objs = TRUE;
+ break;
+ default:
+ return ENTRY_ERROR;
+ }
+ if (!has_user_obj
+ || !has_group_obj
+ || !has_other_obj
+#if 0
+ // These checks are not ok yet since CLASS_OBJ isn't fully implemented.
+ || (has_ug_objs && !has_class_obj)
+ || (has_def_ug_objs && !has_def_class_obj)
+#endif
+ )
+ {
+ if (which)
+ *which = -1;
+ return MISS_ERROR;
+ }
+ return 0;
+}
+
+extern "C"
+int acecmp (const void *a1, const void *a2)
+{
+#define ace(i) ((const aclent_t *) a##i)
+ int ret = ace(1)->a_type - ace(2)->a_type;
+ if (!ret)
+ ret = ace(1)->a_id - ace(2)->a_id;
+ return ret;
+#undef ace
+}
+
+extern "C"
+int
+aclsort (int nentries, int calclass, aclent_t *aclbufp)
+{
+ if (aclcheck (aclbufp, nentries, NULL))
+ return -1;
+ if (!aclbufp || nentries < 1)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ qsort((void *) aclbufp, nentries, sizeof (aclent_t), acecmp);
+ return 0;
+}
+
+extern "C"
+int
+acltomode (aclent_t *aclbufp, int nentries, mode_t *modep)
+{
+ int pos;
+
+ if (!aclbufp || nentries < 1 || ! modep)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ *modep = 0;
+ if ((pos = searchace (aclbufp, nentries, USER_OBJ)) < 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ *modep |= aclbufp[pos].a_perm & S_IRWXU;
+ if ((pos = searchace (aclbufp, nentries, GROUP_OBJ)) < 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ if (searchace (aclbufp, nentries, CLASS_OBJ) < 0)
+ pos = searchace (aclbufp, nentries, CLASS_OBJ);
+ *modep |= (aclbufp[pos].a_perm & S_IRWXU) >> 3;
+ if ((pos = searchace (aclbufp, nentries, OTHER_OBJ)) < 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ *modep |= (aclbufp[pos].a_perm & S_IRWXU) >> 6;
+ return 0;
+}
+
+extern "C"
+int
+aclfrommode(aclent_t *aclbufp, int nentries, mode_t *modep)
+{
+ int pos;
+
+ if (!aclbufp || nentries < 1 || ! modep)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ if ((pos = searchace (aclbufp, nentries, USER_OBJ)) < 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ aclbufp[pos].a_perm = (*modep & S_IRWXU)
+ | (*modep & S_IRWXU) >> 3
+ | (*modep & S_IRWXU) >> 6;
+ if ((pos = searchace (aclbufp, nentries, GROUP_OBJ)) < 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ if (searchace (aclbufp, nentries, CLASS_OBJ) < 0)
+ pos = searchace (aclbufp, nentries, CLASS_OBJ);
+ aclbufp[pos].a_perm = (*modep & S_IRWXG)
+ | (*modep & S_IRWXG) << 3
+ | (*modep & S_IRWXG) >> 3;
+ if ((pos = searchace (aclbufp, nentries, OTHER_OBJ)) < 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ aclbufp[pos].a_perm = (*modep & S_IRWXO)
+ | (*modep & S_IRWXO) << 6
+ | (*modep & S_IRWXO) << 3;
+ return 0;
+}
+
+extern "C"
+int
+acltopbits (aclent_t *aclbufp, int nentries, mode_t *pbitsp)
+{
+ return acltomode (aclbufp, nentries, pbitsp);
+}
+
+extern "C"
+int
+aclfrompbits (aclent_t *aclbufp, int nentries, mode_t *pbitsp)
+{
+ return aclfrommode (aclbufp, nentries, pbitsp);
+}
+
+static char *
+permtostr (mode_t perm)
+{
+ static char pbuf[4];
+
+ pbuf[0] = (perm & S_IREAD) ? 'r' : '-';
+ pbuf[1] = (perm & S_IWRITE) ? 'w' : '-';
+ pbuf[2] = (perm & S_IEXEC) ? 'x' : '-';
+ pbuf[3] = '\0';
+ return pbuf;
+}
+
+extern "C"
+char *
+acltotext (aclent_t *aclbufp, int aclcnt)
+{
+ if (!aclbufp || aclcnt < 1 || aclcnt > MAX_ACL_ENTRIES
+ || aclcheck (aclbufp, aclcnt, NULL))
+ {
+ set_errno (EINVAL);
+ return NULL;
+ }
+ char buf[32000];
+ 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)
+ {
+ case USER_OBJ:
+ sprintf (buf + strlen (buf), "user::%s",
+ permtostr (aclbufp[pos].a_perm));
+ break;
+ case USER:
+ sprintf (buf + strlen (buf), "user:%d:%s",
+ aclbufp[pos].a_id, permtostr (aclbufp[pos].a_perm));
+ break;
+ case GROUP_OBJ:
+ sprintf (buf + strlen (buf), "group::%s",
+ permtostr (aclbufp[pos].a_perm));
+ break;
+ case GROUP:
+ sprintf (buf + strlen (buf), "group:%d:%s",
+ aclbufp[pos].a_id, permtostr (aclbufp[pos].a_perm));
+ break;
+ case CLASS_OBJ:
+ sprintf (buf + strlen (buf), "mask::%s",
+ permtostr (aclbufp[pos].a_perm));
+ break;
+ case OTHER_OBJ:
+ sprintf (buf + strlen (buf), "other::%s",
+ permtostr (aclbufp[pos].a_perm));
+ break;
+ default:
+ set_errno (EINVAL);
+ return NULL;
+ }
+ }
+ return strdup (buf);
+}
+
+static mode_t
+permfromstr (char *perm)
+{
+ mode_t mode = 0;
+
+ if (strlen (perm) != 3)
+ return 01000;
+ if (perm[0] == 'r')
+ mode |= S_IRUSR | S_IRGRP | S_IROTH;
+ else if (perm[0] != '-')
+ return 01000;
+ if (perm[1] == 'w')
+ mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+ else if (perm[1] != '-')
+ return 01000;
+ if (perm[2] == 'x')
+ mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ else if (perm[2] != '-')
+ return 01000;
+ return mode;
+}
+
+extern "C"
+aclent_t *
+aclfromtext (char *acltextp, int *aclcnt)
+{
+ if (!acltextp)
+ {
+ set_errno (EINVAL);
+ return NULL;
+ }
+ char buf[strlen (acltextp) + 1];
+ aclent_t lacl[MAX_ACL_ENTRIES];
+ memset (lacl, 0, sizeof lacl);
+ int pos = 0;
+ for (char *c = strtok (buf, ","); c; c = strtok (NULL, ","))
+ {
+ if (!strncmp (c, "default", 7))
+ {
+ lacl[pos].a_type |= ACL_DEFAULT;
+ c += 7;
+ }
+ if (!strncmp (c, "user:", 5))
+ {
+ if (c[5] == ':')
+ lacl[pos].a_type |= USER_OBJ;
+ else
+ {
+ lacl[pos].a_type |= USER;
+ c += 5;
+ if (isalpha (*c))
+ {
+ struct passwd *pw = getpwnam (c);
+ if (!pw)
+ {
+ set_errno (EINVAL);
+ return NULL;
+ }
+ lacl[pos].a_id = pw->pw_uid;
+ c = strchr (c, ':');
+ }
+ else if (isdigit (*c))
+ lacl[pos].a_id = strtol (c, &c, 10);
+ if (!c || *c != ':')
+ {
+ set_errno (EINVAL);
+ return NULL;
+ }
+ }
+ }
+ else if (!strncmp (c, "group:", 6))
+ {
+ if (c[5] == ':')
+ lacl[pos].a_type |= GROUP_OBJ;
+ else
+ {
+ lacl[pos].a_type |= GROUP;
+ c += 5;
+ if (isalpha (*c))
+ {
+ struct group *gr = getgrnam (c);
+ if (!gr)
+ {
+ set_errno (EINVAL);
+ return NULL;
+ }
+ lacl[pos].a_id = gr->gr_gid;
+ c = strchr (c, ':');
+ }
+ else if (isdigit (*c))
+ lacl[pos].a_id = strtol (c, &c, 10);
+ if (!c || *c != ':')
+ {
+ set_errno (EINVAL);
+ return NULL;
+ }
+ }
+ }
+ else if (!strncmp (c, "mask:", 5))
+ {
+ if (c[5] == ':')
+ lacl[pos].a_type |= CLASS_OBJ;
+ else
+ {
+ set_errno (EINVAL);
+ return NULL;
+ }
+ }
+ else if (!strncmp (c, "other:", 6))
+ {
+ if (c[5] == ':')
+ lacl[pos].a_type |= OTHER_OBJ;
+ else
+ {
+ set_errno (EINVAL);
+ return NULL;
+ }
+ }
+ if ((lacl[pos].a_perm = permfromstr (c)) == 01000)
+ {
+ set_errno (EINVAL);
+ return NULL;
+ }
+ ++pos;
+ }
+ aclent_t *aclp = (aclent_t *) malloc (pos * sizeof (aclent_t));
+ if (aclp)
+ memcpy (aclp, lacl, pos * sizeof (aclent_t));
+ return aclp;
+}
+