aboutsummaryrefslogtreecommitdiff
path: root/libminijailpreload.c
blob: 17c8f9749cbd26ba4286c65e9bd87af961c949a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* libminijailpreload.c - preload hack library
 * Copyright 2011 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * This library is preloaded into every program launched by minijail_run().
 * DO NOT EXPORT ANY SYMBOLS FROM THIS LIBRARY. They will replace other symbols
 * in the programs it is preloaded into and cause impossible-to-debug failures.
 * See the minijail0.1 for a design explanation.
 */

#include "libminijail.h"
#include "libminijail-private.h"
#include "util.h"

#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>

static int (*real_main) (int, char **, char **);
static void *libc_handle;

static void truncate_preload_env(char **envp, const char *name)
{
	char *env_value = minijail_getenv(envp, name);
	if (env_value) {
		/*
		 * if we have more than just libminijailpreload.so in
		 * LD_PRELOAD, cut out libminijailpreload.so from it,
		 * as it is guaranteed to always be last in the
		 * LD_PRELOAD list.
		 */
		char *last_space = strrchr(env_value, ' ');
		if (last_space) {
			*last_space = '\0';
		} else {
			/* Only our lib was in LD_PRELOAD, just unset it. */
			minijail_unsetenv(envp, name);
		}
	}
}

/** @brief Fake main(), spliced in before the real call to main() by
 *         __libc_start_main (see below).
 *  We get serialized commands from our invoking process over an fd specified
 *  by an environment variable (kFdEnvVar). The environment variable is a list
 *  of key=value pairs (see move_commands_to_env); we use them to construct a
 *  jail, then enter it.
 */
static int fake_main(int argc, char **argv, char **envp)
{
	char *fd_name = getenv(kFdEnvVar);
	int fd = -1;
	struct minijail *j;
	if (geteuid() != getuid() || getegid() != getgid()) {
		/*
		 * If we didn't do this check, an attacker could set kFdEnvVar
		 * for any setuid program that uses libminijail to cause it to
		 * get capabilities or a uid it did not expect.
		 */
		/* TODO(wad): why would libminijail interact here? */
		return MINIJAIL_ERR_PRELOAD;
	}
	if (!fd_name)
		return MINIJAIL_ERR_PRELOAD;
	fd = atoi(fd_name);
	if (fd < 0)
		return MINIJAIL_ERR_PRELOAD;

	j = minijail_new();
	if (!j)
		die("preload: out of memory");
	if (minijail_from_fd(fd, j))
		die("preload: failed to parse minijail from parent");
	close(fd);

	minijail_unsetenv(envp, kFdEnvVar);

	truncate_preload_env(envp, kLdPreloadEnvVar);

	/* Strip out flags meant for the parent. */
	minijail_preenter(j);
	minijail_enter(j);
	minijail_destroy(j);
	dlclose(libc_handle);
	return real_main(argc, argv, envp);
}

/** @brief LD_PRELOAD override of __libc_start_main.
 *
 *  It is really best if you do not look too closely at this function.  We need
 *  to ensure that some of our code runs before the target program (see the
 *  minijail0.1 file in this directory for high-level details about this), and
 *  the only available place to hook is this function, which is normally
 *  responsible for calling main(). Our LD_PRELOAD will overwrite the real
 *  __libc_start_main with this one, so we have to look up the real one from
 *  libc and invoke it with a pointer to the fake main() we'd like to run before
 *  the real main(). We can't just run our setup code *here* because
 *  __libc_start_main is responsible for setting up the C runtime environment,
 *  so we can't rely on things like malloc() being available yet.
 */

int API __libc_start_main(int (*main)(int, char **, char **), int argc,
			  char **ubp_av, void (*init)(void), void (*fini)(void),
			  void (*rtld_fini)(void), void(*stack_end))
{
	void *sym;
	/*
	 * This hack is unfortunately required by C99 - casting directly from
	 * void* to function pointers is left undefined. See POSIX.1-2003, the
	 * Rationale for the specification of dlsym(), and dlsym(3). This
	 * deliberately violates strict-aliasing rules, but gcc can't tell.
	 */
	union {
		int (*fn)(int (*main)(int, char **, char **), int argc,
			  char **ubp_av, void (*init)(void), void (*fini)(void),
			  void (*rtld_fini)(void), void(*stack_end));
		void *symval;
	} real_libc_start_main;

	/*
	 * We hold this handle for the duration of the real __libc_start_main()
	 * and drop it just before calling the real main().
	 */
	libc_handle = dlopen("libc.so.6", RTLD_NOW);

	if (!libc_handle) {
		syslog(LOG_ERR, "can't dlopen() libc");
		/*
		 * We dare not use abort() here because it will run atexit()
		 * handlers and try to flush stdio.
		 */
		_exit(1);
	}
	sym = dlsym(libc_handle, "__libc_start_main");
	if (!sym) {
		syslog(LOG_ERR, "can't find the real __libc_start_main()");
		_exit(1);
	}
	real_libc_start_main.symval = sym;
	real_main = main;

	/*
	 * Note that we swap fake_main in for main - fake_main knows that it
	 * should call real_main after it's done.
	 */
	return real_libc_start_main.fn(fake_main, argc, ubp_av, init, fini,
				       rtld_fini, stack_end);
}