safer selection of the temporary directory for wineserver
authorMichael Gilbert <mgilbert@debian.org>
Wed, 12 Oct 2022 00:18:28 +0000 (01:18 +0100)
committerMichael Gilbert <mgilbert@debian.org>
Wed, 12 Oct 2022 00:18:28 +0000 (01:18 +0100)
bug-debian: https://bugs.debian.org/903622
bug-debian: https://bugs.debian.org/904041
bug-upstream: https://bugs.winehq.org/show_bug.cgi?id=39013

Gbp-Pq: Topic fixes
Gbp-Pq: Name temporary-directory.patch

dlls/ntdll/unix/server.c
libs/wine/config.c
server/request.c

index 9d0594d3374edd1d225e3361ba75ada88337313e..868cd1ac70ddc9c7fc9baf801f48e5d1559d2cc0 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "config.h"
 
+#include <dirent.h>
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
@@ -1104,6 +1105,17 @@ int server_pipe( int fd[2] )
     return ret;
 }
 
+/* build a path from the specified dir and name */
+static char *ntdll_build_path( const char *dir, const char *name )
+{
+    size_t len = strlen(dir);
+    char *ret = malloc( len + strlen(name) + 2 );
+
+    memcpy( ret, dir, len );
+    if (len && ret[len-1] != '/') ret[len++] = '/';
+    strcpy( ret + len, name );
+    return ret;
+}
 
 /***********************************************************************
  *           init_server_dir
@@ -1113,16 +1125,113 @@ static const char *init_server_dir( dev_t dev, ino_t ino )
     char *p, *dir;
     size_t len = sizeof("/server-") + 2 * sizeof(dev) + 2 * sizeof(ino) + 2;
 
-#ifdef __ANDROID__  /* there's no /tmp dir on Android */
-    len += strlen( config_dir ) + sizeof("/.wineserver");
-    dir = malloc( len );
-    strcpy( dir, config_dir );
-    strcat( dir, "/.wineserver/server-" );
-#else
-    len += sizeof("/tmp/.wine-") + 12;
-    dir = malloc( len );
-    sprintf( dir, "/tmp/.wine-%u/server-", getuid() );
-#endif
+    char *root = NULL;
+    char *run_dir = NULL;
+    const char *run_prefix = "/run/user";
+
+    /* use /run/user/$uid as wineserver's tmpdir if possible */
+    run_dir = malloc( strlen(run_prefix) + 13 );
+    sprintf( run_dir, "%s/%u", run_prefix, getuid() );
+    if (opendir( run_dir ))
+    {
+        root = malloc( strlen(run_dir) + 6 );
+        sprintf( root, "%s/wine", run_dir );
+    }
+    free( run_dir );
+
+    /* otherwise, use a randomly named directory under TMPDIR */
+    if (!root)
+    {
+        int error;
+        FILE *stream;
+        char *wineserver_file, *tmp_env, *tmp_dir;
+        mode_t mode = S_IRUSR;
+
+        /* determine the temporary directory */
+        tmp_env = secure_getenv("TMPDIR");
+        if (tmp_env)
+        {
+            tmp_dir = malloc( strlen(tmp_env) + 1 );
+            strcpy( tmp_dir, tmp_env );
+        }
+        else
+        {
+            const char *tmp_default = "/tmp";
+            tmp_dir = malloc( strlen(tmp_default) + 1 );
+            strcpy( tmp_dir, tmp_default );
+        }
+
+        /* remove existing wineserver tmpdir file if permissions are wrong */
+        wineserver_file = ntdll_build_path( config_dir, "wineserver" );
+        if ( access( wineserver_file, F_OK ) != -1 )
+        {
+            struct stat statbuf;
+            mode_t mode_mask = S_IRWXU | S_IRWXG | S_IRWXO;
+            error = stat( wineserver_file, &statbuf );
+            if (error != 0)
+                fatal_error("error reading wineserver tmpdir file permissions\n");
+            if ((statbuf.st_mode & mode_mask) != mode)
+            {
+                error = remove( wineserver_file );
+                if (error != 0)
+                    fatal_error("error removing wineserver tmpdir file\n");
+            }
+        }
+
+        /* create or read the name of the directory */
+        stream = fopen( wineserver_file, "r" );
+        if (!stream)
+        {
+            int n, fd;
+
+            /* create a new randomized name for the directory */
+            root = mkdtemp( ntdll_build_path( tmp_dir, "wine-XXXXXX" ) );
+            if (!root)
+                fatal_error("unable to create wineserver tmpdir\n");
+
+            /* save location of the chosen temporary directory */
+            fd = creat( wineserver_file, mode );
+            if (fd < 0)
+                fatal_error("error opening file descriptor for wineserver tmpdir file\n");
+            stream = fdopen( fd, "w" );
+            if (!stream)
+                fatal_error("error opening wineserver tmpdir file\n");
+            n = fputs( root + strlen(tmp_dir) + 1, stream );
+            if (n < 0)
+                fatal_error("error writing to wineserver tmpdir file\n");
+            error = fclose( stream );
+            if (error != 0)
+                fatal_error("error closing wineserver tmpdir file\n");
+        }
+        else
+        {
+            char *tmp_wineserver;
+            size_t length = 0;
+            ssize_t nread;
+
+            /* try to read a previously generated wineserver tmpdir */
+            nread = getline( &tmp_wineserver, &length, stream );
+            error = fclose( stream );
+            if (error != 0)
+                fatal_error("error closing wineserver tmpdir file\n");
+
+            /* if something went wrong, remove the broken file */
+            if (nread < 0)
+            {
+                remove( wineserver_file );
+                fatal_error("fixed a temporary directory error, please try running the same command again\n");
+            }
+
+            /* build the full path to the temporary directory */
+            root = ntdll_build_path( tmp_dir, tmp_wineserver );
+        }
+        free( tmp_dir );
+        free( wineserver_file );
+    }
+    dir = malloc( strlen(root) + len );
+    sprintf( dir, "%s/server-", root );
+    free( root );
+
     p = dir + strlen( dir );
     if (dev != (unsigned long)dev)
         p += sprintf( p, "%lx%08lx-", (unsigned long)((unsigned long long)dev >> 32), (unsigned long)dev );
