From 321aeff0faee73e2988842af5d4fcb490259ace7 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Fri, 9 Aug 2013 20:57:16 -0600 Subject: server: Store and return security attributes with extended file attributes. --- configure.ac | 1 + dlls/advapi32/tests/security.c | 9 +- dlls/kernel32/tests/directory.c | 15 ++-- server/change.c | 11 ++- server/fd.c | 68 ++++++++++++++- server/file.c | 176 ++++++++++++++++++++++++++++++++++++++- server/file.h | 5 +- 7 files changed, 263 insertions(+), 22 deletions(-) diff --git a/configure.ac b/configure.ac index 184505a..879a645 100644 --- a/configure.ac +++ b/configure.ac @@ -409,6 +409,7 @@ AC_CHECK_HEADERS(\ arpa/inet.h \ arpa/nameser.h \ asm/types.h \ + attr/xattr.h \ curses.h \ direct.h \ dirent.h \ diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c index c622bb2..4c9e8d4 100644 --- a/dlls/advapi32/tests/security.c +++ b/dlls/advapi32/tests/security.c @@ -3166,7 +3166,7 @@ static void test_GetNamedSecurityInfoA(void) bret = pGetAce(pDacl, 0, (VOID **)&ace); ok(bret, "Failed to get Current User ACE.\n"); bret = EqualSid(&ace->SidStart, user_sid); - todo_wine ok(bret, "Current User ACE != Current User SID.\n"); + ok(bret, "Current User ACE != Current User SID.\n"); ok(((ACE_HEADER *)ace)->AceFlags == 0, "Current User ACE has unexpected flags (0x%x != 0x0)\n", ((ACE_HEADER *)ace)->AceFlags); ok(ace->Mask == 0x1f01ff, "Current User ACE has unexpected mask (0x%x != 0x1f01ff)\n", @@ -3177,8 +3177,7 @@ static void test_GetNamedSecurityInfoA(void) bret = pGetAce(pDacl, 1, (VOID **)&ace); ok(bret, "Failed to get Administators Group ACE.\n"); bret = EqualSid(&ace->SidStart, admin_sid); - todo_wine ok(bret || broken(!bret) /* win2k */, - "Administators Group ACE != Administators Group SID.\n"); + ok(bret || broken(!bret) /* win2k */, "Administators Group ACE != Administators Group SID.\n"); ok(((ACE_HEADER *)ace)->AceFlags == 0, "Administators Group ACE has unexpected flags (0x%x != 0x0)\n", ((ACE_HEADER *)ace)->AceFlags); ok(ace->Mask == 0x1f01ff || broken(ace->Mask == GENERIC_ALL) /* win2k */, @@ -3832,7 +3831,7 @@ static void test_GetSecurityInfo(void) bret = pGetAce(pDacl, 0, (VOID **)&ace); ok(bret, "Failed to get Current User ACE.\n"); bret = EqualSid(&ace->SidStart, user_sid); - todo_wine ok(bret, "Current User ACE != Current User SID.\n"); + ok(bret, "Current User ACE != Current User SID.\n"); ok(((ACE_HEADER *)ace)->AceFlags == 0, "Current User ACE has unexpected flags (0x%x != 0x0)\n", ((ACE_HEADER *)ace)->AceFlags); ok(ace->Mask == 0x1f01ff, "Current User ACE has unexpected mask (0x%x != 0x1f01ff)\n", @@ -3843,7 +3842,7 @@ static void test_GetSecurityInfo(void) bret = pGetAce(pDacl, 1, (VOID **)&ace); ok(bret, "Failed to get Administators Group ACE.\n"); bret = EqualSid(&ace->SidStart, admin_sid); - todo_wine ok(bret, "Administators Group ACE != Administators Group SID.\n"); + ok(bret, "Administators Group ACE != Administators Group SID.\n"); ok(((ACE_HEADER *)ace)->AceFlags == 0, "Administators Group ACE has unexpected flags (0x%x != 0x0)\n", ((ACE_HEADER *)ace)->AceFlags); ok(ace->Mask == 0x1f01ff, "Administators Group ACE has unexpected mask (0x%x != 0x1f01ff)\n", diff --git a/dlls/kernel32/tests/directory.c b/dlls/kernel32/tests/directory.c index df434b6..a8dfa81 100644 --- a/dlls/kernel32/tests/directory.c +++ b/dlls/kernel32/tests/directory.c @@ -580,10 +580,9 @@ static void test_security_attributes(void) bret = pGetAce(pDacl, 0, (VOID **)&ace); ok(bret, "Failed to get Current User ACE.\n"); bret = EqualSid(&ace->SidStart, user_sid); - todo_wine ok(bret, "Current User ACE != Current User SID.\n"); - todo_wine ok(((ACE_HEADER *)ace)->AceFlags == (OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE), - "Current User ACE has unexpected flags (0x%x != 0x03)\n", - ((ACE_HEADER *)ace)->AceFlags); + ok(bret, "Current User ACE != Current User SID.\n"); + ok(((ACE_HEADER *)ace)->AceFlags == (OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE), + "Current User ACE has unexpected flags (0x%x != 0x03)\n", ((ACE_HEADER *)ace)->AceFlags); ok(ace->Mask == 0x1f01ff, "Current User ACE has unexpected mask (0x%x != 0x1f01ff)\n", ace->Mask); } @@ -592,10 +591,10 @@ static void test_security_attributes(void) bret = pGetAce(pDacl, 1, (VOID **)&ace); ok(bret, "Failed to get Administators Group ACE.\n"); bret = EqualSid(&ace->SidStart, admin_sid); - todo_wine ok(bret, "Administators Group ACE != Administators Group SID.\n"); - todo_wine ok(((ACE_HEADER *)ace)->AceFlags == (OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE), - "Administators Group ACE has unexpected flags (0x%x != 0x03)\n", - ((ACE_HEADER *)ace)->AceFlags); + ok(bret, "Administators Group ACE != Administators Group SID.\n"); + ok(((ACE_HEADER *)ace)->AceFlags == (OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE), + "Administators Group ACE has unexpected flags (0x%x != 0x03)\n", + ((ACE_HEADER *)ace)->AceFlags); ok(ace->Mask == 0x1f01ff, "Administators Group ACE has unexpected mask (0x%x != 0x1f01ff)\n", ace->Mask); } diff --git a/server/change.c b/server/change.c index f6d56b0..022c780 100644 --- a/server/change.c +++ b/server/change.c @@ -286,6 +286,7 @@ static int get_dir_unix_fd( struct dir *dir ) static struct security_descriptor *dir_get_sd( struct object *obj ) { struct dir *dir = (struct dir *)obj; + const SID *user, *group; int unix_fd; struct stat st; struct security_descriptor *sd; @@ -302,9 +303,11 @@ static struct security_descriptor *dir_get_sd( struct object *obj ) (st.st_uid == dir->uid)) return obj->sd; - sd = mode_to_sd( st.st_mode, - security_unix_uid_to_sid( st.st_uid ), - token_get_primary_group( current->process->token )); + user = security_unix_uid_to_sid( st.st_uid ); + group = token_get_primary_group( current->process->token ); + sd = get_file_acls( unix_fd, user, group ); + if (!sd) + sd = mode_to_sd( st.st_mode, user, group ); if (!sd) return obj->sd; dir->mode = st.st_mode; @@ -353,6 +356,8 @@ static int dir_set_sd( struct object *obj, const struct security_descriptor *sd, mode = st.st_mode & (S_ISUID|S_ISGID|S_ISVTX); mode |= sd_to_mode( sd, owner ); + set_file_acls( unix_fd, sd ); + if (((st.st_mode ^ mode) & (S_IRWXU|S_IRWXG|S_IRWXO)) && fchmod( unix_fd, mode ) == -1) { file_set_error(); diff --git a/server/fd.c b/server/fd.c index 248f15a..687d4a9 100644 --- a/server/fd.c +++ b/server/fd.c @@ -88,6 +88,9 @@ #include #include #include +#ifdef HAVE_ATTR_XATTR_H +#include +#endif #include "ntstatus.h" #define WIN32_NO_STATUS @@ -96,6 +99,7 @@ #include "handle.h" #include "process.h" #include "request.h" +#include "security.h" #include "winternl.h" #include "winioctl.h" @@ -1723,9 +1727,69 @@ static char *dup_fd_name( struct fd *root, const char *name ) return ret; } +void set_file_acls( int fd, const struct security_descriptor *sd ) +{ +#ifdef HAVE_ATTR_XATTR_H + char buffer[XATTR_SIZE_MAX], *p = buffer; + const ACE_HEADER *ace; + int present, i, j, n; + const ACL *dacl; + + if (!sd) return; + dacl = sd_get_dacl( sd, &present ); + if (!present || !dacl) return; + ace = (const ACE_HEADER *)(dacl + 1); + + for (i = 0; i < dacl->AceCount; i++, ace = ace_next( ace )) + { + BYTE type = ace->AceType, flags; + const ACCESS_ALLOWED_ACE *aaa; + const ACCESS_DENIED_ACE *ada; + char sidtxt[100], *s; + const SID *sid; + DWORD mask; + + if (type & INHERIT_ONLY_ACE) continue; + + switch (type) + { + case ACCESS_DENIED_ACE_TYPE: + ada = (const ACCESS_DENIED_ACE *)ace; + flags = ada->Header.AceFlags; + mask = ada->Mask; + sid = (const SID *)&ada->SidStart; + break; + case ACCESS_ALLOWED_ACE_TYPE: + aaa = (const ACCESS_ALLOWED_ACE *)ace; + flags = aaa->Header.AceFlags; + mask = aaa->Mask; + sid = (const SID *)&aaa->SidStart; + break; + default: + continue; + } + n = sprintf( sidtxt, "S-%u-%d", sid->Revision, + MAKELONG( + MAKEWORD( sid->IdentifierAuthority.Value[5], + sid->IdentifierAuthority.Value[4] ), + MAKEWORD( sid->IdentifierAuthority.Value[3], + sid->IdentifierAuthority.Value[2] ) + ) ); + s = sidtxt + n; + for( j=0; jSubAuthorityCount; j++ ) + s += sprintf( s, "-%u", sid->SubAuthority[j] ); + + p += snprintf( p, XATTR_SIZE_MAX-(p-buffer), "%s%x,%x,%x,%s", + (p != buffer ? ";" : ""), type, flags, mask, sidtxt ); + } + + fsetxattr( fd, "user.wine.acl", buffer, p-buffer, 0 ); +#endif +} + /* open() wrapper that returns a struct fd with no fd user set */ struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode, unsigned int access, - unsigned int sharing, unsigned int options ) + unsigned int sharing, unsigned int options, const struct security_descriptor *sd ) { struct stat st; struct closed_fd *closed_fd; @@ -1801,6 +1865,8 @@ struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode, } } + set_file_acls( fd->unix_fd, sd ); + closed_fd->unix_fd = fd->unix_fd; closed_fd->unlink[0] = 0; fstat( fd->unix_fd, &st ); diff --git a/server/file.c b/server/file.c index 9c6cb80..f4d97fd 100644 --- a/server/file.c +++ b/server/file.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #ifdef HAVE_UTIME_H #include @@ -39,6 +40,9 @@ #ifdef HAVE_POLL_H #include #endif +#ifdef HAVE_ATTR_XATTR_H +#include +#endif #include "ntstatus.h" #define WIN32_NO_STATUS @@ -240,7 +244,7 @@ static struct object *create_file( struct fd *root, const char *nameptr, data_si access = generic_file_map_access( access ); /* FIXME: should set error to STATUS_OBJECT_NAME_COLLISION if file existed before */ - fd = open_fd( root, name, flags | O_NONBLOCK | O_LARGEFILE, &mode, access, sharing, options ); + fd = open_fd( root, name, flags | O_NONBLOCK | O_LARGEFILE, &mode, access, sharing, options, sd ); if (!fd) goto done; if (S_ISDIR(mode)) @@ -427,9 +431,169 @@ struct security_descriptor *mode_to_sd( mode_t mode, const SID *user, const SID return sd; } +struct security_descriptor *get_file_acls( int fd, const SID *user, const SID *group ) +{ +#ifdef HAVE_ATTR_XATTR_H + int ace_count = 0, dacl_size = sizeof(ACL), i, n; + char buffer[XATTR_SIZE_MAX], *p = buffer, *pn; + struct security_descriptor *sd; + ACE_HEADER *current_ace; + ACCESS_ALLOWED_ACE *aaa; + ACCESS_DENIED_ACE *ada; + int type, flags, mask; + ACL *dacl; + char *ptr; + + n = fgetxattr( fd, "user.wine.acl", buffer, sizeof(buffer) ); + if (n == -1) return NULL; + buffer[n] = 0; + + do + { + int sub_authority_count = 0; + + pn = strchr(p, ';'); + if (pn) pn++; + sscanf(p, "%x", &type); + do + { + p = strchr(p, '-'); + if (p) p++; + sub_authority_count++; + } + while(p && (!pn || p < pn)); + sub_authority_count -= 3; /* Revision and IdentifierAuthority don't count */ + + switch (type) + { + case ACCESS_DENIED_ACE_TYPE: + dacl_size += FIELD_OFFSET(ACCESS_DENIED_ACE, SidStart) + + FIELD_OFFSET(SID, SubAuthority[sub_authority_count]); + break; + case ACCESS_ALLOWED_ACE_TYPE: + dacl_size += FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + + FIELD_OFFSET(SID, SubAuthority[sub_authority_count]); + break; + default: + continue; + } + ace_count++; + p = pn; + } + while(p); + + sd = mem_alloc( sizeof(struct security_descriptor) + + FIELD_OFFSET(SID, SubAuthority[user->SubAuthorityCount]) + + FIELD_OFFSET(SID, SubAuthority[group->SubAuthorityCount]) + + dacl_size ); + + 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 = ace_count; + dacl->Sbz2 = 0; + aaa = (ACCESS_ALLOWED_ACE *)(dacl + 1); + current_ace = &aaa->Header; + + p = buffer; + for(i=0; iHeader; + } + pn = strchr(p, ';'); + if (pn) pn++; + sscanf(p, "%x,%x,%x,%[^;]", &type, &flags, &mask, sidtxt); + sscanf(sidtxt, "S-%u-%d", &rev, &ia); + sid->Revision = rev; + sid->IdentifierAuthority.Value[0] = 0; + sid->IdentifierAuthority.Value[1] = 0; + sid->IdentifierAuthority.Value[2] = HIBYTE(HIWORD(ia)); + sid->IdentifierAuthority.Value[3] = LOBYTE(HIWORD(ia)); + sid->IdentifierAuthority.Value[4] = HIBYTE(LOWORD(ia)); + sid->IdentifierAuthority.Value[5] = LOBYTE(LOWORD(ia)); + p = strchr(sidtxt, '-')+1; + p = strchr(p, '-')+1; /* Revision doesn't count */ + p = strchr(p, '-')+1; /* IdentifierAuthority doesn't count */ + do + { + sscanf(p, "%u", &sa); + sid->SubAuthority[sub_authority_count] = sa; + p = strchr(p, '-'); + if (p) p++; + sub_authority_count++; + } + while(p); + sid->SubAuthorityCount = sub_authority_count; + + /* Convert generic rights into standard access rights */ + if (mask & GENERIC_ALL) + mask |= WRITE_DAC | WRITE_OWNER | DELETE | FILE_DELETE_CHILD; + if (mask & (GENERIC_ALL|GENERIC_READ)) + mask |= FILE_GENERIC_READ; + if (mask & (GENERIC_ALL|GENERIC_WRITE)) + mask |= FILE_GENERIC_WRITE; + if (mask & (GENERIC_ALL|GENERIC_EXECUTE)) + mask |= FILE_GENERIC_EXECUTE; + mask &= 0x0FFFFFFF; + + /* Handle the specific ACE */ + switch (type) + { + case ACCESS_DENIED_ACE_TYPE: + ada = (ACCESS_DENIED_ACE *)aaa; + ada->Header.AceType = type; + ada->Header.AceFlags = flags; + ada->Header.AceSize = FIELD_OFFSET(ACCESS_DENIED_ACE, SidStart) + + FIELD_OFFSET(SID, SubAuthority[sid->SubAuthorityCount]); + ada->Mask = mask; + memcpy( &ada->SidStart, sid, FIELD_OFFSET(SID, SubAuthority[sid->SubAuthorityCount]) ); + break; + case ACCESS_ALLOWED_ACE_TYPE: + aaa->Header.AceType = type; + aaa->Header.AceFlags = flags; + aaa->Header.AceSize = FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + + FIELD_OFFSET(SID, SubAuthority[sid->SubAuthorityCount]); + aaa->Mask = mask; + memcpy( &aaa->SidStart, sid, FIELD_OFFSET(SID, SubAuthority[sid->SubAuthorityCount]) ); + break; + default: + continue; + } + p = pn; + } + + return sd; +#else + return NULL; +#endif +} + static struct security_descriptor *file_get_sd( struct object *obj ) { struct file *file = (struct file *)obj; + const SID *user, *group; struct stat st; int unix_fd; struct security_descriptor *sd; @@ -446,9 +610,11 @@ static struct security_descriptor *file_get_sd( struct object *obj ) (st.st_uid == file->uid)) return obj->sd; - sd = mode_to_sd( st.st_mode, - security_unix_uid_to_sid( st.st_uid ), - token_get_primary_group( current->process->token )); + user = security_unix_uid_to_sid( st.st_uid ); + group = token_get_primary_group( current->process->token ); + sd = get_file_acls( unix_fd, user, group ); + if (!sd) + sd = mode_to_sd( st.st_mode, user, group); if (!sd) return obj->sd; file->mode = st.st_mode; @@ -578,6 +744,8 @@ static int file_set_sd( struct object *obj, const struct security_descriptor *sd mode = st.st_mode & (S_ISUID|S_ISGID|S_ISVTX); mode |= sd_to_mode( sd, owner ); + set_file_acls( unix_fd, sd ); + if (((st.st_mode ^ mode) & (S_IRWXU|S_IRWXG|S_IRWXO)) && fchmod( unix_fd, mode ) == -1) { file_set_error(); diff --git a/server/file.h b/server/file.h index ead356d..77737e8 100644 --- a/server/file.h +++ b/server/file.h @@ -56,7 +56,8 @@ extern struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct obje unsigned int options ); extern void set_no_fd_status( struct fd *fd, unsigned int status ); extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode, - unsigned int access, unsigned int sharing, unsigned int options ); + unsigned int access, unsigned int sharing, unsigned int options, + const struct security_descriptor *sd ); extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, struct object *user, unsigned int options ); extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing, @@ -122,6 +123,8 @@ extern struct file *create_file_for_fd_obj( struct fd *fd, unsigned int access, extern void file_set_error(void); extern struct security_descriptor *mode_to_sd( mode_t mode, const SID *user, const SID *group ); extern mode_t sd_to_mode( const struct security_descriptor *sd, const SID *owner ); +extern void set_file_acls( int fd, const struct security_descriptor *sd ); +extern struct security_descriptor *get_file_acls( int fd, const SID *user, const SID *group ); /* file mapping functions */ -- 1.7.9.5