wine/0004-server-Store-and-return-security-attributes-with-ext.patch
2013-12-24 21:29:00 +04:00

499 lines
20 KiB
Diff

From 78a5ebb94f3717bedd77f7c096e8fce1e9a640d1 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Mon, 18 Nov 2013 18:12:05 -0700
Subject: server: Store and return security attributes with extended file
attributes.
---
configure.ac | 6 ++
dlls/advapi32/tests/security.c | 25 +++---
server/change.c | 11 ++-
server/fd.c | 68 +++++++++++++++-
server/file.c | 176 +++++++++++++++++++++++++++++++++++++++-
server/file.h | 5 +-
6 files changed, 269 insertions(+), 22 deletions(-)
diff --git a/configure.ac b/configure.ac
index 98a73f2..bb03667 100644
--- a/configure.ac
+++ b/configure.ac
@@ -71,6 +71,7 @@ AC_ARG_WITH(pthread, AS_HELP_STRING([--without-pthread],[do not use the pthrea
AC_ARG_WITH(sane, AS_HELP_STRING([--without-sane],[do not use SANE (scanner support)]))
AC_ARG_WITH(tiff, AS_HELP_STRING([--without-tiff],[do not use TIFF]))
AC_ARG_WITH(v4l, AS_HELP_STRING([--without-v4l],[do not use v4l1 (v4l support)]))
+AC_ARG_WITH(xattr, AS_HELP_STRING([--without-xattr],[do not use xattr (security attributes support)]))
AC_ARG_WITH(xcomposite,AS_HELP_STRING([--without-xcomposite],[do not use the Xcomposite extension]),
[if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xcomposite_h=no; fi])
AC_ARG_WITH(xcursor, AS_HELP_STRING([--without-xcursor],[do not use the Xcursor extension]),
@@ -666,6 +667,11 @@ AC_CHECK_HEADERS([libprocstat.h],,,
#include <sys/socket.h>
#endif])
+if test "x$with_xattr" != "xno"
+then
+ AC_CHECK_HEADERS(attr/xattr.h)
+fi
+
dnl **** Check for working dll ****
AC_SUBST(dlldir,"\${libdir}/wine")
diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c
index fe31b5c..7d28c05 100644
--- a/dlls/advapi32/tests/security.c
+++ b/dlls/advapi32/tests/security.c
@@ -3088,10 +3088,10 @@ static void test_CreateDirectoryA(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);
}
@@ -3100,10 +3100,10 @@ static void test_CreateDirectoryA(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);
}
@@ -3277,7 +3277,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",
@@ -3288,8 +3288,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 */,
@@ -3943,7 +3942,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",
@@ -3954,7 +3953,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/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 fa8874c..98e3eca 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -91,6 +91,9 @@
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
#endif
+#ifdef HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
@@ -99,6 +102,7 @@
#include "handle.h"
#include "process.h"
#include "request.h"
+#include "security.h"
#include "winternl.h"
#include "winioctl.h"
@@ -1726,9 +1730,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; j<sid->SubAuthorityCount; 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;
@@ -1804,6 +1868,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 cceb8ad..9ac9188 100644
--- a/server/file.c
+++ b/server/file.c
@@ -32,6 +32,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
+#include <limits.h>
#include <unistd.h>
#ifdef HAVE_UTIME_H
#include <utime.h>
@@ -39,6 +40,9 @@
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
+#ifdef HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
@@ -237,7 +241,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))
@@ -424,9 +428,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; i<ace_count; i++)
+ {
+ char b[sizeof(SID) + sizeof(ULONG) * SID_MAX_SUB_AUTHORITIES];
+ int sub_authority_count = 0;
+ SID *sid = (SID *)&b[0];
+ char sidtxt[100];
+ int rev, ia, sa;
+
+ if (i != 0)
+ {
+ aaa = (ACCESS_ALLOWED_ACE *)ace_next( current_ace );
+ current_ace = &aaa->Header;
+ }
+ 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;
@@ -443,9 +607,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;
@@ -575,6 +741,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 493d30b..721c087 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