X-Git-Url: https://git.saurik.com/apple/libc.git/blobdiff_plain/a28bf75d63c6a64e4c3b417c6052e45f42c6cedd..6465356a983ac139f81d3b7913cdb548477c346c:/darwin/_dirhelper.c diff --git a/darwin/_dirhelper.c b/darwin/_dirhelper.c index 04749f6..bec1e1d 100644 --- a/darwin/_dirhelper.c +++ b/darwin/_dirhelper.c @@ -1,5 +1,5 @@ /* - * 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@ * @@ -24,10 +24,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -40,14 +42,17 @@ #include #include #include +#if !TARGET_OS_IPHONE +#include +#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 @@ -55,8 +60,6 @@ #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 */ @@ -76,6 +79,12 @@ static char *userdir = NULL; // 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) { @@ -127,6 +136,25 @@ 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) { @@ -138,7 +166,8 @@ __user_local_dirname(uid_t uid, dirhelper_which_t which, char *path, size_t path #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; } @@ -153,17 +182,20 @@ __user_local_dirname(uid_t uid, dirhelper_which_t which, char *path, size_t path 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; } @@ -180,6 +212,7 @@ __user_local_dirname(uid_t uid, dirhelper_which_t which, char *path, size_t path 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 */ } @@ -196,8 +229,10 @@ __user_local_mkdir_p(char *path) 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; @@ -213,25 +248,29 @@ static void userdir_allocate(void) * 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; } @@ -260,6 +299,7 @@ _dirhelper(dirhelper_which_t which, char *path, size_t pathlen) mach_port_t mp; if(errno != ENOENT) { /* some unknown error */ + setcrashlogmessage("stat: %s: %s", userdir, strerror(errno)); *userdir = 0; MUTEX_UNLOCK(&lock); return NULL; @@ -274,20 +314,30 @@ _dirhelper(dirhelper_which_t which, char *path, size_t pathlen) 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); } @@ -297,6 +347,7 @@ _dirhelper(dirhelper_which_t which, char *path, size_t pathlen) } 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 */ } @@ -311,8 +362,10 @@ _dirhelper(dirhelper_which_t which, char *path, size_t pathlen) #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; @@ -323,6 +376,7 @@ _dirhelper(dirhelper_which_t which, char *path, size_t pathlen) * permission to create it ourselves. */ if(stat(path, &sb) < 0) { + setcrashlogmessage("stat: %s: %s", path, strerror(errno)); errno = EPERM; return NULL; } @@ -333,17 +387,37 @@ _dirhelper(dirhelper_which_t which, char *path, size_t pathlen) */ 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 */ } @@ -355,8 +429,10 @@ _dirhelper(dirhelper_which_t which, char *path, size_t pathlen) * 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