Fix: Perform rcu barrier before tearing down the run-as worker
[lttng-tools.git] / src / common / runas.c
... / ...
CommitLineData
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/getenv.h>
37#include <common/sessiond-comm/unix.h>
38
39#include "runas.h"
40
41struct run_as_data;
42typedef int (*run_as_fct)(struct run_as_data *data);
43
44struct run_as_mkdir_data {
45 char path[PATH_MAX];
46 mode_t mode;
47};
48
49struct run_as_open_data {
50 char path[PATH_MAX];
51 int flags;
52 mode_t mode;
53};
54
55struct run_as_unlink_data {
56 char path[PATH_MAX];
57};
58
59struct run_as_rmdir_recursive_data {
60 char path[PATH_MAX];
61};
62
63enum run_as_cmd {
64 RUN_AS_MKDIR,
65 RUN_AS_OPEN,
66 RUN_AS_UNLINK,
67 RUN_AS_RMDIR_RECURSIVE,
68 RUN_AS_MKDIR_RECURSIVE,
69};
70
71struct run_as_data {
72 enum run_as_cmd cmd;
73 union {
74 struct run_as_mkdir_data mkdir;
75 struct run_as_open_data open;
76 struct run_as_unlink_data unlink;
77 struct run_as_rmdir_recursive_data rmdir_recursive;
78 } u;
79 uid_t uid;
80 gid_t gid;
81};
82
83struct run_as_ret {
84 int ret;
85 int _errno;
86};
87
88struct run_as_worker {
89 pid_t pid; /* Worker PID. */
90 int sockpair[2];
91 char *procname;
92};
93
94/* Single global worker per process (for now). */
95static struct run_as_worker *global_worker;
96/* Lock protecting the worker. */
97static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
98
99#ifdef VALGRIND
100static
101int use_clone(void)
102{
103 return 0;
104}
105#else
106static
107int use_clone(void)
108{
109 return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
110}
111#endif
112
113LTTNG_HIDDEN
114int _utils_mkdir_recursive_unsafe(const char *path, mode_t mode);
115
116/*
117 * Create recursively directory using the FULL path.
118 */
119static
120int _mkdir_recursive(struct run_as_data *data)
121{
122 const char *path;
123 mode_t mode;
124
125 path = data->u.mkdir.path;
126 mode = data->u.mkdir.mode;
127
128 /* Safe to call as we have transitioned to the requested uid/gid. */
129 return _utils_mkdir_recursive_unsafe(path, mode);
130}
131
132static
133int _mkdir(struct run_as_data *data)
134{
135 return mkdir(data->u.mkdir.path, data->u.mkdir.mode);
136}
137
138static
139int _open(struct run_as_data *data)
140{
141 return open(data->u.open.path, data->u.open.flags, data->u.open.mode);
142}
143
144static
145int _unlink(struct run_as_data *data)
146{
147 return unlink(data->u.unlink.path);
148}
149
150static
151int _rmdir_recursive(struct run_as_data *data)
152{
153 return utils_recursive_rmdir(data->u.rmdir_recursive.path);
154}
155
156static
157run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd)
158{
159 switch (cmd) {
160 case RUN_AS_MKDIR:
161 return _mkdir;
162 case RUN_AS_OPEN:
163 return _open;
164 case RUN_AS_UNLINK:
165 return _unlink;
166 case RUN_AS_RMDIR_RECURSIVE:
167 return _rmdir_recursive;
168 case RUN_AS_MKDIR_RECURSIVE:
169 return _mkdir_recursive;
170 default:
171 ERR("Unknown command %d", (int) cmd)
172 return NULL;
173 }
174}
175
176static
177int do_send_fd(struct run_as_worker *worker,
178 enum run_as_cmd cmd, int fd)
179{
180 ssize_t len;
181
182 switch (cmd) {
183 case RUN_AS_OPEN:
184 break;
185 default:
186 return 0;
187 }
188 if (fd < 0) {
189 return 0;
190 }
191 len = lttcomm_send_fds_unix_sock(worker->sockpair[1], &fd, 1);
192 if (len < 0) {
193 PERROR("lttcomm_send_fds_unix_sock");
194 return -1;
195 }
196 if (close(fd) < 0) {
197 PERROR("close");
198 return -1;
199 }
200 return 0;
201}
202
203static
204int do_recv_fd(struct run_as_worker *worker,
205 enum run_as_cmd cmd, int *fd)
206{
207 ssize_t len;
208
209 switch (cmd) {
210 case RUN_AS_OPEN:
211 break;
212 default:
213 return 0;
214 }
215 if (*fd < 0) {
216 return 0;
217 }
218 len = lttcomm_recv_fds_unix_sock(worker->sockpair[0], fd, 1);
219 if (!len) {
220 return -1;
221 } else if (len < 0) {
222 PERROR("lttcomm_recv_fds_unix_sock");
223 return -1;
224 }
225 return 0;
226}
227
228/*
229 * Return < 0 on error, 0 if OK, 1 on hangup.
230 */
231static
232int handle_one_cmd(struct run_as_worker *worker)
233{
234 int ret = 0;
235 struct run_as_data data;
236 ssize_t readlen, writelen;
237 struct run_as_ret sendret;
238 run_as_fct cmd;
239 uid_t prev_euid;
240
241 /* Read data */
242 readlen = lttcomm_recv_unix_sock(worker->sockpair[1], &data,
243 sizeof(data));
244 if (readlen == 0) {
245 /* hang up */
246 ret = 1;
247 goto end;
248 }
249 if (readlen < sizeof(data)) {
250 PERROR("lttcomm_recv_unix_sock error");
251 ret = -1;
252 goto end;
253 }
254
255 cmd = run_as_enum_to_fct(data.cmd);
256 if (!cmd) {
257 ret = -1;
258 goto end;
259 }
260
261 prev_euid = getuid();
262 if (data.gid != getegid()) {
263 ret = setegid(data.gid);
264 if (ret < 0) {
265 PERROR("setegid");
266 goto write_return;
267 }
268 }
269 if (data.uid != prev_euid) {
270 ret = seteuid(data.uid);
271 if (ret < 0) {
272 PERROR("seteuid");
273 goto write_return;
274 }
275 }
276 /*
277 * Also set umask to 0 for mkdir executable bit.
278 */
279 umask(0);
280 ret = (*cmd)(&data);
281
282write_return:
283 sendret.ret = ret;
284 sendret._errno = errno;
285 /* send back return value */
286 writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
287 sizeof(sendret));
288 if (writelen < sizeof(sendret)) {
289 PERROR("lttcomm_send_unix_sock error");
290 ret = -1;
291 goto end;
292 }
293 ret = do_send_fd(worker, data.cmd, ret);
294 if (ret) {
295 PERROR("do_send_fd error");
296 ret = -1;
297 goto end;
298 }
299 if (seteuid(prev_euid) < 0) {
300 PERROR("seteuid");
301 ret = -1;
302 goto end;
303 }
304 ret = 0;
305end:
306 return ret;
307}
308
309static
310int run_as_worker(struct run_as_worker *worker)
311{
312 int ret;
313 ssize_t writelen;
314 struct run_as_ret sendret;
315 size_t proc_orig_len;
316
317 /*
318 * Initialize worker. Set a different process cmdline.
319 */
320 proc_orig_len = strlen(worker->procname);
321 memset(worker->procname, 0, proc_orig_len);
322 strncpy(worker->procname, DEFAULT_RUN_AS_WORKER_NAME, proc_orig_len);
323
324 ret = pthread_setname_np(pthread_self(), DEFAULT_RUN_AS_WORKER_NAME);
325 if (ret) {
326 errno = ret;
327 ret = -1;
328 PERROR("pthread_setname_np");
329 return EXIT_FAILURE;
330 }
331
332 sendret.ret = 0;
333 sendret._errno = 0;
334 writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
335 sizeof(sendret));
336 if (writelen < sizeof(sendret)) {
337 PERROR("lttcomm_send_unix_sock error");
338 ret = EXIT_FAILURE;
339 goto end;
340 }
341
342 for (;;) {
343 ret = handle_one_cmd(worker);
344 if (ret < 0) {
345 ret = EXIT_FAILURE;
346 goto end;
347 } else if (ret > 0) {
348 break;
349 } else {
350 continue; /* Next command. */
351 }
352 }
353 ret = EXIT_SUCCESS;
354end:
355 return ret;
356}
357
358static
359int run_as_cmd(struct run_as_worker *worker,
360 enum run_as_cmd cmd,
361 struct run_as_data *data,
362 uid_t uid, gid_t gid)
363{
364 ssize_t readlen, writelen;
365 struct run_as_ret recvret;
366
367 pthread_mutex_lock(&worker_lock);
368 /*
369 * If we are non-root, we can only deal with our own uid.
370 */
371 if (geteuid() != 0) {
372 if (uid != geteuid()) {
373 recvret.ret = -1;
374 recvret._errno = EPERM;
375 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
376 uid, geteuid());
377 goto end;
378 }
379 }
380
381 data->cmd = cmd;
382 data->uid = uid;
383 data->gid = gid;
384
385 writelen = lttcomm_send_unix_sock(worker->sockpair[0], data,
386 sizeof(*data));
387 if (writelen < sizeof(*data)) {
388 PERROR("Error writing message to run_as");
389 recvret.ret = -1;
390 recvret._errno = errno;
391 goto end;
392 }
393
394 /* receive return value */
395 readlen = lttcomm_recv_unix_sock(worker->sockpair[0], &recvret,
396 sizeof(recvret));
397 if (!readlen) {
398 ERR("Run-as worker has hung-up during run_as_cmd");
399 recvret.ret = -1;
400 recvret._errno = EIO;
401 goto end;
402 } else if (readlen < sizeof(recvret)) {
403 PERROR("Error reading response from run_as");
404 recvret.ret = -1;
405 recvret._errno = errno;
406 }
407 if (do_recv_fd(worker, cmd, &recvret.ret)) {
408 recvret.ret = -1;
409 recvret._errno = EIO;
410 }
411
412end:
413 pthread_mutex_unlock(&worker_lock);
414 errno = recvret._errno;
415 return recvret.ret;
416}
417
418/*
419 * This is for debugging ONLY and should not be considered secure.
420 */
421static
422int run_as_noworker(enum run_as_cmd cmd,
423 struct run_as_data *data, uid_t uid, gid_t gid)
424{
425 int ret, saved_errno;
426 mode_t old_mask;
427 run_as_fct fct;
428
429 fct = run_as_enum_to_fct(cmd);
430 if (!fct) {
431 errno = -ENOSYS;
432 ret = -1;
433 goto end;
434 }
435 old_mask = umask(0);
436 ret = fct(data);
437 saved_errno = errno;
438 umask(old_mask);
439 errno = saved_errno;
440end:
441 return ret;
442}
443
444static
445int run_as(struct run_as_worker *worker,
446 enum run_as_cmd cmd,
447 struct run_as_data *data, uid_t uid, gid_t gid)
448{
449 int ret;
450
451 if (worker) {
452 DBG("Using run_as worker");
453 ret = run_as_cmd(worker, cmd, data, uid, gid);
454 } else {
455 DBG("Using run_as without worker");
456 ret = run_as_noworker(cmd, data, uid, gid);
457 }
458 return ret;
459}
460
461LTTNG_HIDDEN
462int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
463{
464 struct run_as_worker *worker = global_worker;
465 struct run_as_data data;
466
467 DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
468 path, mode, uid, gid);
469 strncpy(data.u.mkdir.path, path, PATH_MAX - 1);
470 data.u.mkdir.path[PATH_MAX - 1] = '\0';
471 data.u.mkdir.mode = mode;
472 return run_as(worker, RUN_AS_MKDIR_RECURSIVE, &data, uid, gid);
473}
474
475LTTNG_HIDDEN
476int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
477{
478 struct run_as_worker *worker = global_worker;
479 struct run_as_data data;
480
481 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
482 path, mode, uid, gid);
483 strncpy(data.u.mkdir.path, path, PATH_MAX - 1);
484 data.u.mkdir.path[PATH_MAX - 1] = '\0';
485 data.u.mkdir.mode = mode;
486 return run_as(worker, RUN_AS_MKDIR, &data, uid, gid);
487}
488
489/*
490 * Note: open_run_as is currently not working. We'd need to pass the fd
491 * opened in the child to the parent.
492 */
493LTTNG_HIDDEN
494int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
495{
496 struct run_as_worker *worker = global_worker;
497 struct run_as_data data;
498
499 DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
500 path, flags, mode, uid, gid);
501 strncpy(data.u.open.path, path, PATH_MAX - 1);
502 data.u.open.path[PATH_MAX - 1] = '\0';
503 data.u.open.flags = flags;
504 data.u.open.mode = mode;
505 return run_as(worker, RUN_AS_OPEN, &data, uid, gid);
506}
507
508LTTNG_HIDDEN
509int run_as_unlink(const char *path, uid_t uid, gid_t gid)
510{
511 struct run_as_worker *worker = global_worker;
512 struct run_as_data data;
513
514 DBG3("unlink() %s with for uid %d and gid %d",
515 path, uid, gid);
516 strncpy(data.u.unlink.path, path, PATH_MAX - 1);
517 data.u.unlink.path[PATH_MAX - 1] = '\0';
518 return run_as(worker, RUN_AS_UNLINK, &data, uid, gid);
519}
520
521LTTNG_HIDDEN
522int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid)
523{
524 struct run_as_worker *worker = global_worker;
525 struct run_as_data data;
526
527 DBG3("rmdir_recursive() %s with for uid %d and gid %d",
528 path, uid, gid);
529 strncpy(data.u.rmdir_recursive.path, path, PATH_MAX - 1);
530 data.u.rmdir_recursive.path[PATH_MAX - 1] = '\0';
531 return run_as(worker, RUN_AS_RMDIR_RECURSIVE, &data, uid, gid);
532}
533
534LTTNG_HIDDEN
535int run_as_create_worker(char *procname)
536{
537 pid_t pid;
538 int i, ret = 0;
539 ssize_t readlen;
540 struct run_as_ret recvret;
541 struct run_as_worker *worker;
542
543 if (!use_clone()) {
544 ret = 0;
545 goto end;
546 }
547 worker = zmalloc(sizeof(*worker));
548 if (!worker) {
549 ret = -ENOMEM;
550 goto end;
551 }
552 worker->procname = procname;
553 /* Create unix socket. */
554 if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
555 ret = -1;
556 goto error_sock;
557 }
558 /* Fork worker. */
559 pid = fork();
560 if (pid < 0) {
561 PERROR("fork");
562 ret = -1;
563 goto error_fork;
564 } else if (pid == 0) {
565 /* Child */
566
567 /* Just close, no shutdown. */
568 if (close(worker->sockpair[0])) {
569 PERROR("close");
570 exit(EXIT_FAILURE);
571 }
572 worker->sockpair[0] = -1;
573 ret = run_as_worker(worker);
574 if (lttcomm_close_unix_sock(worker->sockpair[1])) {
575 PERROR("close");
576 ret = -1;
577 }
578 worker->sockpair[1] = -1;
579 exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
580 } else {
581 /* Parent */
582
583 /* Just close, no shutdown. */
584 if (close(worker->sockpair[1])) {
585 PERROR("close");
586 ret = -1;
587 goto error_fork;
588 }
589 worker->sockpair[1] = -1;
590 worker->pid = pid;
591 /* Wait for worker to become ready. */
592 readlen = lttcomm_recv_unix_sock(worker->sockpair[0],
593 &recvret, sizeof(recvret));
594 if (readlen < sizeof(recvret)) {
595 ERR("readlen: %zd", readlen);
596 PERROR("Error reading response from run_as at creation");
597 ret = -1;
598 goto error_fork;
599 }
600 global_worker = worker;
601 }
602end:
603 return ret;
604
605 /* Error handling. */
606error_fork:
607 for (i = 0; i < 2; i++) {
608 if (worker->sockpair[i] < 0) {
609 continue;
610 }
611 if (lttcomm_close_unix_sock(worker->sockpair[i])) {
612 PERROR("close");
613 }
614 worker->sockpair[i] = -1;
615 }
616error_sock:
617 free(worker);
618 return ret;
619}
620
621LTTNG_HIDDEN
622void run_as_destroy_worker(void)
623{
624 struct run_as_worker *worker = global_worker;
625 int status;
626 pid_t pid;
627
628 if (!worker) {
629 return;
630 }
631 /* Close unix socket */
632 if (lttcomm_close_unix_sock(worker->sockpair[0])) {
633 PERROR("close");
634 }
635 worker->sockpair[0] = -1;
636 /* Wait for worker. */
637 pid = waitpid(worker->pid, &status, 0);
638 if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
639 PERROR("wait");
640 }
641 free(worker);
642 global_worker = NULL;
643}
This page took 0.024862 seconds and 4 git commands to generate.