mirror of
https://github.com/darlinghq/darling.git
synced 2024-11-27 14:20:24 +00:00
Implement the new 'darling' executable
This commit is contained in:
parent
4922fc9028
commit
9f0a3d5400
@ -164,7 +164,7 @@ Congratulations, you have just compiled and run your own Hello world application
|
||||
|
||||
### AppKit
|
||||
|
||||
AppKit is still highly expiramental and incomplete, but to work on it you need to configure CMake with `-DFRAMEWORK_APPKIT=1` and install some additional packages.
|
||||
AppKit is still highly expiramental and incomplete, but to work on it you need to configure CMake with `-DFRAMEWORK_APPKIT=1` and install some additional packages.
|
||||
|
||||
Ubuntu 16.04:
|
||||
```
|
||||
|
@ -38,94 +38,230 @@ along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "darling-config.h"
|
||||
|
||||
const char* DARLING_INIT_COMM = "darling-init";
|
||||
uid_t g_originalUid;
|
||||
char *prefix;
|
||||
uid_t g_originalUid, g_originalGid;
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
const char* dprefix;
|
||||
int pidInit;
|
||||
|
||||
pid_t pidInit, pidChild;
|
||||
char path[4096];
|
||||
int wstatus;
|
||||
|
||||
if (argc <= 1)
|
||||
{
|
||||
showHelp(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*if (geteuid() != 0)
|
||||
|
||||
if (geteuid() != 0)
|
||||
{
|
||||
missingSetuidRoot();
|
||||
return 1;
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
if (loadKernelModule())
|
||||
return 1;
|
||||
|
||||
// Temporarily drop root privileges
|
||||
g_originalUid = getuid();
|
||||
seteuid(getuid());
|
||||
setegid(getgid());
|
||||
|
||||
dprefix = getenv("DPREFIX");
|
||||
if (!dprefix)
|
||||
dprefix = defaultPrefixPath();
|
||||
if (!dprefix)
|
||||
g_originalGid = getgid();
|
||||
|
||||
prefix = getenv("DPREFIX");
|
||||
if (!prefix)
|
||||
prefix = defaultPrefixPath();
|
||||
if (!prefix)
|
||||
return 1;
|
||||
checkPrefixOwner(dprefix);
|
||||
|
||||
pidInit = getInitProcess(dprefix);
|
||||
|
||||
setenv("DPREFIX", prefix, 0);
|
||||
|
||||
if (!checkPrefixDir())
|
||||
setupPrefix();
|
||||
checkPrefixOwner();
|
||||
|
||||
pidInit = getInitProcess();
|
||||
|
||||
// If prefix's init is not running, start it up
|
||||
if (pidInit == 0)
|
||||
{
|
||||
char* opts;
|
||||
|
||||
createDir(dprefix);
|
||||
setupWorkdir(dprefix);
|
||||
|
||||
// Since overlay cannot be mounted inside user namespaces, we have to setup a new mount namespace
|
||||
// and do the mount while we can be root
|
||||
//seteuid(0);
|
||||
if (unshare(CLONE_NEWNS) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot unshare(CLONE_NEWNS): %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Because IDIOTIC systemd marks / as MS_SHARED and we would inherit this into the overlay mount,
|
||||
// causing it not to be unmounted once the init process dies.
|
||||
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot remount / as private: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
opts = (char*) malloc(strlen(dprefix)*2 + sizeof(LIBEXEC_PATH) + 50);
|
||||
sprintf(opts, "lowerdir=%s,upperdir=%s,workdir=%s.workdir", LIBEXEC_PATH, dprefix, dprefix);
|
||||
|
||||
// Mount overlay onto our prefix
|
||||
if (mount("overlay", dprefix, "overlay", 0, opts) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot mount overlay: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(opts);
|
||||
//seteuid(getuid());
|
||||
|
||||
pidInit = spawnInitProcess(dprefix);
|
||||
setupWorkdir();
|
||||
pidInit = spawnInitProcess();
|
||||
putInitPid(pidInit);
|
||||
}
|
||||
|
||||
// TODO: Spawn the executable in the namespace, wait for it and return its exit code
|
||||
unloadKernelModule();
|
||||
|
||||
if (strcmp(argv[1], "shell") != 0)
|
||||
{
|
||||
const char *argv_child[argc + 1];
|
||||
|
||||
argv_child[0] = "dyld";
|
||||
for (int i = 1; i < argc; i++)
|
||||
argv_child[i] = argv[i];
|
||||
argv_child[argc] = NULL;
|
||||
|
||||
pidChild = spawnChild(pidInit, "/usr/local/bin/dyld", argv_child);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Spawn the shell
|
||||
snprintf(path, sizeof(path), "%s/bin/bash", prefix);
|
||||
if (argc > 2)
|
||||
{
|
||||
size_t total_len = 0;
|
||||
for (int i = 2; i < argc; i++)
|
||||
total_len += strlen(argv[i]);
|
||||
|
||||
char buffer[total_len + argc];
|
||||
|
||||
char *to = buffer;
|
||||
for (int i = 2; i < argc; i++)
|
||||
to = stpcpy(stpcpy(to, argv[i]), " ");
|
||||
// Overwrite the last whitespace
|
||||
*(to - 1) = '\0';
|
||||
|
||||
pidChild = spawnChild(pidInit, "/usr/local/bin/dyld",
|
||||
(const char *[5]) {"dyld", path, "-c", buffer, NULL});
|
||||
}
|
||||
else
|
||||
pidChild = spawnChild(pidInit, "/usr/local/bin/dyld",
|
||||
(const char *[3]) {"dyld", path, NULL});
|
||||
}
|
||||
|
||||
// Drop the privileges so that we can be killed, etc by the user
|
||||
seteuid(g_originalUid);
|
||||
|
||||
waitpid(pidChild, &wstatus, 0);
|
||||
|
||||
// Should we unloadKernelModule() here? Others may be still using it
|
||||
if (WIFEXITED(wstatus))
|
||||
return WEXITSTATUS(wstatus);
|
||||
if (WIFSIGNALED(wstatus))
|
||||
return WTERMSIG(wstatus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pid_t spawnChild(int pidInit, const char *path, const char *const argv[])
|
||||
{
|
||||
int fdNS;
|
||||
pid_t pidChild;
|
||||
char pathNS[4096], curPath[4096];
|
||||
|
||||
if (getcwd(curPath, sizeof(curPath)) == NULL)
|
||||
{
|
||||
fprintf(stderr, "Cannot get current directory: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
snprintf(pathNS, sizeof(pathNS), "/proc/%d/ns/pid", pidInit);
|
||||
|
||||
fdNS = open(pathNS, O_RDONLY);
|
||||
|
||||
if (fdNS < 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot open PID namespace file: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Calling setns() with a PID namespace doesn't move our process into it,
|
||||
// but our child process will be spawned inside the namespace
|
||||
if (setns(fdNS, CLONE_NEWPID) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot join PID namespace: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
close(fdNS);
|
||||
|
||||
pidChild = fork();
|
||||
if (pidChild < 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot spawn a child process: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pidChild == 0)
|
||||
{
|
||||
// This is the child process
|
||||
|
||||
// We still have the outside PIDs in /proc
|
||||
snprintf(pathNS, sizeof(pathNS), "/proc/%d/ns/mnt", pidInit);
|
||||
fdNS = open(pathNS, O_RDONLY);
|
||||
if (fdNS < 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot open mount namespace file: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (setns(fdNS, CLONE_NEWNS) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot join mount namespace: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
close(fdNS);
|
||||
|
||||
snprintf(pathNS, sizeof(pathNS), "/proc/%d/ns/user", pidInit);
|
||||
fdNS = open(pathNS, O_RDONLY);
|
||||
if (fdNS < 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot open user namespace file: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
setresuid(g_originalUid, g_originalUid, g_originalUid);
|
||||
setresgid(g_originalGid, g_originalGid, g_originalGid);
|
||||
|
||||
if (setns(fdNS, CLONE_NEWUSER) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot join user namespace: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
close(fdNS);
|
||||
|
||||
setupChild(curPath);
|
||||
|
||||
execv(path, (char * const *) argv);
|
||||
|
||||
fprintf(stderr, "Cannot exec the target program: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return pidChild;
|
||||
}
|
||||
|
||||
void setupChild(const char *curPath)
|
||||
{
|
||||
char buffer1[4096];
|
||||
char buffer2[4096];
|
||||
|
||||
setenv("PATH", "/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin", 1);
|
||||
|
||||
sscanf(getenv("HOME"), "/home/%4096s", buffer1);
|
||||
snprintf(buffer2, sizeof(buffer2), "/Users/%s", buffer1);
|
||||
setenv("HOME", buffer2, 1);
|
||||
|
||||
if (sscanf(curPath, "/home/%4096s", buffer1) == 1)
|
||||
{
|
||||
// We're currently inside our home directory
|
||||
snprintf(buffer2, sizeof(buffer2), "/Users/%s", buffer1);
|
||||
setenv("PWD", buffer2, 1);
|
||||
|
||||
snprintf(buffer2, sizeof(buffer2), "%s/Users/%s", prefix, buffer1);
|
||||
chdir(buffer2);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buffer2, sizeof(buffer2), "/system-root%s", curPath);
|
||||
setenv("PWD", buffer2, 1);
|
||||
|
||||
snprintf(buffer2, sizeof(buffer2), "%s/system-root%s", prefix, curPath);
|
||||
chdir(buffer2);
|
||||
}
|
||||
}
|
||||
|
||||
void showHelp(const char* argv0)
|
||||
{
|
||||
fprintf(stderr, "This is Darling, translation layer for macOS software.\n\n");
|
||||
fprintf(stderr, "Copyright (C) 2012-2016 Lubos Dolezel\n\n");
|
||||
|
||||
fprintf(stderr, "Usage: %s program-path [arguments...]\n\n", argv0);
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, "\t%s program-path [arguments...]\n", argv0);
|
||||
fprintf(stderr, "\t%s shell [arguments...]\n", argv0);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Environment variables:\n"
|
||||
"DPREFIX - specifies the location of Darling prefix, defaults to ~/.darling\n");
|
||||
}
|
||||
@ -134,80 +270,184 @@ void missingSetuidRoot(void)
|
||||
{
|
||||
char path[4096];
|
||||
int len;
|
||||
|
||||
|
||||
len = readlink("/proc/self/exe", path, sizeof(path)-1);
|
||||
if (len < 0)
|
||||
strcpy(path, "darling");
|
||||
else
|
||||
path[len] = '\0';
|
||||
|
||||
|
||||
fprintf(stderr, "Sorry, the `%s' binary is not setuid root, which is mandatory.\n", path);
|
||||
fprintf(stderr, "Darling needs this in order to create mount and PID namespaces and to perform mounts.\n");
|
||||
}
|
||||
|
||||
int spawnInitProcess(const char* prefix)
|
||||
pid_t spawnInitProcess(void)
|
||||
{
|
||||
char* childStack;
|
||||
int pid;
|
||||
char uidmap[100];
|
||||
|
||||
childStack = (char*) malloc(16*1024);
|
||||
childStack += 16*1024;
|
||||
|
||||
//setuid(0);
|
||||
pid = clone(darlingPreInit, childStack, /*CLONE_NEWPID |*/ CLONE_NEWUSER, (void*) prefix);
|
||||
|
||||
if (pid <= 0)
|
||||
pid_t pid;
|
||||
int pipefd[2];
|
||||
char idmap[100];
|
||||
char buffer[1];
|
||||
FILE *file;
|
||||
|
||||
if (pipe(pipefd) == -1)
|
||||
{
|
||||
fprintf(stderr, "Cannot clone() to create darling-init: %s\n", strerror(errno));
|
||||
fprintf(stderr, "Cannot create a pipe for synchronization: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sprintf(uidmap, "/proc/%d/uid_map", pid);
|
||||
|
||||
FILE* file = fopen(uidmap, "w");
|
||||
|
||||
if (unshare(CLONE_NEWPID) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot unshare pid namespace to create darling-init: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid < 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot fork() to create darling-init: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
// The child
|
||||
|
||||
char *opts;
|
||||
|
||||
// Since overlay cannot be mounted inside user namespaces, we have to setup a new mount namespace
|
||||
// and do the mount while we can be root
|
||||
if (unshare(CLONE_NEWNS) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot unshare mount namespace: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Because systemd marks / as MS_SHARED and we would inherit this into the overlay mount,
|
||||
// causing it not to be unmounted once the init process dies.
|
||||
if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot remount / as slave: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
opts = (char*) malloc(strlen(prefix)*2 + sizeof(LIBEXEC_PATH) + 50);
|
||||
sprintf(opts, "lowerdir=%s,upperdir=%s,workdir=%s.workdir", LIBEXEC_PATH, prefix, prefix);
|
||||
|
||||
// Mount overlay onto our prefix
|
||||
if (mount("overlay", prefix, "overlay", 0, opts) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot mount overlay: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(opts);
|
||||
|
||||
// Drop the privileges
|
||||
setresuid(g_originalUid, g_originalUid, g_originalUid);
|
||||
setresgid(g_originalGid, g_originalGid, g_originalGid);
|
||||
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
|
||||
|
||||
prctl(PR_SET_NAME, DARLING_INIT_COMM, 0, 0);
|
||||
|
||||
if (unshare(CLONE_NEWUSER) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot unshare user namespace: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Tell the parent we're ready for it to set up UID/GID mappings
|
||||
write(pipefd[1], buffer, 1);
|
||||
close(pipefd[1]);
|
||||
// And wait for it to do it
|
||||
read(pipefd[0], buffer, 1);
|
||||
close(pipefd[0]);
|
||||
|
||||
darlingPreInit();
|
||||
// Never returns
|
||||
}
|
||||
|
||||
// Wait for the child to drop UID/GIDs and unshare stuff
|
||||
read(pipefd[0], buffer, 1);
|
||||
close(pipefd[0]);
|
||||
|
||||
snprintf(idmap, sizeof(idmap), "/proc/%d/uid_map", pid);
|
||||
|
||||
file = fopen(idmap, "w");
|
||||
if (file != NULL)
|
||||
{
|
||||
fprintf(file, "0 %d 1\n", getuid()); // all users map to our user on the outside
|
||||
fprintf(file, "0 %d 1\n", g_originalUid); // all users map to our user on the outside
|
||||
fclose(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Failure setting uid_map in init process\n");
|
||||
fprintf(stderr, "Cannot set uid_map for the init process: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
seteuid(g_originalUid);
|
||||
setuid(g_originalUid);
|
||||
|
||||
|
||||
snprintf(idmap, sizeof(idmap), "/proc/%d/gid_map", pid);
|
||||
|
||||
file = fopen(idmap, "w");
|
||||
if (file != NULL)
|
||||
{
|
||||
fprintf(file, "0 %d 1\n", g_originalGid); // all groups map to our group on the outside
|
||||
fclose(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Cannot set gid_map for the init process: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
// Resume the child
|
||||
write(pipefd[1], buffer, 1);
|
||||
close(pipefd[1]);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
int darlingPreInit(void* arg)
|
||||
void putInitPid(pid_t pidInit)
|
||||
{
|
||||
const char pidFile[] = "/.init.pid";
|
||||
char* pidPath;
|
||||
FILE *fp;
|
||||
|
||||
pidPath = (char*) alloca(strlen(prefix) + sizeof(pidFile));
|
||||
strcpy(pidPath, prefix);
|
||||
strcat(pidPath, pidFile);
|
||||
|
||||
seteuid(g_originalUid);
|
||||
setegid(g_originalGid);
|
||||
|
||||
fp = fopen(pidPath, "w");
|
||||
|
||||
seteuid(0);
|
||||
setegid(0);
|
||||
|
||||
if (fp == NULL)
|
||||
{
|
||||
fprintf(stderr, "Cannot write out PID of the init process: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
fprintf(fp, "%d", (int) pidInit);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void darlingPreInit(void)
|
||||
{
|
||||
const char* prefix = (const char*) arg;
|
||||
|
||||
prctl(PR_SET_NAME, DARLING_INIT_COMM, 0, 0);
|
||||
|
||||
// Wait until the parent sets our uid_map
|
||||
while (getuid() == 65534);
|
||||
|
||||
// TODO: Run /usr/libexec/makewhatis
|
||||
|
||||
|
||||
// TODO: this is where we will exec() launchd in future.
|
||||
// Instead, we just reap zombies.
|
||||
while (1)
|
||||
{
|
||||
int status, sig;
|
||||
sigset_t chld;
|
||||
|
||||
|
||||
sigemptyset(&chld);
|
||||
sigaddset(&chld, SIGCHLD);
|
||||
sigwait(&chld, &sig);
|
||||
|
||||
|
||||
while (waitpid(-1, &status, 0) != -1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* defaultPrefixPath(void)
|
||||
@ -215,24 +455,24 @@ char* defaultPrefixPath(void)
|
||||
const char defaultPath[] = "/.darling";
|
||||
const char* home = getenv("HOME");
|
||||
char* buf;
|
||||
|
||||
|
||||
if (!home)
|
||||
{
|
||||
fprintf(stderr, "Cannot detect your home directory!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
buf = (char*) malloc(strlen(home) + sizeof(defaultPath));
|
||||
strcpy(buf, home);
|
||||
strcat(buf, defaultPath);
|
||||
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void createDir(const char* path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
|
||||
if (stat(path, &st) == 0)
|
||||
{
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
@ -253,56 +493,140 @@ void createDir(const char* path)
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Problem accessing %s: %s\n", path, strerror(errno));
|
||||
fprintf(stderr, "Cannot access %s: %s\n", path, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setupWorkdir(const char* prefix)
|
||||
void setupWorkdir()
|
||||
{
|
||||
char* workdir;
|
||||
const char suffix[] = ".workdir";
|
||||
size_t len;
|
||||
|
||||
|
||||
len = strlen(prefix);
|
||||
workdir = (char*) alloca(len + sizeof(suffix));
|
||||
strcpy(workdir, prefix);
|
||||
|
||||
|
||||
// Remove trailing /
|
||||
while (workdir[len-1] == '/')
|
||||
len--;
|
||||
workdir[len] = '\0';
|
||||
|
||||
|
||||
strcat(workdir, suffix);
|
||||
|
||||
|
||||
createDir(workdir);
|
||||
}
|
||||
|
||||
int getInitProcess(const char* prefix)
|
||||
int checkPrefixDir()
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(prefix, &st) == 0)
|
||||
{
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
{
|
||||
fprintf(stderr, "%s is a file. Remove the file.\n", prefix);
|
||||
exit(1);
|
||||
}
|
||||
return 1; // OK
|
||||
}
|
||||
if (errno == ENOENT)
|
||||
return 0; // not found
|
||||
fprintf(stderr, "Cannot access %s: %s\n", prefix, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void setupPrefix()
|
||||
{
|
||||
char path[4096];
|
||||
|
||||
fprintf(stderr, "Setting up a new Darling prefix at %s\n", prefix);
|
||||
|
||||
seteuid(g_originalUid);
|
||||
setegid(g_originalGid);
|
||||
|
||||
createDir(prefix);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/system-root", prefix);
|
||||
if (symlink("/", path) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot symlink %s: %s\n", path, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s/dev", prefix);
|
||||
if (symlink("system-root/dev", path) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot symlink %s: %s\n", path, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s/tmp", prefix);
|
||||
if (symlink("system-root/tmp", path) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot symlink %s: %s\n", path, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s/Users", prefix);
|
||||
if (symlink("system-root/home", path) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot symlink %s: %s\n", path, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s/Volumes", prefix);
|
||||
createDir(path);
|
||||
snprintf(path, sizeof(path), "%s/Applications", prefix);
|
||||
createDir(path);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/var", prefix);
|
||||
createDir(path);
|
||||
snprintf(path, sizeof(path), "%s/var/root", prefix);
|
||||
createDir(path);
|
||||
snprintf(path, sizeof(path), "%s/var/run", prefix);
|
||||
createDir(path);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/var/run/syslog", prefix);
|
||||
if (symlink("system-root/dev/log", path) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot symlink %s: %s\n", path, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
seteuid(0);
|
||||
setegid(0);
|
||||
}
|
||||
|
||||
pid_t getInitProcess()
|
||||
{
|
||||
const char pidFile[] = "/.init.pid";
|
||||
char* pidPath;
|
||||
int fd, rd, pid;
|
||||
char pidBuf[10];
|
||||
char exeBuf[17], procBuf[100];
|
||||
|
||||
pid_t pid;
|
||||
int pid_i;
|
||||
FILE *fp;
|
||||
char procBuf[100];
|
||||
char *exeBuf, *statusBuf;
|
||||
int uidMatch = 0, gidMatch = 0;
|
||||
|
||||
pidPath = (char*) alloca(strlen(prefix) + sizeof(pidFile));
|
||||
strcpy(pidPath, prefix);
|
||||
strcat(pidPath, pidFile);
|
||||
|
||||
fd = open(pidPath, O_RDONLY);
|
||||
if (fd == -1)
|
||||
|
||||
fp = fopen(pidPath, "r");
|
||||
if (fp == NULL)
|
||||
return 0;
|
||||
|
||||
rd = read(fd, pidBuf, sizeof(pidBuf)-1);
|
||||
close(fd);
|
||||
|
||||
if (rd <= 0)
|
||||
|
||||
if (fscanf(fp, "%d", &pid_i) != 1)
|
||||
{
|
||||
fclose(fp);
|
||||
unlink(pidPath);
|
||||
return 0;
|
||||
|
||||
pidBuf[rd-1] = '\0';
|
||||
pid = atoi(pidBuf);
|
||||
}
|
||||
fclose(fp);
|
||||
pid = (pid_t) pid_i;
|
||||
|
||||
// Does the process exist?
|
||||
if (kill(pid, 0) == -1)
|
||||
@ -310,42 +634,86 @@ int getInitProcess(const char* prefix)
|
||||
unlink(pidPath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Is it actually an init process?
|
||||
sprintf(procBuf, "/proc/%d/comm", pid);
|
||||
fd = open(procBuf, O_RDONLY);
|
||||
if (fd == -1)
|
||||
snprintf(procBuf, sizeof(procBuf), "/proc/%d/comm", pid);
|
||||
fp = fopen(procBuf, "r");
|
||||
if (fp == NULL)
|
||||
{
|
||||
unlink(pidPath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rd = read(fd, exeBuf, sizeof(exeBuf)-1);
|
||||
close(fd);
|
||||
|
||||
if (rd <= 0)
|
||||
|
||||
if (fscanf(fp, "%ms", &exeBuf) != 1)
|
||||
{
|
||||
fclose(fp);
|
||||
unlink(pidPath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
exeBuf[rd] = '\0';
|
||||
fclose(fp);
|
||||
|
||||
if (strcmp(exeBuf, DARLING_INIT_COMM) != 0)
|
||||
{
|
||||
unlink(pidPath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
free(exeBuf);
|
||||
|
||||
// Is it owned by the current user?
|
||||
if (g_originalUid != 0)
|
||||
{
|
||||
snprintf(procBuf, sizeof(procBuf), "/proc/%d/status", pid);
|
||||
fp = fopen(procBuf, "r");
|
||||
if (fp == NULL)
|
||||
{
|
||||
unlink(pidPath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
statusBuf = NULL;
|
||||
size_t len;
|
||||
if (getline(&statusBuf, &len, fp) == -1)
|
||||
break;
|
||||
int rid, eid, sid, fid;
|
||||
if (sscanf(statusBuf, "Uid: %d %d %d %d", &rid, &eid, &sid, &fid) == 4)
|
||||
{
|
||||
uidMatch = 1;
|
||||
uidMatch &= rid == g_originalUid;
|
||||
uidMatch &= eid == g_originalUid;
|
||||
uidMatch &= sid == g_originalUid;
|
||||
uidMatch &= fid == g_originalUid;
|
||||
}
|
||||
if (sscanf(statusBuf, "Gid: %d %d %d %d", &rid, &eid, &sid, &fid) == 4)
|
||||
{
|
||||
gidMatch = 1;
|
||||
gidMatch &= rid == g_originalGid;
|
||||
gidMatch &= eid == g_originalGid;
|
||||
gidMatch &= sid == g_originalGid;
|
||||
gidMatch &= fid == g_originalGid;
|
||||
}
|
||||
free(statusBuf);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
if (!uidMatch || !gidMatch)
|
||||
{
|
||||
unlink(pidPath);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
void checkPrefixOwner(const char* prefix)
|
||||
void checkPrefixOwner()
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
|
||||
if (stat(prefix, &st) == 0)
|
||||
{
|
||||
if (st.st_uid != getuid())
|
||||
if (g_originalUid != 0 && st.st_uid != g_originalUid)
|
||||
{
|
||||
fprintf(stderr, "You do not own the prefix directory.\n");
|
||||
exit(1);
|
||||
@ -368,6 +736,7 @@ int isModuleLoaded()
|
||||
if ((fp = fopen("/proc/modules", "r")) == NULL)
|
||||
{
|
||||
fprintf(stderr, "Failure opening /proc/modules: %s\n", strerror(errno));
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -375,9 +744,13 @@ int isModuleLoaded()
|
||||
{
|
||||
read = getline(&line, &len, fp);
|
||||
if (read > 0 && strstr(line, "darling_mach") != NULL)
|
||||
{
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -391,7 +764,7 @@ int loadKernelModule()
|
||||
return 0;
|
||||
|
||||
uname(&name);
|
||||
sprintf(path, "/lib/modules/%s/kernel/misc/darling-mach.ko", name.release);
|
||||
snprintf(path, sizeof(path), "/lib/modules/%s/kernel/misc/darling-mach.ko", name.release);
|
||||
if (access(path, F_OK))
|
||||
{
|
||||
fprintf(stderr, "Cannot find kernel module at %s: %s\n", path, strerror(errno));
|
||||
|
@ -17,6 +17,8 @@ You should have received a copy of the GNU General Public License
|
||||
along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef _DARLING_H_
|
||||
#define _DARLING_H_
|
||||
|
||||
@ -27,20 +29,37 @@ void missingSetuidRoot(void);
|
||||
|
||||
// Returns ~/.darling with ~ expanded
|
||||
char* defaultPrefixPath(void);
|
||||
void setupWorkdir(const char* prefix);
|
||||
|
||||
void setupWorkdir(void);
|
||||
|
||||
void setupPrefix(void);
|
||||
|
||||
int checkPrefixDir(void);
|
||||
|
||||
// Creates the given directory, exit()ing if not possible
|
||||
void createDir(const char* path);
|
||||
|
||||
// Return the PID of the init process in prefix (in our namespace)
|
||||
// Spawn the specified proceess inside the namespaces that PID 1 is in
|
||||
// Returns the PID of the child
|
||||
// exit()s on error
|
||||
pid_t spawnChild(int pidInit, const char *path, const char *const argv[]);
|
||||
|
||||
// Set up some environment variables
|
||||
// As well as the working directory
|
||||
// Called in the child process
|
||||
void setupChild(const char *curPath);
|
||||
|
||||
// Returns the PID of the init process in prefix (in our namespace)
|
||||
// Returns 0 if no init is running
|
||||
int getInitProcess(const char* prefix);
|
||||
pid_t getInitProcess(void);
|
||||
|
||||
int spawnInitProcess(const char* prefix);
|
||||
pid_t spawnInitProcess(void);
|
||||
|
||||
int darlingPreInit(void* arg);
|
||||
void putInitPid(pid_t pidInit);
|
||||
|
||||
void checkPrefixOwner(const char* prefix);
|
||||
void darlingPreInit(void);
|
||||
|
||||
void checkPrefixOwner(void);
|
||||
|
||||
int isModuleLoaded(void);
|
||||
|
||||
|
@ -29,25 +29,26 @@ along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
static std::string GetUserLibrary()
|
||||
{
|
||||
const char* home;
|
||||
const char *home, *prefix;
|
||||
std::stringstream ss;
|
||||
std::string path;
|
||||
|
||||
|
||||
prefix = getenv("DPREFIX");
|
||||
home = getenv("HOME");
|
||||
if (!home)
|
||||
if (!prefix || !home)
|
||||
return std::string(); // give up on this user
|
||||
|
||||
ss << home << '/' << "Library" << '/';
|
||||
|
||||
ss << prefix << home << '/' << "Library" << '/';
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool HasUserDirectoryStructure()
|
||||
{
|
||||
std::string path = GetUserLibrary();
|
||||
|
||||
|
||||
if (path.empty())
|
||||
return true; // give up on this user
|
||||
|
||||
|
||||
if (::access(path.c_str(), F_OK) == -1)
|
||||
return false;
|
||||
else
|
||||
@ -90,25 +91,24 @@ void SetupUserDirectoryStructure()
|
||||
"Spelling",
|
||||
"Voices",
|
||||
};
|
||||
|
||||
|
||||
std::string path = GetUserLibrary();
|
||||
|
||||
|
||||
if (path.empty())
|
||||
return;
|
||||
|
||||
|
||||
std::cerr << "Darling: Creating Library structure at " << path << std::endl;
|
||||
|
||||
|
||||
if (::mkdir(path.c_str(), 0777) == -1)
|
||||
std::cerr << "Darling: Cannot mkdir(" << path << "): " << strerror(errno) << std::endl;
|
||||
|
||||
|
||||
for (const char* dir : dirs)
|
||||
{
|
||||
std::string s = path;
|
||||
|
||||
|
||||
s += dir;
|
||||
|
||||
|
||||
if (::mkdir(s.c_str(), 0777) == -1)
|
||||
std::cerr << "Darling: Cannot mkdir(" << s << "): " << strerror(errno) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user