]> git.saurik.com Git - apple/libc.git/blobdiff - darwin/_dirhelper.c
Libc-997.1.1.tar.gz
[apple/libc.git] / darwin / _dirhelper.c
index 04749f660a0311e468e9d4fa8eec936932d6e43c..bec1e1d10e7a65dfba47a664e95f75b4b80e1e76 100644 (file)
@@ -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@
  *
 #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
@@ -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