*
* FileTryDir --
*
- * Check to see if the given directory is actually a directory
+ * Check to see if the specified directory is actually a directory
* and is writable by us.
*
* Results:
- * The expanded directory name on success, NULL on failure.
+ * !NULL The expanded directory name (must be freed)
+ * NULL Failure
*
* Side effects:
- * The result is allocated.
+ * None
*
*----------------------------------------------------------------------
*/
*
* FileGetTmpDir --
*
- * 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.
+ * 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.
*
* Results:
- * NULL if error (reported to the user).
+ * !NULL The temp directory name (must be freed)
+ * NULL Failure (reported to the user).
*
* Side effects:
- * The result is allocated.
+ * None
*
*----------------------------------------------------------------------
*/
*
* FileGetUserName --
*
- * Retrieve the name associated with a user ID. Thread-safe
+ * Retrieve the name associated with the specified UID. Thread-safe
* version. --hpreg
*
* Results:
- * The allocated name on success
- * NULL on failure
+ * !NULL The user name (must be freed)
+ * NULL Failure
*
* Side effects:
* None
FileGetUserName(uid_t uid) // IN:
{
char *memPool;
- char *userName;
+ long memPoolSize;
struct passwd pw;
struct passwd *pw_p;
- long memPoolSize;
+ char *userName = NULL;
#if defined(__APPLE__)
memPoolSize = _PASSWORD_LEN;
}
#endif
- memPool = malloc(memPoolSize);
- if (memPool == NULL) {
- Warning("%s: Not enough memory.\n", __FUNCTION__);
-
- return NULL;
- }
+ memPool = Util_SafeMalloc(memPoolSize);
if ((Posix_Getpwuid_r(uid, &pw, memPool, memPoolSize, &pw_p) != 0) ||
pw_p == NULL) {
- Posix_Free(memPool);
- Warning("%s: Unable to retrieve the username associated with "
- "user ID %u.\n", __FUNCTION__, uid);
-
- return NULL;
+ Warning("%s: Unable to retrieve the user name associated with UID %u.\n",
+ __FUNCTION__, uid);
+ } else {
+ userName = Util_SafeStrdup(pw_p->pw_name);
}
- userName = strdup(pw_p->pw_name);
Posix_Free(memPool);
+
+ return userName;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FileGetUserIdentifier --
+ *
+ * Attempt to obtain an user identification string.
+ *
+ * Results:
+ * A dynamically allocated string containing the user identifier.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static char *
+FileGetUserIdentifier(uid_t uid, // IN:
+ Bool addPid) // IN:
+{
+ char *userName = FileGetUserName(uid);
+
if (userName == NULL) {
- Warning("%s: Not enough memory.\n", __FUNCTION__);
+ Warning("%s: Failed to get user name, using UID.\n", __FUNCTION__);
- return NULL;
+ /* Fallback on just using the uid as the user name. */
+ userName = Str_SafeAsprintf(NULL, "uid_%u", uid);
+ }
+
+ if (addPid) {
+ char *pidToo = Str_SafeAsprintf(NULL, "%s_%u", userName, getpid());
+
+ Posix_Free(userName);
+ userName = pidToo;
}
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.
+ * Determines if the specified path is acceptable as a safe temp
+ * directory. The directory must either be creatable with the appropriate
+ * permissions and UID or it must already exist with those settings.
*
* Results:
- * TRUE if path is acceptible, FALSE otherwise
+ * TRUE Path is acceptible
+ * FALSE Otherwise
*
* Side effects:
* Directory may be created
*/
static Bool
-FileAcceptableSafeTmpDir(const char *dirname, // IN:
- int userId) // IN:
+FileAcceptableSafeTmpDir(const char *dirName, // IN:
+ uid_t uid) // IN:
{
- Bool result;
+ Bool acceptable;
static const mode_t mode = 0700;
- result = (Posix_Mkdir(dirname, mode) == 0);
- if (!result) {
+ acceptable = (Posix_Mkdir(dirName, mode) == 0);
+
+ if (!acceptable) {
int error = errno;
if (EEXIST == error) {
* effective user with permissions 'mode'.
*/
- if (Posix_Lstat(dirname, &st) == 0) {
+ if (Posix_Lstat(dirName, &st) == 0) {
/*
- * 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.
+ * 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_uid == uid) &&
((st.st_mode & 05777) == mode)) {
- result = TRUE;
+ acceptable = TRUE;
}
}
}
}
- return result;
+ return acceptable;
}
*
* 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.
+ * Searches baseTmpDir to see if any subdirectories are suitable to use
+ * as a safe temp directory. The safe temp directory must have the correct
+ * permissions and UID.
*
* Results:
- * Path to discovered safe temp directory (must be freed).
- * NULL returned if no suitable directory is found.
+ * !NULL Path to discovered safe temp directory (must be freed)
+ * NULL No suitable directory was found
*
* Side effects:
* None
*/
static char *
-FileFindExistingSafeTmpDir(uid_t userId, // IN:
+FileFindExistingSafeTmpDir(const char *baseTmpDir, // IN:
const char *userName, // IN:
- const char *baseTmpDir) // IN:
+ uid_t uid) // IN:
{
int i;
int numFiles;
char **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.
+ * 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);
char *path = Unicode_Join(baseTmpDir, DIRSEPS, fileList[i], NULL);
if (File_IsDirectory(path) &&
- FileAcceptableSafeTmpDir(path, userId)) {
+ FileAcceptableSafeTmpDir(path, uid)) {
tmpDir = path;
break;
}
* FileCreateSafeTmpDir --
*
* Creates a new directory within baseTmpDir with the correct permissions
- * and userId to ensure it is safe from symlink attacks.
+ * and UID 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.
+ * !NULL Path to created safe temp directory (must be freed)
+ * NULL No suitable directory was found
*
* Side effects:
* Directory may be created.
*/
static char *
-FileCreateSafeTmpDir(uid_t userId, // IN:
+FileCreateSafeTmpDir(const char *baseTmpDir, // IN:
const char *userName, // IN:
- const char *baseTmpDir) // IN:
+ uid_t uid) // IN:
{
- static const int MAX_DIR_ITERS = 250;
int curDirIter = 0;
char *tmpDir = NULL;
+ static const int MAX_DIR_ITERS = 250;
while (TRUE) {
/*
* an unused name than if we had simply tried suffixes in numeric order.
*/
- tmpDir = Str_Asprintf(NULL, "%s%s%s-%s-%u", baseTmpDir, DIRSEPS,
- PRODUCT_GENERIC_NAME_LOWER, userName,
- FileSimpleRandom());
+ tmpDir = Str_SafeAsprintf(NULL, "%s%s%s-%s-%u", baseTmpDir, DIRSEPS,
+ PRODUCT_GENERIC_NAME_LOWER, userName,
+ FileSimpleRandom());
- if (tmpDir == NULL) {
- Warning("%s: Out of memory error.\n", __FUNCTION__);
- break;
- }
-
- if (FileAcceptableSafeTmpDir(tmpDir, userId)) {
+ if (FileAcceptableSafeTmpDir(tmpDir, uid)) {
break;
}
}
Posix_Free(tmpDir);
- tmpDir = NULL;
}
return tmpDir;
* FileGetSafeTmpDir --
*
* 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).
+ * is not prone to symlink attacks, because it is only writable with the
+ * current set of credentials (EUID).
*
- * Guaranteed to return the same directory, based on the randomTemp
- * boolean argument, every time it is called during the lifetime of
- * the current process, for the current effective user ID. (Barring
- * the user manually deleting or renaming the directory.)
+ * Guaranteed to return the same directory every time it is called during
+ * the lifetime of the current process, for the current effective UID.
+ * (Barring the user manually deleting or renaming the directory.)
+ *
+ * Optionally, add the PID to the user identifier for the cases where
+ * the EUID may change during the lifetime of the calling process.
*
* Results:
- * The allocated directory path on success.
- * NULL on failure.
+ * !NULL Path to safe temp directory (must be freed)
+ * NULL No suitable directory was found
*
* Side effects:
* None.
*/
static char *
-FileGetSafeTmpDir(Bool useConf, // IN:
- Bool randomTemp) // IN:
+FileGetSafeTmpDir(Bool useConf, // IN: Use configuration variables?
+ Bool addPid) // IN: Add PID to userName?
{
char *tmpDir = NULL;
#else
static Atomic_Ptr lckStorage;
static char *safeDir;
- static char *safeRandomDir;
+ static char *safePidDir;
+ MXUserExclLock *lck;
char *testSafeDir;
- char *baseTmpDir = NULL;
char *userName = NULL;
- uid_t userId;
- MXUserExclLock *lck;
-
- userId = geteuid();
+ char *baseTmpDir = NULL;
+ uid_t euid = geteuid();
/* Get and take lock for our safe dir. */
lck = MXUser_CreateSingletonExclLock(&lckStorage, "getSafeTmpDirLock",
MXUser_AcquireExclLock(lck);
/*
- * Based on the randomTemp boolean argument, check if we've created a
+ * Based on the addPid boolean argument, check if we've created a
* temporary dir already and if it is still usable.
*/
- testSafeDir = randomTemp ? safeRandomDir : safeDir;
- if (testSafeDir && FileAcceptableSafeTmpDir(testSafeDir, userId)) {
+ testSafeDir = addPid ? safePidDir : safeDir;
+
+ if ((testSafeDir != NULL) && FileAcceptableSafeTmpDir(testSafeDir, euid)) {
tmpDir = Util_SafeStrdup(testSafeDir);
goto exit;
}
baseTmpDir = FileGetTmpDir(useConf);
if (baseTmpDir == NULL) {
- Warning("%s: FileGetTmpDir failed.\n", __FUNCTION__);
goto exit;
}
- userName = FileGetUserName(userId);
+ userName = FileGetUserIdentifier(euid, addPid);
- if (userName == NULL) {
- 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);
+ tmpDir = Str_SafeAsprintf(NULL, "%s%s%s-%s", baseTmpDir, DIRSEPS,
+ PRODUCT_GENERIC_NAME_LOWER, userName);
- if (userName == NULL) {
- Warning("%s: Str_Asprintf error.\n", __FUNCTION__);
- goto exit;
- }
- }
-
- if (randomTemp) {
+ if (addPid || !FileAcceptableSafeTmpDir(tmpDir, euid)) {
/*
- * Suffix the userName with the PID for the cases where the
- * EUID may toggle during the lifetime of the process.
- */
-
- char *userNameAndPid;
- pid_t pid = getpid();
-
- userNameAndPid = Str_Asprintf(NULL, "%s_%d", userName, pid);
-
- if (userNameAndPid == NULL) {
- Warning("%s: Str_Asprintf error.\n", __FUNCTION__);
- goto exit;
- }
-
- Posix_Free(userName);
- userName = userNameAndPid;
- }
-
- tmpDir = Str_Asprintf(NULL, "%s%s%s-%s", baseTmpDir, DIRSEPS,
- PRODUCT_GENERIC_NAME_LOWER, userName);
-
- if (tmpDir == NULL) {
- Warning("%s: Out of memory error.\n", __FUNCTION__);
- goto exit;
- }
-
- if (randomTemp || !FileAcceptableSafeTmpDir(tmpDir, userId)) {
- /*
- * Either we want a truely random temp directory or 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.
+ * Either we want a truely random temp directory or 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.
*/
Posix_Free(tmpDir);
- tmpDir = FileFindExistingSafeTmpDir(userId, userName, baseTmpDir);
+ tmpDir = FileFindExistingSafeTmpDir(baseTmpDir, userName, euid);
if (tmpDir == NULL) {
/*
* We didn't find any usable directories, so try to create one now.
*/
- tmpDir = FileCreateSafeTmpDir(userId, userName, baseTmpDir);
+ tmpDir = FileCreateSafeTmpDir(baseTmpDir, userName, euid);
}
}
*/
testSafeDir = Util_SafeStrdup(tmpDir);
- if (randomTemp) {
- Posix_Free(safeRandomDir);
- safeRandomDir = testSafeDir;
+
+ if (addPid) {
+ Posix_Free(safePidDir);
+ safePidDir = testSafeDir;
} else {
Posix_Free(safeDir);
safeDir = testSafeDir;
}
}
- exit:
+exit:
+
MXUser_ReleaseExclLock(lck);
Posix_Free(baseTmpDir);
Posix_Free(userName);
* 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).
+ * is not prone to symlink attacks, because it is only writable with the
+ * current set of credentials (EUID).
*
- * Guaranteed to return the same directory every time it is
- * called during the lifetime of the current process, for the
- * current effective user ID. (Barring the user manually deleting
- * or renaming the directory.)
+ * Guaranteed to return the same directory every time it is called during
+ * the lifetime of the current process, for the current effective UID.
+ * (Barring the user manually deleting or renaming the directory.)
*
* Results:
- * The allocated directory path on success.
- * NULL on failure.
+ * !NULL Path to safe temp directory (must be freed)
+ * NULL No suitable directory was found
*
* Side effects:
* None.
char *
File_GetSafeTmpDir(Bool useConf) // IN:
{
- return FileGetSafeTmpDir(useConf, FALSE);
+ return FileGetSafeTmpDir(useConf, FALSE);
}
* File_GetSafeRandomTmpDir --
*
* Return a safe, random temporary directory (i.e. a temporary directory
- * which is not prone to symlink attacks, because it is only writable
- * by the current effective user).
+ * is not prone to symlink attacks, because it is only writable with the
+ * current set of credentials (EUID).
*
- * Guaranteed to return the same directory every time it is
- * called during the lifetime of the current process, for the
- * current effective user ID. (Barring the user manually deleting
- * or renaming the directory.)
+ * Guaranteed to return the same directory every time it is called during
+ * the lifetime of the current process, for the current effective UID.
+ * (Barring the user manually deleting or renaming the directory.)
*
* Results:
- * The allocated directory path on success.
- * NULL on failure.
+ * !NULL Path to safe temp directory (must be freed).
+ * NULL No suitable directory was found.
*
* Side effects:
* None.