index 23c76fda1282063b2e4f05a6d45cdadca0be2cea..a8a39800b12c85c33c19ec7b4b5abd7d71b79f03 100644 (file)
@@ -382,6 +382,8 @@ static void init_server_dir( dev_t dev, ino_t ino )
 {
     char *p, *root;
 
+    fatal_error("called obsolete init_server_dir\n");
+
 #ifdef __ANDROID__  /* there's no /tmp dir on Android */
     root = build_path( config_dir, ".wineserver" );
 #else
index 7021741c765aad696a8b65095dccee07ec9477fe..a3d5f0689ce72d00577aca7ffa070e6abe673d85 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "config.h"
 
+#include <dirent.h>
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -609,11 +610,23 @@ static void create_dir( const char *name, struct stat *st )
     if (st->st_mode & 077) fatal_error( "%s must not be accessible by other users\n", name );
 }
 
+static char *server_build_path( const char *dir, const char *name )
+{
+    size_t len = strlen(dir);
+    char *ret = malloc( len + strlen(name) + 2 );
+
+    memcpy( ret, dir, len );
+    if (len && ret[len-1] != '/') ret[len++] = '/';
+    strcpy( ret + len, name );
+    return ret;
+}
+
 /* create the server directory and chdir to it */
 static char *create_server_dir( int force )
 {
+    const char *server_root_prefix = "/run/user";
     const char *prefix = getenv( "WINEPREFIX" );
-    char *p, *config_dir;
+    char *p, *config_dir, *run_dir;
     struct stat st, st2;
     size_t len = sizeof("/server-") + 2 * sizeof(st.st_dev) + 2 * sizeof(st.st_ino) + 2;
 
@@ -663,9 +676,117 @@ static char *create_server_dir( int force )
     strcpy( server_dir, config_dir );
     strcat( server_dir, "/.wineserver" );
 #else
-    len += sizeof("/tmp/.wine-") + 12;
-    if (!(server_dir = malloc( len ))) fatal_error( "out of memory\n" );
-    sprintf( server_dir, "/tmp/.wine-%u", getuid() );
+    /* use /run/user/$uid as wineserver's tmpdir by default */
+    if (!(run_dir = malloc( strlen(server_root_prefix) + 13 )))
+        fatal_error( "out of memory\n" );
+    sprintf( run_dir, "%s/%u", server_root_prefix, getuid() );
+
+    if (opendir( run_dir )) /* use /run as the temporary directory */
+    {
+        len += strlen(run_dir) + 6;
+        if (!(server_dir = malloc( len )))
+            fatal_error( "out of memory\n" );
+        sprintf( server_dir, "%s/wine", run_dir );
+    }
+    else /* use a randomly named directory in TMPDIR */
+    {
+        int error;
+        FILE *stream;
+        char *wineserver_file, *tmp_dir, *tmp_env, *tmp_root;
+        mode_t mode = S_IRUSR;
+
+        /* determine the temporary directory */
+        tmp_env = secure_getenv("TMPDIR");
+        if (tmp_env)
+        {
+            if (!(tmp_dir = malloc( strlen(tmp_env) + 1 )))
+                fatal_error( "out of memory\n" );
+            strcpy( tmp_dir, tmp_env );
+        }
+        else
+        {
+            const char *tmp_default = "/tmp";
+            if (!(tmp_dir = malloc( strlen(tmp_default) + 1 )))
+                fatal_error( "out of memory\n" );
+            strcpy( tmp_dir, tmp_default );
+        }
+
+        /* remove existing wineserver tmpdir file if permissions are wrong */
+        wineserver_file = server_build_path( config_dir, "wineserver" );
+        if ( access( wineserver_file, F_OK ) != -1 )
+        {
+            struct stat statbuf;
+            mode_t mode_mask = S_IRWXU | S_IRWXG | S_IRWXO;
+            error = stat( wineserver_file, &statbuf );
+            if (error != 0)
+                fatal_error("error reading wineserver tmpdir file permissions\n");
+            if ((statbuf.st_mode & mode_mask) != mode)
+            {
+                error = remove( wineserver_file );
+                if (error != 0)
+                    fatal_error("error removing wineserver tmpdir file\n");
+            }
+        }
+
+        /* create or read the name of the directory */
+        stream = fopen( wineserver_file, "r" );
+        if (!stream)
+        {
+            int n, fd;
+
+            /* create a new randomized name for the directory */
+            tmp_root = mkdtemp( server_build_path( tmp_dir, "wine-XXXXXX" ) );
+            if (!tmp_root)
+                fatal_error("unable to create wineserver tmpdir\n");
+
+            /* save location of the chosen temporary directory */
+            fd = creat( wineserver_file, mode );
+            if (fd < 0)
+                fatal_error("error opening file descriptor for wineserver tmpdir file\n");
+            stream = fdopen( fd, "w" );
+            if (!stream)
+                fatal_error("error opening wineserver tmpdir file\n");
+            n = fputs( tmp_root + strlen(tmp_dir) + 1, stream );
+            if (n < 0)
+                fatal_error("error writing to wineserver tmpdir file\n");
+            error = fclose( stream );
+            if (error != 0)
+                fatal_error("error closing wineserver tmpdir file\n");
+        }
+        else
+        {
+            char *tmp_wineserver;
+            size_t length = 0;
+            ssize_t nread;
+
+            /* try to read a previously generated wineserver tmpdir */
+            nread = getline( &tmp_wineserver, &length, stream );
+            error = fclose( stream );
+            if (error != 0)
+                fatal_error("error closing wineserver tmpdir file\n");
+/* if something went wrong, remove the broken file */
+            if (nread < 0)
+            {
+                remove( wineserver_file );
+                fatal_error("fixed a temporary directory error, please try running the same command again\n");
+            }
+
+            /* build the full path to the temporary directory */
+            tmp_root = server_build_path( tmp_dir, tmp_wineserver );
+        }
+
+        /* copy the path into its final destination */
+        len += strlen(tmp_root) + 1;
+        if (!(server_dir = malloc( len )))
+            fatal_error( "out of memory\n" );
+        strcpy( server_dir, tmp_root );
+
+        /* free temporaries */
+        free( tmp_dir );
+        free( tmp_root );
+        free( wineserver_file );
+    }
+    free( run_dir );
 #endif
     create_dir( server_dir, &st2 );