]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
lib/user, lib/file: move safe temp directory code to lib/file
authorVMware, Inc <>
Tue, 26 Apr 2011 20:33:42 +0000 (13:33 -0700)
committerMarcelo Vanzin <mvanzin@vmware.com>
Tue, 26 Apr 2011 20:33:42 +0000 (13:33 -0700)
Part of lib/file has been sitting, heavily entangled, in lib/user
for a long time. This prevents out code from always using safe
temporary directories.

These functions sat in lib/user but used lib/file functionality.
They are no more or less entangled living in lib/file and having
lib/file names than in lib/user - once the last lib/user entanglement
is removed.

It is now possible to untangle the functions and move them. This is
"heavy lifting" change. A follow on will remove the older, unsafe
versions from lib/file and our code base.

Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
open-vm-tools/lib/file/Makefile.am
open-vm-tools/lib/file/filePosix.c
open-vm-tools/lib/file/fileTemp.c [new file with mode: 0644]
open-vm-tools/lib/file/fileTempPosix.c [new file with mode: 0644]
open-vm-tools/lib/include/file.h
open-vm-tools/lib/include/util.h
open-vm-tools/services/plugins/vix/vixTools.c

index 1354df9bff8073f7550ad70e74f2360a2f0046b1..880db318b69cf701a39a28d30bc92edc649a194a 100644 (file)
@@ -25,4 +25,5 @@ libFile_la_SOURCES += fileIO.c
 libFile_la_SOURCES += fileIOPosix.c
 libFile_la_SOURCES += fileLockPrimitive.c
 libFile_la_SOURCES += fileLockPosix.c
-
+libFile_la_SOURCES += fileTemp.c
+libFile_la_SOURCES += fileTempPosix.c
index 9ef639d62cbcb810691e8826133b8a244b07d7fd..cb270e928ff66c2e6c9d537238df8f3c8b4a642b 100644 (file)
@@ -2714,7 +2714,7 @@ FileTryDir(const char *dirName)  // IN: Is this a writable directory?
  *     Determine the best temporary directory. Unsafe since the
  *     returned directory is generally going to be 0777, thus all sorts
  *     of denial of service or symlink attacks are possible.  Please
- *     use Util_GetSafeTmpDir if your dependencies permit it.
+ *     use File_GetSafeTmpDir if your dependencies permit it.
  *
  * Results:
  *     NULL if error (reported to the user).
