Line data Source code
1 : /* Copyright (c) 2003-2004, Roger Dingledine
2 : * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 : * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 : /* See LICENSE for licensing information */
5 :
6 : /**
7 : * \file restrict.c
8 : * \brief Drop privileges from the current process.
9 : **/
10 :
11 : #include "orconfig.h"
12 : #include "lib/process/restrict.h"
13 : #include "lib/intmath/cmp.h"
14 : #include "lib/log/log.h"
15 : #include "lib/log/util_bug.h"
16 : #include "lib/net/socket.h"
17 :
18 : #ifdef HAVE_SYS_MMAN_H
19 : #include <sys/mman.h>
20 : #endif
21 : #include <errno.h>
22 : #include <stdlib.h>
23 : #include <string.h>
24 :
25 : /* We only use the linux prctl for now. There is no Win32 support; this may
26 : * also work on various BSD systems and Mac OS X - send testing feedback!
27 : *
28 : * On recent Gnu/Linux kernels it is possible to create a system-wide policy
29 : * that will prevent non-root processes from attaching to other processes
30 : * unless they are the parent process; thus gdb can attach to programs that
31 : * they execute but they cannot attach to other processes running as the same
32 : * user. The system wide policy may be set with the sysctl
33 : * kernel.yama.ptrace_scope or by inspecting
34 : * /proc/sys/kernel/yama/ptrace_scope and it is 1 by default on Ubuntu 11.04.
35 : *
36 : * This ptrace scope will be ignored on Gnu/Linux for users with
37 : * CAP_SYS_PTRACE and so it is very likely that root will still be able to
38 : * attach to the Tor process.
39 : */
40 : /** Attempt to disable debugger attachment: return 1 on success, -1 on
41 : * failure, and 0 if we don't know how to try on this platform. */
42 : int
43 4 : tor_disable_debugger_attach(void)
44 : {
45 4 : int r = -1;
46 4 : log_debug(LD_CONFIG,
47 : "Attempting to disable debugger attachment to Tor for "
48 : "unprivileged users.");
49 : #if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) \
50 : && defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
51 : #define TRIED_TO_DISABLE
52 : r = prctl(PR_SET_DUMPABLE, 0);
53 : #elif defined(__APPLE__) && defined(PT_DENY_ATTACH)
54 : #define TRIED_TO_ATTACH
55 : r = ptrace(PT_DENY_ATTACH, 0, 0, 0);
56 : #endif /* defined(__linux__) && defined(HAVE_SYS_PRCTL_H) ... || ... */
57 :
58 : // XXX: TODO - Mac OS X has dtrace and this may be disabled.
59 : // XXX: TODO - Windows probably has something similar
60 : #ifdef TRIED_TO_DISABLE
61 : if (r == 0) {
62 : log_debug(LD_CONFIG,"Debugger attachment disabled for "
63 : "unprivileged users.");
64 : return 1;
65 : } else {
66 : log_warn(LD_CONFIG, "Unable to disable debugger attaching: %s",
67 : strerror(errno));
68 : }
69 : #endif /* defined(TRIED_TO_DISABLE) */
70 : #undef TRIED_TO_DISABLE
71 4 : return r;
72 : }
73 :
74 : #if defined(HAVE_MLOCKALL) && HAVE_DECL_MLOCKALL && defined(RLIMIT_MEMLOCK)
75 : #define HAVE_UNIX_MLOCKALL
76 : #endif
77 :
78 : #ifdef HAVE_UNIX_MLOCKALL
79 : /** Attempt to raise the current and max rlimit to infinity for our process.
80 : * This only needs to be done once and can probably only be done when we have
81 : * not already dropped privileges.
82 : */
83 : static int
84 0 : tor_set_max_memlock(void)
85 : {
86 : /* Future consideration for Windows is probably SetProcessWorkingSetSize
87 : * This is similar to setting the memory rlimit of RLIMIT_MEMLOCK
88 : * https://msdn.microsoft.com/en-us/library/ms686234(VS.85).aspx
89 : */
90 :
91 0 : struct rlimit limit;
92 :
93 : /* RLIM_INFINITY is -1 on some platforms. */
94 0 : limit.rlim_cur = RLIM_INFINITY;
95 0 : limit.rlim_max = RLIM_INFINITY;
96 :
97 0 : if (setrlimit(RLIMIT_MEMLOCK, &limit) == -1) {
98 0 : if (errno == EPERM) {
99 0 : log_warn(LD_GENERAL, "You appear to lack permissions to change memory "
100 : "limits. Are you root?");
101 : }
102 0 : log_warn(LD_GENERAL, "Unable to raise RLIMIT_MEMLOCK: %s",
103 : strerror(errno));
104 0 : return -1;
105 : }
106 :
107 : return 0;
108 : }
109 : #endif /* defined(HAVE_UNIX_MLOCKALL) */
110 :
111 : /** Attempt to lock all current and all future memory pages.
112 : * This should only be called once and while we're privileged.
113 : * Like mlockall() we return 0 when we're successful and -1 when we're not.
114 : * Unlike mlockall() we return 1 if we've already attempted to lock memory.
115 : */
116 : int
117 0 : tor_mlockall(void)
118 : {
119 0 : static int memory_lock_attempted = 0;
120 :
121 0 : if (memory_lock_attempted) {
122 : return 1;
123 : }
124 :
125 0 : memory_lock_attempted = 1;
126 :
127 : /*
128 : * Future consideration for Windows may be VirtualLock
129 : * VirtualLock appears to implement mlock() but not mlockall()
130 : *
131 : * https://msdn.microsoft.com/en-us/library/aa366895(VS.85).aspx
132 : */
133 :
134 : #ifdef HAVE_UNIX_MLOCKALL
135 0 : if (tor_set_max_memlock() == 0) {
136 0 : log_debug(LD_GENERAL, "RLIMIT_MEMLOCK is now set to RLIM_INFINITY.");
137 : }
138 :
139 0 : if (mlockall(MCL_CURRENT|MCL_FUTURE) == 0) {
140 0 : log_info(LD_GENERAL, "Insecure OS paging is effectively disabled.");
141 0 : return 0;
142 : } else {
143 0 : if (errno == ENOSYS) {
144 : /* Apple - it's 2009! I'm looking at you. Grrr. */
145 0 : log_notice(LD_GENERAL, "It appears that mlockall() is not available on "
146 : "your platform.");
147 0 : } else if (errno == EPERM) {
148 0 : log_notice(LD_GENERAL, "It appears that you lack the permissions to "
149 : "lock memory. Are you root?");
150 : }
151 0 : log_notice(LD_GENERAL, "Unable to lock all current and future memory "
152 : "pages: %s", strerror(errno));
153 0 : return -1;
154 : }
155 : #else /* !defined(HAVE_UNIX_MLOCKALL) */
156 : log_warn(LD_GENERAL, "Unable to lock memory pages. mlockall() unsupported?");
157 : return -1;
158 : #endif /* defined(HAVE_UNIX_MLOCKALL) */
159 : }
160 :
161 : /** Number of extra file descriptors to keep in reserve beyond those that we
162 : * tell Tor it's allowed to use. */
163 : #define ULIMIT_BUFFER 32 /* keep 32 extra fd's beyond ConnLimit_ */
164 :
165 : /** Learn the maximum allowed number of file descriptors, and tell the
166 : * system we want to use up to that number. (Some systems have a low soft
167 : * limit, and let us set it higher.) We compute this by finding the largest
168 : * number that we can use.
169 : *
170 : * If the limit is below the reserved file descriptor value (ULIMIT_BUFFER),
171 : * return -1 and <b>max_out</b> is untouched.
172 : *
173 : * If we can't find a number greater than or equal to <b>limit</b>, then we
174 : * fail by returning -1 and <b>max_out</b> is untouched.
175 : *
176 : * If we are unable to set the limit value because of setrlimit() failing,
177 : * return 0 and <b>max_out</b> is set to the current maximum value returned
178 : * by getrlimit().
179 : *
180 : * Otherwise, return 0 and store the maximum we found inside <b>max_out</b>
181 : * and set <b>max_sockets</b> with that value as well.*/
182 : int
183 4 : set_max_file_descriptors(rlim_t limit, int *max_out)
184 : {
185 4 : if (limit < ULIMIT_BUFFER) {
186 0 : log_warn(LD_CONFIG,
187 : "ConnLimit must be at least %d. Failing.", ULIMIT_BUFFER);
188 0 : return -1;
189 : }
190 :
191 : /* Define some maximum connections values for systems where we cannot
192 : * automatically determine a limit. Re Cygwin, see
193 : * https://archives.seul.org/or/talk/Aug-2006/msg00210.html
194 : * For an iPhone, 9999 should work. For Windows and all other unknown
195 : * systems we use 15000 as the default. */
196 : #ifndef HAVE_GETRLIMIT
197 : #if defined(CYGWIN) || defined(__CYGWIN__)
198 : const char *platform = "Cygwin";
199 : const unsigned long MAX_CONNECTIONS = 3200;
200 : #elif defined(_WIN32)
201 : const char *platform = "Windows";
202 : const unsigned long MAX_CONNECTIONS = 15000;
203 : #else
204 : const char *platform = "unknown platforms with no getrlimit()";
205 : const unsigned long MAX_CONNECTIONS = 15000;
206 : #endif /* defined(CYGWIN) || defined(__CYGWIN__) || ... */
207 : log_fn(LOG_INFO, LD_NET,
208 : "This platform is missing getrlimit(). Proceeding.");
209 : if (limit > MAX_CONNECTIONS) {
210 : log_warn(LD_CONFIG,
211 : "We do not support more than %lu file descriptors "
212 : "on %s. Tried to raise to %lu.",
213 : (unsigned long)MAX_CONNECTIONS, platform, (unsigned long)limit);
214 : return -1;
215 : }
216 : limit = MAX_CONNECTIONS;
217 : #else /* defined(HAVE_GETRLIMIT) */
218 4 : struct rlimit rlim;
219 :
220 4 : if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
221 0 : log_warn(LD_NET, "Could not get maximum number of file descriptors: %s",
222 : strerror(errno));
223 0 : return -1;
224 : }
225 4 : if (rlim.rlim_max < limit) {
226 0 : log_warn(LD_CONFIG,"We need %lu file descriptors available, and we're "
227 : "limited to %lu. Please change your ulimit -n.",
228 : (unsigned long)limit, (unsigned long)rlim.rlim_max);
229 0 : return -1;
230 : }
231 :
232 4 : if (rlim.rlim_max > rlim.rlim_cur) {
233 4 : log_info(LD_NET,"Raising max file descriptors from %lu to %lu.",
234 : (unsigned long)rlim.rlim_cur, (unsigned long)rlim.rlim_max);
235 : }
236 : /* Set the current limit value so if the attempt to set the limit to the
237 : * max fails at least we'll have a valid value of maximum sockets. */
238 4 : *max_out = (int)rlim.rlim_cur - ULIMIT_BUFFER;
239 4 : set_max_sockets(*max_out);
240 4 : rlim.rlim_cur = rlim.rlim_max;
241 :
242 4 : if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
243 0 : int couldnt_set = 1;
244 0 : const int setrlimit_errno = errno;
245 : #ifdef OPEN_MAX
246 : uint64_t try_limit = OPEN_MAX - ULIMIT_BUFFER;
247 : if (errno == EINVAL && try_limit < (uint64_t) rlim.rlim_cur) {
248 : /* On some platforms, OPEN_MAX is the real limit, and getrlimit() is
249 : * full of nasty lies. I'm looking at you, OSX 10.5.... */
250 : rlim.rlim_cur = MIN((rlim_t) try_limit, rlim.rlim_cur);
251 : if (setrlimit(RLIMIT_NOFILE, &rlim) == 0) {
252 : if (rlim.rlim_cur < (rlim_t)limit) {
253 : log_warn(LD_CONFIG, "We are limited to %lu file descriptors by "
254 : "OPEN_MAX (%lu), and ConnLimit is %lu. Changing "
255 : "ConnLimit; sorry.",
256 : (unsigned long)try_limit, (unsigned long)OPEN_MAX,
257 : (unsigned long)limit);
258 : } else {
259 : log_info(LD_CONFIG, "Dropped connection limit to %lu based on "
260 : "OPEN_MAX (%lu); Apparently, %lu was too high and rlimit "
261 : "lied to us.",
262 : (unsigned long)try_limit, (unsigned long)OPEN_MAX,
263 : (unsigned long)rlim.rlim_max);
264 : }
265 : couldnt_set = 0;
266 : }
267 : }
268 : #endif /* defined(OPEN_MAX) */
269 0 : if (couldnt_set) {
270 0 : log_warn(LD_CONFIG,"Couldn't set maximum number of file descriptors: %s",
271 : strerror(setrlimit_errno));
272 : }
273 : }
274 : /* leave some overhead for logs, etc, */
275 4 : limit = rlim.rlim_cur;
276 : #endif /* !defined(HAVE_GETRLIMIT) */
277 :
278 4 : if (limit > INT_MAX)
279 : limit = INT_MAX;
280 4 : tor_assert(max_out);
281 4 : *max_out = (int)limit - ULIMIT_BUFFER;
282 4 : set_max_sockets(*max_out);
283 :
284 4 : return 0;
285 : }
|