Skip to content
Snippets Groups Projects
Commit cb124c9b authored by Robert Shearman's avatar Robert Shearman Committed by Alexandre Julliard
Browse files

server: Add primitive support for setting and getting the security descriptor...

server: Add primitive support for setting and getting the security descriptor of files based on their Unix permissions.
parent c1707d89
No related branches found
No related tags found
No related merge requests found
......@@ -52,6 +52,8 @@
#include "handle.h"
#include "thread.h"
#include "request.h"
#include "process.h"
#include "security.h"
struct file
{
......@@ -59,12 +61,15 @@ struct file
struct fd *fd; /* file descriptor for this file */
unsigned int access; /* file access (FILE_READ_DATA etc.) */
mode_t mode; /* file stat.st_mode */
uid_t uid; /* file stat.st_uid */
};
static unsigned int generic_file_map_access( unsigned int access );
static void file_dump( struct object *obj, int verbose );
static struct fd *file_get_fd( struct object *obj );
static struct security_descriptor *file_get_sd( struct object *obj );
static int file_set_sd( struct object *obj, const struct security_descriptor *sd, unsigned int set_info );
static void file_destroy( struct object *obj );
static int file_get_poll_events( struct fd *fd );
......@@ -82,8 +87,8 @@ static const struct object_ops file_ops =
no_signal, /* signal */
file_get_fd, /* get_fd */
default_fd_map_access, /* map_access */
default_get_sd, /* get_sd */
default_set_sd, /* set_sd */
file_get_sd, /* get_sd */
file_set_sd, /* set_sd */
no_lookup_name, /* lookup_name */
no_open_file, /* open_file */
fd_close_handle, /* close_handle */
......@@ -269,6 +274,248 @@ static unsigned int generic_file_map_access( unsigned int access )
return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
}
static struct security_descriptor *file_get_sd( struct object *obj )
{
struct file *file = (struct file *)obj;
struct stat st;
int unix_fd;
struct security_descriptor *sd;
const SID *user;
const SID *group;
size_t dacl_size;
ACCESS_ALLOWED_ACE *aaa;
ACL *dacl;
SID *sid;
char *ptr;
const SID *world_sid = security_world_sid;
const SID *local_system_sid = security_local_system_sid;
assert( obj->ops == &file_ops );
unix_fd = get_file_unix_fd( file );
if (unix_fd == -1) return obj->sd;
if (fstat( unix_fd, &st ) == -1)
return obj->sd;
/* mode and uid the same? if so, no need to re-generate security descriptor */
if (obj->sd && (st.st_mode & (S_IRWXU|S_IRWXO)) == (file->mode & (S_IRWXU|S_IRWXO)) &&
(st.st_uid == file->uid))
return obj->sd;
user = security_unix_uid_to_sid( st.st_uid );
group = token_get_primary_group( current->process->token );
dacl_size = sizeof(ACL) + FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) +
FIELD_OFFSET(SID, SubAuthority[local_system_sid->SubAuthorityCount]);
if (st.st_mode & S_IRWXU)
dacl_size += FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) +
FIELD_OFFSET(SID, SubAuthority[user->SubAuthorityCount]);
if (st.st_mode & S_IRWXO)
dacl_size += FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) +
FIELD_OFFSET(SID, SubAuthority[world_sid->SubAuthorityCount]);
sd = mem_alloc( sizeof(struct security_descriptor) +
FIELD_OFFSET(SID, SubAuthority[user->SubAuthorityCount]) +
FIELD_OFFSET(SID, SubAuthority[group->SubAuthorityCount]) +
dacl_size );
if (!sd) return obj->sd;
sd->control = SE_DACL_PRESENT;
sd->owner_len = FIELD_OFFSET(SID, SubAuthority[user->SubAuthorityCount]);
sd->group_len = FIELD_OFFSET(SID, SubAuthority[group->SubAuthorityCount]);
sd->sacl_len = 0;
sd->dacl_len = dacl_size;
ptr = (char *)(sd + 1);
memcpy( ptr, user, sd->owner_len );
ptr += sd->owner_len;
memcpy( ptr, group, sd->group_len );
ptr += sd->group_len;
dacl = (ACL *)ptr;
dacl->AclRevision = ACL_REVISION;
dacl->Sbz1 = 0;
dacl->AclSize = dacl_size;
dacl->AceCount = 1 + (st.st_mode & S_IRWXU ? 1 : 0) + (st.st_mode & S_IRWXO ? 1 : 0);
dacl->Sbz2 = 0;
/* always give FILE_ALL_ACCESS for Local System */
aaa = (ACCESS_ALLOWED_ACE *)(dacl + 1);
aaa->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
aaa->Header.AceFlags = 0;
aaa->Header.AceSize = FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) +
FIELD_OFFSET(SID, SubAuthority[local_system_sid->SubAuthorityCount]);
aaa->Mask = FILE_ALL_ACCESS;
sid = (SID *)&aaa->SidStart;
memcpy( sid, local_system_sid, FIELD_OFFSET(SID, SubAuthority[local_system_sid->SubAuthorityCount]) );
if (st.st_mode & S_IRWXU)
{
/* appropriate access rights for the user */
aaa = (ACCESS_ALLOWED_ACE *)ace_next( &aaa->Header );
aaa->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
aaa->Header.AceFlags = 0;
aaa->Header.AceSize = FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) +
FIELD_OFFSET(SID, SubAuthority[user->SubAuthorityCount]);
aaa->Mask = WRITE_DAC | WRITE_OWNER;
if (st.st_mode & S_IRUSR)
aaa->Mask |= FILE_GENERIC_READ;
if (st.st_mode & S_IWUSR)
aaa->Mask |= FILE_GENERIC_WRITE | DELETE;
if (st.st_mode & S_IXUSR)
aaa->Mask |= FILE_GENERIC_EXECUTE;
sid = (SID *)&aaa->SidStart;
memcpy( sid, user, FIELD_OFFSET(SID, SubAuthority[user->SubAuthorityCount]) );
}
if (st.st_mode & S_IRWXO)
{
/* appropriate access rights for Everyone */
aaa = (ACCESS_ALLOWED_ACE *)ace_next( &aaa->Header );
aaa->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
aaa->Header.AceFlags = 0;
aaa->Header.AceSize = FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) +
FIELD_OFFSET(SID, SubAuthority[world_sid->SubAuthorityCount]);
aaa->Mask = 0;
if (st.st_mode & S_IROTH)
aaa->Mask |= FILE_GENERIC_READ;
if (st.st_mode & S_IWOTH)
aaa->Mask |= FILE_GENERIC_WRITE | DELETE;
if (st.st_mode & S_IXOTH)
aaa->Mask |= FILE_GENERIC_EXECUTE;
sid = (SID *)&aaa->SidStart;
memcpy( sid, world_sid, FIELD_OFFSET(SID, SubAuthority[world_sid->SubAuthorityCount]) );
}
file->mode = st.st_mode;
file->uid = st.st_uid;
free( obj->sd );
obj->sd = sd;
return sd;
}
static int file_set_sd( struct object *obj, const struct security_descriptor *sd,
unsigned int set_info )
{
struct file *file = (struct file *)obj;
mode_t new_mode;
mode_t denied_mode = 0;
const SID *owner;
int unix_fd;
assert( obj->ops == &file_ops );
/* only DACL translation is currently supported */
if (!(set_info & DACL_SECURITY_INFORMATION))
return 1;
unix_fd = get_file_unix_fd( file );
if (unix_fd == -1) return 1;
if (set_info & OWNER_SECURITY_INFORMATION)
{
owner = sd_get_owner( sd );
if (!owner)
{
set_error( STATUS_INVALID_SECURITY_DESCR );
return 0;
}
if (!obj->sd || !security_equal_sid( owner, sd_get_owner( obj->sd ) ))
{
/* FIXME: get Unix uid and call fchown */
}
}
else if (obj->sd)
owner = sd_get_owner( obj->sd );
else
owner = token_get_user( current->process->token );
/* keep the bits that we don't map to access rights in the ACL */
new_mode = file->mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXG);
if (set_info & DACL_SECURITY_INFORMATION)
{
if (sd->control & SE_DACL_PRESENT)
{
const ACL *dacl = (const ACL *)((char *)sd + sd->owner_len + sd->group_len + sd->sacl_len);
const ACE_HEADER *ace = (const ACE_HEADER *)(dacl + 1);
ULONG i;
for (i = 0; i < dacl->AceCount; i++)
{
const ACCESS_ALLOWED_ACE *aa_ace;
const ACCESS_DENIED_ACE *ad_ace;
const SID *sid;
switch (ace->AceType)
{
case ACCESS_DENIED_ACE_TYPE:
ad_ace = (const ACCESS_DENIED_ACE *)ace;
sid = (const SID *)&ad_ace->SidStart;
if (security_equal_sid( sid, security_world_sid ))
{
unsigned int access = generic_file_map_access( ad_ace->Mask );
if (access & FILE_READ_DATA)
denied_mode |= S_IROTH;
if (access & FILE_WRITE_DATA)
denied_mode |= S_IWOTH;
if (access & FILE_EXECUTE)
denied_mode |= S_IXOTH;
}
else if (security_equal_sid( sid, owner ))
{
unsigned int access = generic_file_map_access( ad_ace->Mask );
if (access & FILE_READ_DATA)
denied_mode |= S_IRUSR;
if (access & FILE_WRITE_DATA)
denied_mode |= S_IWUSR;
if (access & FILE_EXECUTE)
denied_mode |= S_IXUSR;
}
break;
case ACCESS_ALLOWED_ACE_TYPE:
aa_ace = (const ACCESS_ALLOWED_ACE *)ace;
sid = (const SID *)&aa_ace->SidStart;
if (security_equal_sid( sid, security_world_sid ))
{
unsigned int access = generic_file_map_access( aa_ace->Mask );
if (access & FILE_READ_DATA)
new_mode |= S_IROTH;
if (access & FILE_WRITE_DATA)
new_mode |= S_IWOTH;
if (access & FILE_EXECUTE)
new_mode |= S_IXOTH;
}
else if (security_equal_sid( sid, owner ))
{
unsigned int access = generic_file_map_access( aa_ace->Mask );
if (access & FILE_READ_DATA)
new_mode |= S_IRUSR;
if (access & FILE_WRITE_DATA)
new_mode |= S_IWUSR;
if (access & FILE_EXECUTE)
new_mode |= S_IXUSR;
}
break;
}
ace = ace_next( ace );
}
}
else
/* no ACL means full access rights to anyone */
new_mode |= S_IRWXU | S_IRWXO;
if (fchmod( unix_fd, new_mode & ~denied_mode ) == -1)
{
file_set_error();
return 0;
}
file->mode = new_mode & ~denied_mode;
}
return 1;
}
static void file_destroy( struct object *obj )
{
struct file *file = (struct file *)obj;
......
......@@ -39,7 +39,9 @@ extern const LUID SeManageVolumePrivilege;
extern const LUID SeImpersonatePrivilege;
extern const LUID SeCreateGlobalPrivilege;
extern const PSID security_world_sid;
extern const PSID security_interactive_sid;
extern const PSID security_local_system_sid;
/* token functions */
......@@ -54,6 +56,17 @@ extern const ACL *token_get_default_dacl( struct token *token );
extern const SID *token_get_user( struct token *token );
extern const SID *token_get_primary_group( struct token *token );
static inline const ACE_HEADER *ace_next( const ACE_HEADER *ace )
{
return (const ACE_HEADER *)((const char *)ace + ace->AceSize);
}
static inline int security_equal_sid( const SID *sid1, const SID *sid2 )
{
return ((sid1->SubAuthorityCount == sid2->SubAuthorityCount) &&
!memcmp( sid1, sid2, FIELD_OFFSET(SID, SubAuthority[sid1->SubAuthorityCount]) ));
}
extern void security_set_thread_token( struct thread *thread, obj_handle_t handle );
extern const SID *security_unix_uid_to_sid( uid_t uid );
extern int check_object_access( struct object *obj, unsigned int *access );
......
......@@ -70,11 +70,11 @@ static const SID interactive_sid = { SID_REVISION, 1, { SECURITY_NT_AUTHORITY },
static const SID anonymous_logon_sid = { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_ANONYMOUS_LOGON_RID } };
static const SID authenticated_user_sid = { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_AUTHENTICATED_USER_RID } };
static const SID local_system_sid = { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_LOCAL_SYSTEM_RID } };
static const PSID security_world_sid = (PSID)&world_sid;
const PSID security_world_sid = (PSID)&world_sid;
static const PSID security_local_sid = (PSID)&local_sid;
const PSID security_interactive_sid = (PSID)&interactive_sid;
static const PSID security_authenticated_user_sid = (PSID)&authenticated_user_sid;
static const PSID security_local_system_sid = (PSID)&local_system_sid;
const PSID security_local_system_sid = (PSID)&local_system_sid;
static luid_t prev_luid_value = { 1000, 0 };
......@@ -166,12 +166,6 @@ static SID *security_sid_alloc( const SID_IDENTIFIER_AUTHORITY *idauthority, int
return sid;
}
static inline int security_equal_sid( const SID *sid1, const SID *sid2 )
{
return ((sid1->SubAuthorityCount == sid2->SubAuthorityCount) &&
!memcmp( sid1, sid2, FIELD_OFFSET(SID, SubAuthority[sid1->SubAuthorityCount]) ));
}
void security_set_thread_token( struct thread *thread, obj_handle_t handle )
{
if (!handle)
......@@ -195,11 +189,6 @@ void security_set_thread_token( struct thread *thread, obj_handle_t handle )
}
}
static const ACE_HEADER *ace_next( const ACE_HEADER *ace )
{
return (const ACE_HEADER *)((const char *)ace + ace->AceSize);
}
const SID *security_unix_uid_to_sid( uid_t uid )
{
/* very simple mapping: either the current user or not the current user */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment