libc/winsup/cygwin/security.cc

2054 lines
54 KiB
C++

/* 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;
}
/* read_sd 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 sd_buf.
sd_size contains the size of the buffer. If
it's too small, to contain the complete SD, 0 is
returned and sd_size is set to the needed size
of the buffer.
*/
LONG
read_sd(const char *file, PSECURITY_DESCRIPTOR sd_buf, LPDWORD sd_size)
{
/* Check parameters */
if (! sd_size)
{
set_errno (EINVAL);
return -1;
}
debug_printf("file = %s", file);
DWORD len = 0;
if (! GetFileSecurity (file,
OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| DACL_SECURITY_INFORMATION,
sd_buf, *sd_size, &len))
{
__seterrno ();
return -1;
}
if (len > *sd_size)
{
*sd_size = len;
return 0;
}
return len;
}
LONG
write_sd(const char *file, PSECURITY_DESCRIPTOR sd_buf, DWORD sd_size)
{
/* Check parameters */
if (! sd_buf || ! sd_size)
{
set_errno (EINVAL);
return -1;
}
HANDLE fh;
fh = 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 (fh == 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 = sd_size;
header.dwStreamNameSize = 0;
if (!BackupWrite (fh, (LPBYTE) &header,
3 * sizeof (DWORD) + sizeof (LARGE_INTEGER),
&bytes_written, FALSE, TRUE, &context))
{
__seterrno ();
CloseHandle (fh);
return -1;
}
/* write new security descriptor */
if (!BackupWrite (fh, (LPBYTE) sd_buf,
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 (fh, NULL, 0, &bytes_written, TRUE, TRUE, &context);
CloseHandle (fh);
return -1;
}
}
/* terminate the restore process */
BackupWrite (fh, NULL, 0, &bytes_written, TRUE, TRUE, &context);
CloseHandle (fh);
return 0;
}
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,
uid_t *uidret, gid_t *gidret)
{
if (os_being_run != winNT)
return 0;
syscall_printf ("file: %s", file);
/* 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 = read_sd (file, psd, &sd_size)) <= 0)
{
debug_printf ("read_sd %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;
}
uid_t uid = get_uid_from_sid (owner_sid);
gid_t gid = get_gid_from_sid (group_sid);
if (uidret)
*uidret = uid;
if (gidret)
*gidret = gid;
if (! attribute)
{
syscall_printf ("file: %s uid %d, gid %d", uid, gid);
return 0;
}
BOOL grp_member = is_grp_member (uid, gid);
if (! acl_exists || ! acl)
{
*attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
syscall_printf ("file: %s No ACL = %x, uid %d, gid %d",
file, *attribute, uid, gid);
return 0;
}
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, uid %d, gid %d", file, *attribute, uid, gid);
return 0;
}
int
get_file_attribute (int use_ntsec, const char *file,
int *attribute, uid_t *uidret, gid_t *gidret)
{
if (use_ntsec && allow_ntsec)
return get_nt_attribute (file, attribute, uidret, gidret);
if (uidret)
*uidret = getuid ();
if (gidret)
*gidret = getgid ();
if (! attribute)
return 0;
int 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)
set_errno (ENOSYS);
return res > 0 ? 0 : -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_WRITE | 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_WRITE | 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;
DWORD sd_size = 4096;
char sd_buf[4096];
PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) sd_buf;
int ret;
if ((ret = read_sd (file, psd, &sd_size)) <= 0)
{
debug_printf ("read_sd %E");
return ret;
}
sd_size = 4096;
if (! (psd = alloc_sd (uid, gid, logsrv, attribute, psd, &sd_size)))
return -1;
return write_sd (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;
if (!use_ntsec || !allow_ntsec)
{
if (! NTWriteEA (file, ".UNIXATTR",
(char *) &attribute, sizeof (attribute)))
{
__seterrno ();
return -1;
}
return 0;
}
int ret = set_nt_attribute (file, uid, gid, logsrv, attribute);
syscall_printf ("%d = set_file_attribute (%s, %d, %d, %p)",
ret, file, uid, gid, attribute);
return ret;
}
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 (read_sd (file, psd, &sd_size) <= 0)
{
debug_printf ("read_sd %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 write_sd (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 = read_sd (file, psd, &sd_size)) <= 0)
{
debug_printf ("read_sd %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;
}
static
int
acl_worker (const char *path, int cmd, int nentries, aclent_t *aclbufp,
int nofollow)
{
path_conv real_path (path, nofollow ? SYMLINK_NOFOLLOW : SYMLINK_FOLLOW, 1);
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 ((nofollow && ! lstat (path, &st))
|| (!nofollow && ! 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
acl (const char *path, int cmd, int nentries, aclent_t *aclbufp)
{
return acl_worker (path, cmd, nentries, aclbufp, 0);
}
extern "C"
int
lacl (const char *path, int cmd, int nentries, aclent_t *aclbufp)
{
return acl_worker (path, cmd, nentries, aclbufp, 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_worker (path, cmd, nentries, aclbufp, 0);
}
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, 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 *)
{
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;
}