diff options
Diffstat (limited to 'winsup/cygwin/security.cc')
-rw-r--r-- | winsup/cygwin/security.cc | 2084 |
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; +} + |