【问题标题】:Mac sandbox: running a binary tool that needs /tmpMac 沙箱:运行需要 /tmp 的二进制工具
【发布时间】:2014-10-18 03:09:04
【问题描述】:

我有一个沙盒化的 Cocoa 应用程序,在导出过程中,它需要运行第三方命令行工具。这个工具似乎被硬编码为使用/tmp 作为它的临时文件;沙盒不允许访问此文件夹,因此导出失败。

我怎样才能让这个工具运行?我无权访问它的源代码,所以我无法修改它以使用NSTemporaryDirectory(),而且它似乎不尊重TMPTEMPDIR 环境变量。由于我不明白的原因,给自己一个com.apple.security.temporary-exception.files.absolute-path.read-write 权利似乎也不起作用。

有没有办法在我的沙箱中重新映射文​​件夹?我可以使用一些晦涩的技巧吗?我应该尝试以某种方式修补该工具的二进制文件吗?到这里我就束手无策了。

【问题讨论】:

  • 我立即想到的一个解决方案是使用DYLD_INSERT_LIBRARIES,如所述,例如here 并覆盖 open/fopen 和其他相关的系统调用
  • 另外你可能需要dtrace那个第三方工具,检查你需要覆盖哪些系统调用。 dtruss 似乎是 DTrace MacOSX 内核工具的少数命令行前端。
  • 你能控制访问/tmp的工具如何启动吗?如果您可以用自定义脚本替换它的调用,那么您就有了解决方案。如果不是,则需要一些额外的技巧。
  • 我正在通过NSTask 启动该工具。大约一年前我尝试了DYLD_INSERT_LIBRARIES,但它没有用,因为(信不信由你)-[NSTask setEnvironment:] 似乎没有做任何事情,至少在沙箱中是这样。但是,在启动之前调用 POSIX setenv(3) 似乎至少设置了可以使用 /usr/bin/env 看到的环境变量。我明天可以试试这个。
  • 知道了! @user3159253,如果你写一些关于 DYLD_INSERT_LIBRARIES 的东西,我很乐意给你赏金。

标签: c macos appstore-sandbox


【解决方案1】:

我能够让 user3159253 的DYLD_INSERT_LIBRARIES 工作。我希望他们会写一个答案来描述它是如何工作的,所以我将省略细节并解释最终针对此案例的部分。

感谢 LLDB、麻烦事以及 Hopper 的帮助,我能够确定第三方工具使用 mkstemp() 生成其临时文件名,并且使用了一些调用(不是全部)以/tmp 开头的固定模板。然后我写了一个libtmphack.dylib,它拦截了对mkstemp()的调用,并在调用标准库版本之前修改了参数。

由于mkstemp() 使用一个指向预分配缓冲区的指针,我不觉得我可以将一个以“/tmp”之类的短字符串开头的路径重写为到达 Caches 文件夹中所需的非常长的字符串沙盒。相反,我选择在当前工作目录中创建一个名为“$tmp”的符号链接。如果工具chdir()在不合时宜的时间出现,这可能会中断,但幸运的是它似乎没有这样做。

这是我的代码:

//
//  libtmphack.c
//  Typesetter
//
//  Created by Brent Royal-Gordon on 8/27/14.
//  Copyright (c) 2014 Groundbreaking Software. This file is MIT licensed.
//

#include "libtmphack.h"
#include <dlfcn.h>
#include <stdlib.h>
#include <unistd.h>
//#include <errno.h>
#include <string.h>

static int gbs_has_prefix(char * needle, char * haystack) {
    return strncmp(needle, haystack, strlen(needle)) == 0;
}

int mkstemp(char *template) {
    static int (*original_mkstemp)(char * template) = NULL;

    if(!original_mkstemp) {
        original_mkstemp = dlsym(RTLD_NEXT, "mkstemp");
    }

    if(gbs_has_prefix("/tmp", template)) {
        printf("libtmphack: rewrote mkstemp(\"%s\") ", template);
        template[0] = '$';
        printf("to mkstemp(\"%s\")\n", template);

        // If this isn't successful, we'll presume it's because it's already been made
        symlink(getenv("TEMP"), "$tmp");

        int ret = original_mkstemp(template);

        // Can't do this, the caller needs to be able to open the file
        // int retErrno = errno;
        // unlink("$tmp");
        // errno = retErrno;

        return ret;
    }
    else {
        printf("libtmphack: OK with mkstemp(\"%s\")\n", template);
        return original_mkstemp(template);
    }
}

非常快速和肮脏,但它就像一个魅力。

【讨论】:

  • 快速优雅,来自更文明的时代。
  • @Prof.Falken 我写 Objective-C 已经七年了;在此之前,我在一个大型开源项目(解释器)中编写了 C,其中包含相当广泛的库和抽象层;在此之前,我学习了 C++ 计算机科学。但我认为这是我用普通的旧 C 语言编写的第一个没有花哨的库来帮助我的代码。
  • 哈哈,有了这样的背景,我可以看到普通的 unix-ish API 看起来既快又脏。 :)
【解决方案2】:

由于@BrentRoyal-Gordon 已经发布了一个可行的解决方案,我只是复制了我的评论,这激发了他提出解决方案:

为了修复程序行为,我将在 DYLD_INSERT_LIBRARIES 和一个自定义共享库的帮助下拦截并覆盖一些系统调用,该库具有给定系统调用的自定义实现。

需要重写的系统调用的确切列表取决于应用程序的性质,并且可以使用基于 MacOS DTrace 内核工具构建的许多工具进行研究。例如。 dtrussHopper。 @BrentRoyal-Gordon 已调查该应用程序只能通过 mkstemp 的/适当/实现来修复。

就是这样。我仍然不确定我是否应该得到赏金:)

【讨论】:

    【解决方案3】:

    另一种解决方案是在子进程中使用chroot(或posix_spawn 选项)将其根目录更改为沙箱中的目录。然后,它的“/tmp”将成为该目录中的“tmp”目录。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-03
      • 2018-05-20
      • 1970-01-01
      • 2021-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多