Fix: define _LGPL_SOURCE in C files
[lttng-tools.git] / src / common / runas.c
1 /*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License, version 2 only,
7 * as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #define _GNU_SOURCE
20 #define _LGPL_SOURCE
21 #include <errno.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/wait.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sched.h>
32 #include <sys/signal.h>
33
34 #include <common/common.h>
35 #include <common/utils.h>
36 #include <common/compat/mman.h>
37 #include <common/compat/clone.h>
38
39 #include "runas.h"
40
41 #define RUNAS_CHILD_STACK_SIZE 10485760
42
43 #ifndef MAP_STACK
44 #define MAP_STACK 0
45 #endif
46
47 #ifdef __FreeBSD__
48 /* FreeBSD MAP_STACK always return -ENOMEM */
49 #define LTTNG_MAP_STACK 0
50 #else
51 #define LTTNG_MAP_STACK MAP_STACK
52 #endif
53
54 #ifndef MAP_GROWSDOWN
55 #define MAP_GROWSDOWN 0
56 #endif
57
58 #ifndef MAP_ANONYMOUS
59 #define MAP_ANONYMOUS MAP_ANON
60 #endif
61
62 struct run_as_data {
63 int (*cmd)(void *data);
64 void *data;
65 uid_t uid;
66 gid_t gid;
67 int retval_pipe;
68 };
69
70 struct run_as_mkdir_data {
71 const char *path;
72 mode_t mode;
73 };
74
75 struct run_as_open_data {
76 const char *path;
77 int flags;
78 mode_t mode;
79 };
80
81 #ifdef VALGRIND
82 static
83 int use_clone(void)
84 {
85 return 0;
86 }
87 #else
88 static
89 int use_clone(void)
90 {
91 return !getenv("LTTNG_DEBUG_NOCLONE");
92 }
93 #endif
94
95 /*
96 * Create recursively directory using the FULL path.
97 */
98 static
99 int _mkdir_recursive(void *_data)
100 {
101 struct run_as_mkdir_data *data = _data;
102 const char *path;
103 mode_t mode;
104
105 path = data->path;
106 mode = data->mode;
107
108 return utils_mkdir_recursive(path, mode);
109 }
110
111 static
112 int _mkdir(void *_data)
113 {
114 int ret;
115 struct run_as_mkdir_data *data = _data;
116
117 ret = mkdir(data->path, data->mode);
118 if (ret < 0) {
119 ret = -errno;
120 }
121
122 return ret;
123 }
124
125 static
126 int _open(void *_data)
127 {
128 struct run_as_open_data *data = _data;
129 return open(data->path, data->flags, data->mode);
130 }
131
132 static
133 int child_run_as(void *_data)
134 {
135 int ret;
136 struct run_as_data *data = _data;
137 ssize_t writelen;
138 int sendret;
139
140 /*
141 * Child: it is safe to drop egid and euid while sharing the
142 * file descriptors with the parent process, since we do not
143 * drop "uid": therefore, the user we are dropping egid/euid to
144 * cannot attach to this process with, e.g. ptrace, nor map this
145 * process memory.
146 */
147 if (data->gid != getegid()) {
148 ret = setegid(data->gid);
149 if (ret < 0) {
150 PERROR("setegid");
151 sendret = -1;
152 goto write_return;
153 }
154 }
155 if (data->uid != geteuid()) {
156 ret = seteuid(data->uid);
157 if (ret < 0) {
158 PERROR("seteuid");
159 sendret = -1;
160 goto write_return;
161 }
162 }
163 /*
164 * Also set umask to 0 for mkdir executable bit.
165 */
166 umask(0);
167 sendret = (*data->cmd)(data->data);
168
169 write_return:
170 /* send back return value */
171 writelen = lttng_write(data->retval_pipe, &sendret, sizeof(sendret));
172 if (writelen < sizeof(sendret)) {
173 PERROR("lttng_write error");
174 return EXIT_FAILURE;
175 } else {
176 return EXIT_SUCCESS;
177 }
178 }
179
180 static
181 int run_as_clone(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid)
182 {
183 struct run_as_data run_as_data;
184 int ret = 0;
185 ssize_t readlen;
186 int status;
187 pid_t pid;
188 int retval_pipe[2];
189 void *child_stack;
190 int retval;
191
192 /*
193 * If we are non-root, we can only deal with our own uid.
194 */
195 if (geteuid() != 0) {
196 if (uid != geteuid()) {
197 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
198 uid, geteuid());
199 return -EPERM;
200 }
201 }
202
203 ret = pipe(retval_pipe);
204 if (ret < 0) {
205 PERROR("pipe");
206 retval = ret;
207 goto end;
208 }
209 run_as_data.data = data;
210 run_as_data.cmd = cmd;
211 run_as_data.uid = uid;
212 run_as_data.gid = gid;
213 run_as_data.retval_pipe = retval_pipe[1]; /* write end */
214 child_stack = mmap(NULL, RUNAS_CHILD_STACK_SIZE,
215 PROT_WRITE | PROT_READ,
216 MAP_PRIVATE | MAP_GROWSDOWN | MAP_ANONYMOUS | LTTNG_MAP_STACK,
217 -1, 0);
218 if (child_stack == MAP_FAILED) {
219 PERROR("mmap");
220 retval = -ENOMEM;
221 goto close_pipe;
222 }
223 /*
224 * Pointing to the middle of the stack to support architectures
225 * where the stack grows up (HPPA).
226 */
227 pid = lttng_clone_files(child_run_as, child_stack + (RUNAS_CHILD_STACK_SIZE / 2),
228 &run_as_data);
229 if (pid < 0) {
230 PERROR("clone");
231 retval = pid;
232 goto unmap_stack;
233 }
234 /* receive return value */
235 readlen = lttng_read(retval_pipe[0], &retval, sizeof(retval));
236 if (readlen < sizeof(retval)) {
237 ret = -1;
238 }
239
240 /*
241 * Parent: wait for child to return, in which case the
242 * shared memory map will have been created.
243 */
244 pid = waitpid(pid, &status, 0);
245 if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
246 PERROR("wait");
247 retval = -1;
248 }
249 unmap_stack:
250 ret = munmap(child_stack, RUNAS_CHILD_STACK_SIZE);
251 if (ret < 0) {
252 PERROR("munmap");
253 retval = ret;
254 }
255 close_pipe:
256 ret = close(retval_pipe[0]);
257 if (ret) {
258 PERROR("close");
259 }
260 ret = close(retval_pipe[1]);
261 if (ret) {
262 PERROR("close");
263 }
264 end:
265 return retval;
266 }
267
268 /*
269 * To be used on setups where gdb has issues debugging programs using
270 * clone/rfork. Note that this is for debuging ONLY, and should not be
271 * considered secure.
272 */
273 static
274 int run_as_noclone(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid)
275 {
276 int ret;
277 mode_t old_mask;
278
279 old_mask = umask(0);
280 ret = cmd(data);
281 umask(old_mask);
282
283 return ret;
284 }
285
286 static
287 int run_as(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid)
288 {
289 if (use_clone()) {
290 int ret;
291
292 DBG("Using run_as_clone");
293 pthread_mutex_lock(&lttng_libc_state_lock);
294 ret = run_as_clone(cmd, data, uid, gid);
295 pthread_mutex_unlock(&lttng_libc_state_lock);
296 return ret;
297 } else {
298 DBG("Using run_as_noclone");
299 return run_as_noclone(cmd, data, uid, gid);
300 }
301 }
302
303 LTTNG_HIDDEN
304 int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
305 {
306 struct run_as_mkdir_data data;
307
308 DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
309 path, mode, uid, gid);
310 data.path = path;
311 data.mode = mode;
312 return run_as(_mkdir_recursive, &data, uid, gid);
313 }
314
315 LTTNG_HIDDEN
316 int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
317 {
318 struct run_as_mkdir_data data;
319
320 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
321 path, mode, uid, gid);
322 data.path = path;
323 data.mode = mode;
324 return run_as(_mkdir, &data, uid, gid);
325 }
326
327 /*
328 * Note: open_run_as is currently not working. We'd need to pass the fd
329 * opened in the child to the parent.
330 */
331 LTTNG_HIDDEN
332 int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
333 {
334 struct run_as_open_data data;
335
336 DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
337 path, flags, mode, uid, gid);
338 data.path = path;
339 data.flags = flags;
340 data.mode = mode;
341 return run_as(_open, &data, uid, gid);
342 }
This page took 0.037215 seconds and 5 git commands to generate.