/*
- * Copyright (c) 2006, 2007, 2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2006, 2007, 2009-2013 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <servers/bootstrap.h>
+#include <bootstrap_priv.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
+#include <mach-o/dyld_priv.h>
#include <membership.h>
#include <pthread.h>
#include <errno.h>
#include <TargetConditionals.h>
#include <xpc/xpc.h>
#include <xpc/private.h>
+#if !TARGET_OS_IPHONE
+#include <CrashReporterClient.h>
+#endif /* !TARGET_OS_IPHONE */
#include "dirhelper.h"
#include "dirhelper_priv.h"
#define BUCKETLEN 2
-#define MUTEX_LOCK(x) if(__is_threaded) pthread_mutex_lock(x)
-#define MUTEX_UNLOCK(x) if(__is_threaded) pthread_mutex_unlock(x)
+#define MUTEX_LOCK(x) pthread_mutex_lock(x)
+#define MUTEX_UNLOCK(x) pthread_mutex_unlock(x)
// Use 5 bits per character, to avoid uppercase and shell magic characters
#define ENCODEBITS 5
#define MASK(x) ((1 << (x)) - 1)
#define UUID_UID_SIZE (sizeof(uuid_t) + sizeof(uid_t))
-extern int __is_threaded;
-
static const mode_t modes[] = {
0755, /* user */
0700, /* temp */
// 32 characters.
static const char encode[] = "0123456789_bcdfghjklmnpqrstvwxyz";
+#if TARGET_OS_IPHONE
+
+#define setcrashlogmessage(fmt, ...) /* nothing */
+
+#else /* !TARGET_OS_IPHONE */
+
static void
encode_uuid_uid(const uuid_t uuid, uid_t uid, char *str)
{
*str = 0;
}
+static void
+_setcrashlogmessage(const char *fmt, ...) __attribute__((__format__(__printf__,1,2)))
+{
+ char *mess = NULL;
+ int res;
+ va_list ap;
+
+ va_start(ap, fmt);
+ res = vasprintf(&mess, fmt, ap);
+ va_end(ap);
+ if (res < 0)
+ mess = (char *)fmt; /* the format string is better than nothing */
+ CRSetCrashLogMessage(mess);
+}
+
+#define setcrashlogmessage(fmt, ...) _setcrashlogmessage("%s: %u: " fmt, __func__, __LINE__, ##__VA_ARGS__)
+
+#endif /* !TARGET_OS_IPHONE */
+
char *
__user_local_dirname(uid_t uid, dirhelper_which_t which, char *path, size_t pathlen)
{
#endif
int res;
- if(which < 0 || which > DIRHELPER_USER_LOCAL_LAST) {
+ if((int)which < 0 || which > DIRHELPER_USER_LOCAL_LAST) {
+ setcrashlogmessage("Out of range: which=%d", (int)which);
errno = EINVAL;
return NULL;
}
if(which == DIRHELPER_USER_LOCAL_TEMP) {
tmpdir = getenv("TMPDIR");
if(!tmpdir) {
+ setcrashlogmessage("TMPDIR not set");
errno = EINVAL;
return NULL;
}
res = snprintf(path, pathlen, "%s", tmpdir);
} else {
+ setcrashlogmessage("Only DIRHELPER_USER_LOCAL_TEMP is supported: which=%d", (int)which);
errno = EINVAL;
return NULL;
}
#else
res = mbr_uid_to_uuid(uid, uuid);
if(res != 0) {
+ setcrashlogmessage("mbr_uid_to_uuid returned %d, uid=%d", res, (int)uid);
errno = res;
return NULL;
}
VAR_FOLDERS_PATH, BUCKETLEN, str, str + BUCKETLEN, subdirs[which]);
#endif
if(res >= pathlen) {
+ setcrashlogmessage("snprintf: buffer too small: res=%d >= pathlen=%zu", res, pathlen);
errno = EINVAL;
return NULL; /* buffer too small */
}
while ((next = strchr(next, '/')) != NULL) {
*next = 0; // temporarily truncate
res = mkdir(path, 0755);
- if (res != 0 && errno != EEXIST)
+ if (res != 0 && errno != EEXIST) {
+ setcrashlogmessage("mkdir: path=%s mode=0755: %s", path, strerror(errno));
return NULL;
+ }
*next++ = '/'; // restore the slash and increment
}
return path;
* There is a rare case when launchd will have userdir set, and child process
* will sometimes inherit this cached value.
*/
+__private_extern__ void _dirhelper_fork_child(void);
__private_extern__ void
_dirhelper_fork_child(void)
{
if(userdir) *userdir = 0;
}
+__private_extern__ char *_dirhelper(dirhelper_which_t which, char *path, size_t pathlen);
__private_extern__ char *
_dirhelper(dirhelper_which_t which, char *path, size_t pathlen)
{
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
struct stat sb;
- if(which < 0 || which > DIRHELPER_USER_LOCAL_LAST) {
+ if((int)which < 0 || which > DIRHELPER_USER_LOCAL_LAST) {
+ setcrashlogmessage("Out of range: which=%d", (int)which);
errno = EINVAL;
return NULL;
}
if (pthread_once(&userdir_control, userdir_allocate)
|| !userdir) {
+ setcrashlogmessage("Out of memory in userdir_allocate");
errno = ENOMEM;
return NULL;
}
mach_port_t mp;
if(errno != ENOENT) { /* some unknown error */
+ setcrashlogmessage("stat: %s: %s", userdir, strerror(errno));
*userdir = 0;
MUTEX_UNLOCK(&lock);
return NULL;
return NULL;
}
} else {
- if(bootstrap_look_up(bootstrap_port, DIRHELPER_BOOTSTRAP_NAME, &mp) != KERN_SUCCESS) {
- errno = EPERM;
+ kern_return_t res;
+#if TARGET_IPHONE_SIMULATOR
+ res = bootstrap_look_up(bootstrap_port, DIRHELPER_BOOTSTRAP_NAME, &mp);
+#else
+ res = bootstrap_look_up2(bootstrap_port, DIRHELPER_BOOTSTRAP_NAME, &mp, 0, BOOTSTRAP_PRIVILEGED_SERVER);
+#endif
+
+ if(res != KERN_SUCCESS) {
+ setcrashlogmessage("bootstrap_look_up returned %d", res);
+ errno = EPERM;
server_error:
- mach_port_deallocate(mach_task_self(), mp);
- MUTEX_UNLOCK(&lock);
- return NULL;
+ mach_port_deallocate(mach_task_self(), mp);
+ MUTEX_UNLOCK(&lock);
+ return NULL;
}
- if(__dirhelper_create_user_local(mp) != KERN_SUCCESS) {
- errno = EPERM;
- goto server_error;
+ if((res = __dirhelper_create_user_local(mp)) != KERN_SUCCESS) {
+ setcrashlogmessage("__dirhelper_create_user_local returned %d", res);
+ errno = EPERM;
+ goto server_error;
}
/* double check that the directory really got created */
if(stat(userdir, &sb) < 0) {
- goto server_error;
+ setcrashlogmessage("stat: %s: %s", userdir, strerror(errno));
+ goto server_error;
}
mach_port_deallocate(mach_task_self(), mp);
}
}
if(pathlen < strlen(userdir) + strlen(subdirs[which]) + 1) {
+ setcrashlogmessage("buffer too small: pathlen=%zu userdir=%s subdirs[%d]=%s", pathlen, userdir, which, subdirs[which]);
errno = EINVAL;
return NULL; /* buffer too small */
}
#if !TARGET_OS_IPHONE
if (!_xpc_runtime_is_app_sandboxed())
#endif
- if(mkdir(path, modes[which]) != 0 && errno != EEXIST)
+ if(mkdir(path, modes[which]) != 0 && errno != EEXIST) {
+ setcrashlogmessage("mkdir: path=%s modes[%d]=0%o: %s", path, which, modes[which], strerror(errno));
return NULL;
+ }
#if !TARGET_OS_IPHONE
char *userdir_suffix = NULL;
* permission to create it ourselves.
*/
if(stat(path, &sb) < 0) {
+ setcrashlogmessage("stat: %s: %s", path, strerror(errno));
errno = EPERM;
return NULL;
}
*/
userdir_suffix = getenv(XPC_ENV_SANDBOX_CONTAINER_ID);
if (!userdir_suffix) {
+ setcrashlogmessage("XPC_ENV_SANDBOX_CONTAINER_ID not set");
errno = EINVAL;
return NULL;
}
- } else
+ } else if (!dyld_process_is_restricted()) {
userdir_suffix = getenv(DIRHELPER_ENV_USER_DIR_SUFFIX);
+ }
if (userdir_suffix) {
+ /*
+ * do not allow paths that contain path traversal dots.
+ */
+ const char *pos = userdir_suffix;
+ while ((pos = strnstr(pos, "..", strlen(pos)))) {
+ if ((pos == userdir_suffix && strlen(userdir_suffix) == 2) || // string is ".." only
+ (pos == userdir_suffix && strlen(userdir_suffix) > 2 && userdir_suffix[2] == '/') || // prefixed with "../"
+ ((pos - userdir_suffix == strlen(userdir_suffix) - 2) && pos[-1] == '/') || // suffixed with "/.."
+ (pos[-1] == '/' && pos[2] == '/')) // middle of string with '/../'
+ {
+ setcrashlogmessage("illegal path traversal (..) pattern found in DIRHELPER_USER_DIR_SUFFIX");
+ errno = EINVAL;
+ return NULL;
+ }
+ pos += 2;
+ }
+
/*
* suffix (usually container ID) doesn't end in a slash, so +2 is for slash and \0
*/
if (pathlen < strlen(path) + strlen(userdir_suffix) + 2) {
+ setcrashlogmessage("buffer too small: pathlen=%zu path=%s userdir_suffix=%s", pathlen, path, userdir_suffix);
errno = EINVAL;
return NULL; /* buffer too small */
}
* create suffix subdirectory with the appropriate permissions
* if it doesn't already exist.
*/
- if (mkdir(path, modes[which]) != 0 && errno != EEXIST)
+ if (mkdir(path, modes[which]) != 0 && errno != EEXIST) {
+ setcrashlogmessage("mkdir: path=%s modes[%d]=0%o: %s", path, which, modes[which], strerror(errno));
return NULL;
+ }
/*
* update TMPDIR if necessary