--- /dev/null
+/*********************************************************
+ * 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;
+}
--- /dev/null
+/*********************************************************
+ * 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__