diff --git a/open-vm-tools/lib/file/fileTemp.c b/open-vm-tools/lib/file/fileTemp.c
new file mode 100644 (file)
index 0000000..de65258
--- /dev/null
@@ -0,0 +1,128 @@
+/*********************************************************
+ * Copyright (C) 2011 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *********************************************************/
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#include "vmware.h"
+#include "log.h"
+#include "file.h"
+#include "util.h"
+#include "unicodeOperations.h"
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * File_MakeSafeTemp --
+ *
+ *      Exactly the same as File_MakeTemp except uses a safe directory
+ *      as the default temporary directory.
+ *
+ * Results:
+ *      Open file descriptor or -1
+ *
+ * Side effects:
+ *      Creates a file if successful.
+ *----------------------------------------------------------------------
+ */
+
+int
+File_MakeSafeTemp(ConstUnicode tag,  // IN (OPT):
+                  Unicode *presult)  // OUT:
+{
+   int fd = -1;
+   Unicode dir = NULL;
+   Unicode fileName = NULL;
+
+   *presult = NULL;
+
+   if (tag && File_IsFullPath(tag)) {
+      File_GetPathName(tag, &dir, &fileName);
+   } else {
+      dir = File_GetSafeTmpDir(TRUE);
+      fileName = Unicode_Duplicate(tag ? tag : "vmware");
+   }
+
+   fd = File_MakeTempEx(dir, fileName, presult);
+
+   Unicode_Free(dir);
+   Unicode_Free(fileName);
+
+   return fd;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * File_DoesVolumeSupportAcls --
+ *
+ *    Determines if the volume that the pathname resides on supports
+ *    ACLs.
+ *
+ * Results:
+ *    TRUE   it does
+ *    FALSE  it doesn't
+ *
+ * Side effects:
+ *    None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+File_DoesVolumeSupportAcls(ConstUnicode path)  // IN:
+{
+   Bool succeeded = FALSE;
+
+#if defined(_WIN32)
+   Bool res;
+   Unicode vol, vol2;
+   const utf16_t *vol2W;
+   DWORD fsFlags;
+
+   ASSERT(path);
+
+   File_SplitName(path, &vol, NULL, NULL);
+   vol2 = Unicode_Append(vol, DIRSEPS);
+
+   vol2W = UNICODE_GET_UTF16(vol2);
+   res = GetVolumeInformationW(vol2W, NULL, 0, NULL, NULL, &fsFlags, NULL, 0);
+   UNICODE_RELEASE_UTF16(vol2W);
+
+   if (res) {
+      if ((fsFlags & FS_PERSISTENT_ACLS) == 0) {
+         goto exit;
+      }
+   } else {
+      Log("%s: GetVolumeInformation failed: %d\n", __FUNCTION__,
+          GetLastError());
+      goto exit;
+   }
+
+   succeeded = TRUE;
+
+  exit:
+   Unicode_Free(vol);
+   Unicode_Free(vol2);
+#endif
+
+   return succeeded;
+}
diff --git a/open-vm-tools/lib/file/fileTempPosix.c b/open-vm-tools/lib/file/fileTempPosix.c
new file mode 100644 (file)
index 0000000..e66fc24
--- /dev/null
@@ -0,0 +1,451 @@
+/*********************************************************
+ * Copyright (C) 2004-2011 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *********************************************************/
+
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#if !__FreeBSD__ && !sun
+#   include <pwd.h>
+#endif
+
+#ifdef sun
+#   include <procfs.h>
+#endif
+
+#include "vmware.h"
+#include "file.h"
+#include "util.h"
+#include "su.h"
+#include "vm_atomic.h"
+#include "str.h"
+#include "vm_version.h"
+#include "random.h"
+#include "userlock.h"
+#include "unicodeOperations.h"
+#include "err.h"
+#include "posix.h"
+#include "mutexRankLib.h"
+#include "hostType.h"
+
+#define LOGLEVEL_MODULE util
+#include "loglevel_user.h"
+
+
+#if !__FreeBSD__ && !sun
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FileGetUserName --
+ *
+ *      Retrieve the name associated with a user ID. Thread-safe
+ *      version. --hpreg
+ *
+ * Results:
+ *      The allocated name on success
+ *      NULL on failure
+ *
+ * Side effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static char *
+FileGetUserName(uid_t uid)  // IN:
+{
+   char *memPool;
+   char *userName;
+   struct passwd pw;
+   struct passwd *pw_p;
+   long memPoolSize;
+
+#if __APPLE__
+   memPoolSize = _PASSWORD_LEN;
+#else
+   memPoolSize = sysconf(_SC_GETPW_R_SIZE_MAX);
+   if (memPoolSize <= 0) {
+      Warning("%s: sysconf(_SC_GETPW_R_SIZE_MAX) failed.\n", __FUNCTION__);
+
+      return NULL;
+   }
+#endif
+
+   memPool = malloc(memPoolSize);
+   if (memPool == NULL) {
+      Warning("%s: Not enough memory.\n", __FUNCTION__);
+
+      return NULL;
+   }
+
+   if ((Posix_Getpwuid_r(uid, &pw, memPool, memPoolSize, &pw_p) != 0) ||
+       pw_p == NULL) {
+      free(memPool);
+      Warning("%s: Unable to retrieve the username associated with "
+              "user ID %u.\n", __FUNCTION__, uid);
+
+      return NULL;
+   }
+
+   userName = strdup(pw_p->pw_name);
+   free(memPool);
+   if (userName == NULL) {
+      Warning("%s: Not enough memory.\n", __FUNCTION__);
+
+      return NULL;
+   }
+
+   return userName;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FileAcceptableSafeTmpDir --
+ *
+ *      Determines if the specified path is acceptable as the safe
+ *      temp directory.  The directory must either be creatable
+ *      with the appropriate permissions and userId or it must
+ *      already exist with those settings.
+ *
+ * Results:
+ *      TRUE if path is acceptible, FALSE otherwise
+ *
+ * Side effects:
+ *      Directory may be created
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+FileAcceptableSafeTmpDir(const char *dirname,  // IN:
+                         int userId)           // IN:
+{
+   Bool result;
+   static const mode_t mode = 0700;
+
+   result = (Posix_Mkdir(dirname, mode) == 0);
+   if (!result) {
+      int error = errno;
+
+      if (EEXIST == error) {
+         struct stat st;
+
+         /*
+          * The name already exists. Check that it is what we want: a
+          * directory owned by the current effective user with permissions
+          * 'mode'. It is crucial to use lstat() instead of stat() here,
+          * because we do not want the name to be a symlink (created by
+          * another user) pointing to a directory owned by the current
+          * effective user with permissions 'mode'.
+          */
+
+         if (0 == Posix_Lstat(dirname, &st)) {
+            /*
+             * Our directory inherited S_ISGID if its parent had it. So it
+             * is important to ignore that bit, and it is safe to do so
+             * because that bit does not affect the owner's permissions.
+             */
+
+            if (S_ISDIR(st.st_mode) &&
+                (st.st_uid == userId) &&
+                ((st.st_mode & 05777) == mode)) {
+               result = TRUE;
+            }
+         }
+      }
+   }
+
+   return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FileFindExistingSafeTmpDir --
+ *
+ *      Searches the directory baseTmpDir to see if any subdirectories
+ *      are suitable to use as the safe temp directory.  The safe temp
+ *      directory must have the correct permissions and userId.
+ *
+ * Results:
+ *      Path to discovered safe temp directory (must be freed).
+ *      NULL returned if no suitable directory is found.
+ *
+ * Side effects:
+ *      None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Unicode
+FileFindExistingSafeTmpDir(uid_t userId,            // IN:
+                           const char *userName,    // IN:
+                           const char *baseTmpDir)  // IN:
+{
+   int i;
+   int numFiles;
+   Unicode pattern;
+   Unicode tmpDir = NULL;
+   Unicode *fileList = NULL;
+   
+   /*
+    * We always use the pattern PRODUCT-USER-xxxx when creating
+    * alternative safe temp directories, so check for ones with
+    * those names and the appropriate permissions.
+    */
+   
+   pattern = Unicode_Format("%s-%s-", PRODUCT_GENERIC_NAME_LOWER, userName);
+   if (pattern == NULL) {
+      return NULL;
+   }
+
+   numFiles = File_ListDirectory(baseTmpDir, &fileList);
+
+   if (numFiles == -1) {
+      Unicode_Free(pattern);
+
+      return NULL;
+   }
+
+   for (i = 0; i < numFiles; i++) {
+       if (Unicode_StartsWith(fileList[i], pattern)) {
+          Unicode path = Unicode_Join(baseTmpDir, DIRSEPS, fileList[i],
+                                      NULL);
+
+          if (File_IsDirectory(path) &&
+              FileAcceptableSafeTmpDir(path, userId)) {
+             tmpDir = path;
+             break;
+          }
+
+          Unicode_Free(path);
+       }
+   }
+
+   Unicode_FreeList(fileList, numFiles);
+   Unicode_Free(pattern);
+
+   return tmpDir;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FileCreateSafeTmpDir --
+ *
+ *      Creates a new directory within baseTmpDir with the correct permissions
+ *      and userId to ensure it is safe from symlink attacks.
+ *
+ * Results:
+ *      Path to created safe temp directory (must be freed).
+ *      NULL returned if no suitable directory could be created.
+ *
+ * Side effects:
+ *      Directory may be created.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static char *
+FileCreateSafeTmpDir(uid_t userId,            // IN:
+                     const char *userName,    // IN:
+                     const char *baseTmpDir)  // IN:
+{
+   static const int MAX_DIR_ITERS = 250;
+   int curDirIter = 0;
+   char *tmpDir = NULL;
+
+   while (TRUE) {
+      unsigned int suffix;
+      
+      /* 
+       * We use a crypographically strong random number which is overkill
+       * for this purpose but makes it slightly more likely that we will
+       * create an unused name than if we had simply tried suffixes in
+       * numeric order.
+       */
+
+      if (!Random_Crypto(sizeof(suffix), &suffix)) {
+         Warning("%s: Call to Random_Crypto failed.\n", __FUNCTION__);
+         break;
+      }
+      
+      tmpDir = Str_Asprintf(NULL, "%s"DIRSEPS"%s-%s-%u", baseTmpDir,
+                            PRODUCT_GENERIC_NAME_LOWER, userName, suffix);
+      
+      if (!tmpDir) {
+         Warning("%s: Out of memory error.\n", __FUNCTION__);
+         break;
+      }
+      
+      if (FileAcceptableSafeTmpDir(tmpDir, userId)) {
+         break;
+      }
+      
+      if (++curDirIter > MAX_DIR_ITERS) {
+         Warning("%s: Failed to create a safe temporary directory, path "
+                 "\"%s\". The maximum number of attempts was exceeded.\n",
+                 __FUNCTION__, tmpDir);
+         free(tmpDir);
+         tmpDir = NULL;
+         break;
+      }
+      
+      free(tmpDir);
+      tmpDir = NULL;
+   }
+
+   return tmpDir;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * File_GetSafeTmpDir --
+ *
+ *      Return a safe temporary directory (i.e. a temporary directory which
+ *      is not prone to symlink attacks, because it is only writable by the
+ *      current effective user). Guaranteed to return the same directory
+ *      every time it is called during the lifetime of the current process
+ *      (unless that directory is deleted while the process is running).
+ *
+ * Results:
+ *      The allocated directory path on success.
+ *      NULL on failure.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+char *
+File_GetSafeTmpDir(Bool useConf)  // IN:
+{
+   static Atomic_Ptr lckStorage;
+   static char *safeDir;
+   char *tmpDir = NULL;
+   char *baseTmpDir = NULL;
+   char *userName = NULL;
+   uid_t userId;
+   MXUserExclLock *lck;
+
+   userId = geteuid();
+
+   /* Get and take lock for our safe dir. */
+   lck = MXUser_CreateSingletonExclLock(&lckStorage, "getSafeTmpDirLock",
+                                        RANK_getSafeTmpDirLock);
+   ASSERT_NOT_IMPLEMENTED(lck != NULL);
+
+   MXUser_AcquireExclLock(lck);
+
+   /*
+    * Check if we've created a temporary dir already and if it is still usable.
+    */
+
+   if (safeDir && FileAcceptableSafeTmpDir(safeDir, userId)) {
+      tmpDir = Util_SafeStrdup(safeDir);
+      goto exit;
+   }
+
+   /* We don't have a useable temporary dir, create one. */
+   baseTmpDir = File_GetTmpDir(useConf);
+   
+   if (!baseTmpDir) {
+      Warning("%s: File_GetTmpDir failed.\n", __FUNCTION__);
+      goto exit;
+   }
+   
+   userName = FileGetUserName(userId);
+   
+   if (!userName) {
+      Warning("%s: FileGetUserName failed, using numeric ID "
+              "as username instead.\n", __FUNCTION__);
+      
+      /* Fallback on just using the userId as the username. */
+      userName = Str_Asprintf(NULL, "uid-%d", userId);
+      
+      if (!userName) {
+         Warning("%s: Str_Asprintf error.\n", __FUNCTION__);
+         goto exit;
+      }
+   }
+   
+   tmpDir = Str_Asprintf(NULL, "%s"DIRSEPS"%s-%s", baseTmpDir,
+                         PRODUCT_GENERIC_NAME_LOWER, userName);
+   
+   if (!tmpDir) {
+      Warning("%s: Out of memory error.\n", __FUNCTION__);
+      goto exit;
+   }
+
+   if (!FileAcceptableSafeTmpDir(tmpDir, userId)) {
+      /*
+       * We didn't get our first choice for the safe temp directory.
+       * Search through the unsafe tmp directory to see if there is
+       * an acceptable one to use.
+       */
+
+      free(tmpDir);
+
+      tmpDir = FileFindExistingSafeTmpDir(userId, userName, baseTmpDir);
+
+      if (!tmpDir) {
+         /*
+          * We didn't find any usable directories, so try to create one now.
+          */
+
+         tmpDir = FileCreateSafeTmpDir(userId, userName, baseTmpDir);
+      }
+   }
+
+   if (tmpDir) {
+      /*
+       * We have successfully created a temporary directory, remember it for
+       * future calls.
+       */
+
+      free(safeDir);
+      safeDir = Util_SafeStrdup(tmpDir);
+   }
+
+  exit:
+   MXUser_ReleaseExclLock(lck);
+   free(baseTmpDir);
+   free(userName);
+
+   return tmpDir;
+}
+#endif // __linux__
index d3f89759542fe39cbd0acbd67499151dda05b44a..5f7009d66fa7b97acfde7ef6fad34815936f9481 100644 (file)
@@ -78,7 +78,7 @@ typedef const WalkDirContextImpl *WalkDirContext;
  * If successful, this function should return a dynamically allocated string
  * with the filename.
  *
- * File_MakeTempEx2 frees the fileName after a successful call to this
+ * File_MakeTempEx2 frees the pathName after a successful call to this
  * function.
  *
  */
@@ -179,7 +179,7 @@ Bool File_IsDirectory(ConstUnicode pathName);
 
 Bool File_IsFile(ConstUnicode pathName);
 
-Bool File_IsSymLink(ConstUnicode fileName);
+Bool File_IsSymLink(ConstUnicode pathName);
 
 Bool File_IsCharDevice(ConstUnicode pathName);
 
@@ -198,15 +198,15 @@ uint64 File_GetFreeSpace(ConstUnicode pathName,
 
 uint64 File_GetCapacity(ConstUnicode pathName);
 
-/* Deprecated; use Util_GetSafeTmpDir if you can */
+/* Deprecated; use File_GetSafeTmpDir if you can */
 char *File_GetTmpDir(Bool useConf);
 
-/* Deprecated; use Util_MakeSafeTemp if you can */
+/* Deprecated; use File_MakeSafeTemp if you can */
 int File_MakeTemp(ConstUnicode tag,
                   Unicode *presult);
 
 int File_MakeTempEx(ConstUnicode dir,
-                    ConstUnicode fileName,
+                    ConstUnicode pathName,
                     Unicode *presult);
 
 int File_MakeTempEx2(ConstUnicode dir,
@@ -219,7 +219,7 @@ int64 File_GetModTime(ConstUnicode pathName);
 
 char *File_GetModTimeString(ConstUnicode pathName);
 
-char *File_GetUniqueFileSystemID(const char *fileName);
+char *File_GetUniqueFileSystemID(const char *pathName);
 
 Bool File_GetTimes(ConstUnicode pathName,
                    VmTimeType *createTime,
@@ -285,7 +285,7 @@ Bool File_Move(ConstUnicode oldFile,
                ConstUnicode newFile,
                Bool *asRename);
 
-void File_Rotate(const char *fileName,
+void File_Rotate(const char *pathName,
                  int n,
                  Bool noRename,
                  char **newFileName);
@@ -359,6 +359,13 @@ Bool File_MakeCfgFileExecutable(ConstUnicode pathName);
 
 char *File_ExpandAndCheckDir(const char *dirName);
 
+char *File_GetSafeTmpDir(Bool useConf);
+
+int File_MakeSafeTemp(ConstUnicode tag,
+                      Unicode *presult);
+
+Bool File_DoesVolumeSupportAcls(ConstUnicode pathName);
+
 #ifdef __cplusplus
 } // extern "C" {
 #endif
index ff6e5de1ab039e5a60254dac839fb0c4ae228bf4..7f20b0bd0fec613749b75cc0b5abb87132ae6428 100644 (file)
@@ -95,11 +95,6 @@ EXTERN char *Util_DeriveFileName(const char *source,
 EXTERN char *Util_CombineStrings(char **sources, int count);
 EXTERN char **Util_SeparateStrings(char *source, int *count);
 
-EXTERN char *Util_GetSafeTmpDir(Bool useConf);
-
-EXTERN int Util_MakeSafeTemp(ConstUnicode tag,
-                             Unicode *presult);
-
 typedef struct UtilSingleUseResource UtilSingleUseResource;
 UtilSingleUseResource *Util_SingleUseAcquire(const char *name);
 void Util_SingleUseRelease(UtilSingleUseResource *res);
index 6ea798dc974a8d0f1233ba1012d95c19ef4d8239..2b9a4089d56c9925b4e3c99047dd0aea7ec7e426 100644 (file)
@@ -7444,9 +7444,9 @@ VixToolsGetTempFile(VixCommandRequestHeader *requestMsg,   // IN
 #endif
 
    /*
-    * We need to use File_MakeTemp and not Util_MakeSafeTemp.
-    * File_MakeTemp uses File_GetTmpDir, while Util_MakeSafeTemp
-    * uses Util_GetSafeTmpDir. We can't use Util_GetSafeTmpDir
+    * We need to use File_MakeTemp and not File_MakeSafeTemp.
+    * File_MakeTemp uses File_GetTmpDir, while File_MakeSafeTemp
+    * uses File_GetSafeTmpDir. We can't use File_GetSafeTmpDir
     * because much of win32util.c which gets used in that call creates
     * dependencies on code that won't run on win9x.
     */