From: VMware, Inc <> Date: Tue, 26 Apr 2011 20:33:42 +0000 (-0700) Subject: lib/user, lib/file: move safe temp directory code to lib/file X-Git-Tag: 2011.04.25-402641~66 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ef52e68d9deb0ed08c5f0f4b3a4ea53856892d34;p=thirdparty%2Fopen-vm-tools.git lib/user, lib/file: move safe temp directory code to lib/file 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 --- diff --git a/open-vm-tools/lib/file/Makefile.am b/open-vm-tools/lib/file/Makefile.am index 1354df9bf..880db318b 100644 --- a/open-vm-tools/lib/file/Makefile.am +++ b/open-vm-tools/lib/file/Makefile.am @@ -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 diff --git a/open-vm-tools/lib/file/filePosix.c b/open-vm-tools/lib/file/filePosix.c index 9ef639d62..cb270e928 100644 --- a/open-vm-tools/lib/file/filePosix.c +++ b/open-vm-tools/lib/file/filePosix.c @@ -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 index 000000000..de6525810 --- /dev/null +++ b/open-vm-tools/lib/file/fileTemp.c @@ -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 +#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 index 000000000..e66fc246d --- /dev/null +++ b/open-vm-tools/lib/file/fileTempPosix.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !__FreeBSD__ && !sun +# include +#endif + +#ifdef sun +# include +#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__ diff --git a/open-vm-tools/lib/include/file.h b/open-vm-tools/lib/include/file.h index d3f897595..5f7009d66 100644 --- a/open-vm-tools/lib/include/file.h +++ b/open-vm-tools/lib/include/file.h @@ -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 diff --git a/open-vm-tools/lib/include/util.h b/open-vm-tools/lib/include/util.h index ff6e5de1a..7f20b0bd0 100644 --- a/open-vm-tools/lib/include/util.h +++ b/open-vm-tools/lib/include/util.h @@ -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); diff --git a/open-vm-tools/services/plugins/vix/vixTools.c b/open-vm-tools/services/plugins/vix/vixTools.c index 6ea798dc9..2b9a4089d 100644 --- a/open-vm-tools/services/plugins/vix/vixTools.c +++ b/open-vm-tools/services/plugins/vix/vixTools.c @@ -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. */