diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c index d3efa0b964..7e2e3f2783 100644 --- a/dlls/advapi32/tests/security.c +++ b/dlls/advapi32/tests/security.c @@ -777,6 +777,43 @@ static void test_FileSecurity(void) HeapFree (GetProcessHeap (), 0, sd); + /* Repeat for the temporary directory ... */ + + /* Get size needed */ + retSize = 0; + SetLastError (NO_ERROR); + rc = pGetFileSecurityA (path, request, NULL, 0, &retSize); + ok (!rc, "GetFileSecurityA " + "was expected to fail for '%s'\n", path); + ok (GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetFileSecurityA " + "returned %d; expected ERROR_INSUFFICIENT_BUFFER\n", GetLastError()); + ok (retSize > sizeof (SECURITY_DESCRIPTOR), "GetFileSecurityA " + "returned size %d; expected > %d\n", retSize, sizeof (SECURITY_DESCRIPTOR)); + + sdSize = retSize; + sd = HeapAlloc (GetProcessHeap (), 0, sdSize); + + /* Get security descriptor for real */ + retSize = 0; + SetLastError (NO_ERROR); + rc = pGetFileSecurityA (path, request, sd, sdSize, &retSize); + ok (rc, "GetFileSecurityA " + "was not expected to fail '%s'\n", path); + ok (GetLastError () == NO_ERROR, "GetFileSecurityA " + "returned %d; expected NO_ERROR\n", GetLastError ()); + ok (retSize == sdSize, "GetFileSecurityA " + "returned size %d; expected %d\n", retSize, sdSize); + + /* Use it to set security descriptor */ + SetLastError (NO_ERROR); + rc = pSetFileSecurityA (path, request, sd); + ok (rc, "SetFileSecurityA " + "was not expected to fail '%s'\n", path); + ok (GetLastError () == NO_ERROR, "SetFileSecurityA " + "returned %d; expected NO_ERROR\n", GetLastError ()); + + HeapFree (GetProcessHeap (), 0, sd); + /* Remove temporary file and directory */ DeleteFileA (file); RemoveDirectoryA (path); diff --git a/server/change.c b/server/change.c index 2c690e0c4b..aded849091 100644 --- a/server/change.c +++ b/server/change.c @@ -44,6 +44,8 @@ #include "handle.h" #include "thread.h" #include "request.h" +#include "process.h" +#include "security.h" #include "winternl.h" /* dnotify support */ @@ -146,6 +148,8 @@ struct dir { struct object obj; /* object header */ struct fd *fd; /* file descriptor to the directory */ + mode_t mode; /* file stat.st_mode */ + uid_t uid; /* file stat.st_uid */ struct list entry; /* entry in global change notifications list */ unsigned int filter; /* notification filter */ int notified; /* SIGIO counter */ @@ -157,6 +161,9 @@ struct dir }; static struct fd *dir_get_fd( struct object *obj ); +static struct security_descriptor *dir_get_sd( struct object *obj ); +static int dir_set_sd( struct object *obj, const struct security_descriptor *sd, + unsigned int set_info ); static void dir_dump( struct object *obj, int verbose ); static void dir_destroy( struct object *obj ); @@ -172,8 +179,8 @@ static const struct object_ops dir_ops = no_signal, /* signal */ dir_get_fd, /* get_fd */ default_fd_map_access, /* map_access */ - default_get_sd, /* get_sd */ - default_set_sd, /* set_sd */ + dir_get_sd, /* get_sd */ + dir_set_sd, /* set_sd */ no_lookup_name, /* lookup_name */ no_open_file, /* open_file */ fd_close_handle, /* close_handle */ @@ -291,6 +298,94 @@ static struct fd *dir_get_fd( struct object *obj ) return (struct fd *)grab_object( dir->fd ); } +static int get_dir_unix_fd( struct dir *dir ) +{ + return get_unix_fd( dir->fd ); +} + +static struct security_descriptor *dir_get_sd( struct object *obj ) +{ + struct dir *dir = (struct dir *)obj; + int unix_fd; + struct stat st; + struct security_descriptor *sd; + assert( obj->ops == &dir_ops ); + + unix_fd = get_dir_unix_fd( dir ); + + if (unix_fd == -1 || 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)) == (dir->mode & (S_IRWXU|S_IRWXO)) && + (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 )); + if (!sd) return obj->sd; + + dir->mode = st.st_mode; + dir->uid = st.st_uid; + free( obj->sd ); + obj->sd = sd; + return sd; +} + +static int dir_set_sd( struct object *obj, const struct security_descriptor *sd, + unsigned int set_info ) +{ + struct dir *dir = (struct dir *)obj; + const SID *owner; + mode_t mode; + int unix_fd; + + assert( obj->ops == &dir_ops ); + + unix_fd = get_dir_unix_fd( dir ); + + 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 ); + + if (set_info & DACL_SECURITY_INFORMATION) + { + /* keep the bits that we don't map to access rights in the ACL */ + mode = dir->mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXG); + mode |= sd_to_mode( sd, owner ); + + if (dir->mode != mode) + { + if (fchmod( unix_fd, mode ) == -1) + { + file_set_error(); + return 0; + } + + dir->mode = mode; + } + } + return 1; +} + static struct change_record *get_first_change_record( struct dir *dir ) { struct list *ptr = list_head( &dir->change_records );