blob: 357be6d58c383b8883ac22a809034387adaeb0f8 [file] [log] [blame]
Elliott Hughes18a206c2012-10-29 17:37:13 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include "linker_environ.h"
30
31#include <linux/auxvec.h>
32#include <stddef.h>
33#include <stdlib.h>
34#include <unistd.h>
35
36static char** _envp;
37static bool _AT_SECURE_value = true;
38
39bool get_AT_SECURE() {
40 return _AT_SECURE_value;
41}
42
43/* Returns 1 if 'str' points to a valid environment variable definition.
44 * For now, we check that:
45 * - It is smaller than MAX_ENV_LEN (to detect non-zero terminated strings)
46 * - It contains at least one equal sign that is not the first character
47 */
48static int _is_valid_definition(const char* str) {
49 int pos = 0;
50 int first_equal_pos = -1;
51
52 // According to its sources, the kernel uses 32*PAGE_SIZE by default
53 // as the maximum size for an env. variable definition.
54 const int MAX_ENV_LEN = 32*4096;
55
56 if (str == NULL) {
57 return 0;
58 }
59
60 // Parse the string, looking for the first '=' there, and its size.
61 while (pos < MAX_ENV_LEN) {
62 if (str[pos] == '\0') {
63 break;
64 }
65 if (str[pos] == '=' && first_equal_pos < 0) {
66 first_equal_pos = pos;
67 }
68 pos++;
69 }
70
71 if (pos >= MAX_ENV_LEN) {
72 return 0; // Too large.
73 }
74
75 if (first_equal_pos < 1) {
76 return 0; // No equals sign, or it's the first character.
77 }
78
79 return 1;
80}
81
82static void __init_AT_SECURE(unsigned* auxv) {
83 // Check auxv for AT_SECURE first to see if program is setuid, setgid,
84 // has file caps, or caused a SELinux/AppArmor domain transition.
85 for (unsigned* v = auxv; v[0]; v += 2) {
86 if (v[0] == AT_SECURE) {
87 // Kernel told us whether to enable secure mode.
88 _AT_SECURE_value = v[1];
89 return;
90 }
91 }
92
93 // We don't support ancient kernels.
94 const char* msg = "FATAL: kernel did not supply AT_SECURE\n";
95 write(2, msg, strlen(msg));
96 exit(EXIT_FAILURE);
97}
98
99static void __remove_unsafe_environment_variables() {
100 // None of these should be allowed in setuid programs.
101 static const char* const UNSAFE_VARIABLE_NAMES[] = {
102 "GCONV_PATH",
103 "GETCONF_DIR",
104 "HOSTALIASES",
105 "LD_AOUT_LIBRARY_PATH",
106 "LD_AOUT_PRELOAD",
107 "LD_AUDIT",
108 "LD_DEBUG",
109 "LD_DEBUG_OUTPUT",
110 "LD_DYNAMIC_WEAK",
111 "LD_LIBRARY_PATH",
112 "LD_ORIGIN_PATH",
113 "LD_PRELOAD",
114 "LD_PROFILE",
115 "LD_SHOW_AUXV",
116 "LD_USE_LOAD_BIAS",
117 "LOCALDOMAIN",
118 "LOCPATH",
119 "MALLOC_CHECK_",
120 "MALLOC_TRACE",
121 "NIS_PATH",
122 "NLSPATH",
123 "RESOLV_HOST_CONF",
124 "RES_OPTIONS",
125 "TMPDIR",
126 "TZDIR",
127 NULL
128 };
129 for (size_t i = 0; UNSAFE_VARIABLE_NAMES[i] != NULL; ++i) {
130 linker_env_unset(UNSAFE_VARIABLE_NAMES[i]);
131 }
132}
133
134static void __remove_invalid_environment_variables() {
135 char** src = _envp;
136 char** dst = _envp;
137 for (; src[0] != NULL; ++src) {
138 if (!_is_valid_definition(src[0])) {
139 continue;
140 }
141 dst[0] = src[0];
142 ++dst;
143 }
144 dst[0] = NULL;
145}
146
147unsigned* linker_env_init(unsigned* environment_and_aux_vectors) {
148 // Store environment pointer - can't be NULL.
149 _envp = reinterpret_cast<char**>(environment_and_aux_vectors);
150
151 // Skip over all environment variable definitions.
152 // The end of the environment block is marked by two NULL pointers.
153 unsigned* aux_vectors = environment_and_aux_vectors;
154 while (aux_vectors[0] != 0) {
155 ++aux_vectors;
156 }
157 ++aux_vectors;
158
159 __remove_invalid_environment_variables();
160 __init_AT_SECURE(aux_vectors);
161
162 // Sanitize environment if we're loading a setuid program.
163 if (get_AT_SECURE()) {
164 __remove_unsafe_environment_variables();
165 }
166
167 return aux_vectors;
168}
169
170/* Check if the environment variable definition at 'envstr'
171 * starts with '<name>=', and if so return the address of the
172 * first character after the equal sign. Otherwise return NULL.
173 */
174static char* env_match(char* envstr, const char* name) {
175 size_t i = 0;
176
177 while (envstr[i] == name[i] && name[i] != '\0') {
178 ++i;
179 }
180
181 if (name[i] == '\0' && envstr[i] == '=') {
182 return envstr + i + 1;
183 }
184
185 return NULL;
186}
187
188const char* linker_env_get(const char* name) {
189 if (name == NULL || name[0] == '\0') {
190 return NULL;
191 }
192
193 for (char** p = _envp; p[0] != NULL; ++p) {
194 char* val = env_match(p[0], name);
195 if (val != NULL) {
196 if (val[0] == '\0') {
197 return NULL; // Return NULL for empty strings.
198 }
199 return val;
200 }
201 }
202 return NULL;
203}
204
205void linker_env_unset(const char* name) {
206 char** readp = _envp;
207 char** writep = readp;
208
209 if (name == NULL || name[0] == '\0') {
210 return;
211 }
212
213 for ( ; readp[0] != NULL; readp++ ) {
214 if (env_match(readp[0], name)) {
215 continue;
216 }
217 writep[0] = readp[0];
218 writep++;
219 }
220 /* end list with a NULL */
221 writep[0] = NULL;
222}