aboutsummaryrefslogtreecommitdiff
blob: cc8f7a8a900186b61c83750684dccf4fc97f6c85 (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
/*
 * execve() wrapper.
 *
 * Copyright 1999-2008 Gentoo Foundation
 * Licensed under the GPL-2
 *
 *  Partly Copyright (C) 1998-9 Pancrazio `Ezio' de Mauro <p@demauro.net>,
 *  as some of the InstallWatch code was used.
 */

#define WRAPPER_ARGS_PROTO const char *filename, char *const argv[], char *const envp[]
#define WRAPPER_ARGS filename, argv, envp
extern int EXTERN_NAME(WRAPPER_ARGS_PROTO);
static int (*WRAPPER_TRUE_NAME)(WRAPPER_ARGS_PROTO) = NULL;

/* See to see if this an ELF and if so, is it static which we can't wrap */
void check_exec(const char *filename)
{
	int fd;
	unsigned char *elf;
	struct stat st;

	fd = open(filename, O_RDONLY);
	if (fd == -1)
		return;
	if (stat(filename, &st))
		goto out_fd;
	if (st.st_size < EI_NIDENT)
		goto out_fd;
	elf = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
	if (elf == MAP_FAILED)
		goto out_fd;

	if (elf[EI_MAG0] != ELFMAG0 &&
	    elf[EI_MAG1] != ELFMAG1 &&
	    elf[EI_MAG2] != ELFMAG2 &&
	    elf[EI_MAG3] != ELFMAG3 &&
	    !(elf[EI_CLASS] != ELFCLASS32 ||
	      elf[EI_CLASS] != ELFCLASS64))
		goto out_mmap;

#define PARSE_ELF(n) \
({ \
	Elf##n##_Ehdr *ehdr = (void *)elf; \
	Elf##n##_Phdr *phdr = (void *)(elf + ehdr->e_phoff); \
	uint16_t p; \
	if (st.st_size < sizeof(*ehdr)) \
		goto out_mmap; \
	if (st.st_size < ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum) \
		goto out_mmap; \
	for (p = 0; p < ehdr->e_phnum; ++p) \
		if (phdr[p].p_type == PT_INTERP) \
			goto done; \
})
	if (elf[EI_CLASS] == ELFCLASS32)
		PARSE_ELF(32);
	else
		PARSE_ELF(64);
	SB_EWARN("QA: Static ELF", " %s\n", filename);
 done:

 out_mmap:
	munmap(elf, st.st_size);
 out_fd:
	close(fd);
}

int WRAPPER_NAME(WRAPPER_ARGS_PROTO)
{
	char **my_env = NULL;
	char *entry;
	char *ld_preload = NULL;
	char *old_ld_preload = NULL;
	int old_errno = errno;
	int result = -1;
	int count;

	if (!FUNCTION_SANDBOX_SAFE(filename))
		return result;

	check_exec(filename);

	str_list_for_each_item(envp, entry, count) {
		if (strstr(entry, LD_PRELOAD_EQ) != entry)
			continue;

		/* Check if we do not have to do anything */
		if (NULL != strstr(entry, sandbox_lib)) {
			/* Use the user's envp */
			my_env = (char **)envp;
			goto do_execve;
		} else {
			old_ld_preload = entry;
			/* No need to continue, we have to modify LD_PRELOAD */
			break;
		}
	}

	/* Ok, we need to create our own envp, as we need to add LD_PRELOAD,
	 * and we should not touch the user's envp.  First we add LD_PRELOAD,
	 * and just all the rest. */
	count = strlen(LD_PRELOAD_EQ) + strlen(sandbox_lib) + 1;
	if (NULL != old_ld_preload)
		count += strlen(old_ld_preload) - strlen(LD_PRELOAD_EQ) + 1;
	ld_preload = xmalloc(count * sizeof(char));
	if (NULL == ld_preload)
		goto error;
	snprintf(ld_preload, count, "%s%s%s%s", LD_PRELOAD_EQ, sandbox_lib,
		 (old_ld_preload) ? " " : "",
		 (old_ld_preload) ? old_ld_preload + strlen(LD_PRELOAD_EQ) : "");
	str_list_add_item(my_env, ld_preload, error);

	str_list_for_each_item(envp, entry, count) {
		if (strstr(entry, LD_PRELOAD_EQ) != entry) {
			str_list_add_item(my_env, entry, error);
			continue;
		}
	}

do_execve:
	errno = old_errno;
	check_dlsym(WRAPPER_TRUE_NAME, WRAPPER_SYMNAME,
		    WRAPPER_SYMVER);
	result = WRAPPER_TRUE_NAME(filename, argv, my_env);

	if ((NULL != my_env) && (my_env != envp))
		/* We do not use str_list_free(), as we did not allocate the
		 * entries except for LD_PRELOAD. */
		free(my_env);
	if (NULL != ld_preload)
		free(ld_preload);

	return result;

error:
	if ((NULL != my_env) && (my_env != envp))
		/* We do not use str_list_free(), as we did not allocate the
		 * entries except for LD_PRELOAD. */
		free(my_env);
	if (NULL != ld_preload)
		free(ld_preload);

	return -1;
}