Fix: Perform rcu barrier before tearing down the run-as worker
[lttng-tools.git] / src / common / runas.c
CommitLineData
60b6c79c
MD
1/*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 *
d14d33bf
AM
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.
60b6c79c
MD
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
d14d33bf 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
60b6c79c
MD
12 * more details.
13 *
d14d33bf
AM
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.
60b6c79c
MD
17 */
18
19#define _GNU_SOURCE
6c1c0768 20#define _LGPL_SOURCE
60b6c79c
MD
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>
c2b75c49 31#include <sched.h>
730389d9 32#include <sys/signal.h>
60b6c79c 33
90e535ef 34#include <common/common.h>
3fd15a74 35#include <common/utils.h>
e8fa9fb0 36#include <common/compat/getenv.h>
7567352f 37#include <common/sessiond-comm/unix.h>
60b6c79c 38
0857097f
DG
39#include "runas.h"
40
7567352f
MD
41struct run_as_data;
42typedef int (*run_as_fct)(struct run_as_data *data);
c2b75c49 43
e11d277b 44struct run_as_mkdir_data {
7567352f 45 char path[PATH_MAX];
60b6c79c
MD
46 mode_t mode;
47};
48
e11d277b 49struct run_as_open_data {
7567352f 50 char path[PATH_MAX];
60b6c79c
MD
51 int flags;
52 mode_t mode;
53};
54
4628484a 55struct run_as_unlink_data {
7567352f 56 char path[PATH_MAX];
4628484a
MD
57};
58
7567352f
MD
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;
4628484a
MD
81};
82
df5b86c8
MD
83struct run_as_ret {
84 int ret;
85 int _errno;
86};
87
7567352f
MD
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
8f0044bf
MD
99#ifdef VALGRIND
100static
101int use_clone(void)
102{
103 return 0;
104}
105#else
106static
107int use_clone(void)
108{
e8fa9fb0 109 return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
8f0044bf
MD
110}
111#endif
112
d77dded2
JG
113LTTNG_HIDDEN
114int _utils_mkdir_recursive_unsafe(const char *path, mode_t mode);
115
60b6c79c
MD
116/*
117 * Create recursively directory using the FULL path.
118 */
119static
7567352f 120int _mkdir_recursive(struct run_as_data *data)
60b6c79c 121{
60b6c79c 122 const char *path;
60b6c79c 123 mode_t mode;
60b6c79c 124
7567352f
MD
125 path = data->u.mkdir.path;
126 mode = data->u.mkdir.mode;
60b6c79c 127
d77dded2
JG
128 /* Safe to call as we have transitioned to the requested uid/gid. */
129 return _utils_mkdir_recursive_unsafe(path, mode);
60b6c79c
MD
130}
131
132static
7567352f 133int _mkdir(struct run_as_data *data)
60b6c79c 134{
7567352f
MD
135 return mkdir(data->u.mkdir.path, data->u.mkdir.mode);
136}
7ce36756 137
7567352f
MD
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);
60b6c79c
MD
148}
149
150static
7567352f 151int _rmdir_recursive(struct run_as_data *data)
60b6c79c 152{
7567352f
MD
153 return utils_recursive_rmdir(data->u.rmdir_recursive.path);
154}
df5b86c8 155
7567352f
MD
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 }
60b6c79c
MD
174}
175
4628484a 176static
7567352f
MD
177int do_send_fd(struct run_as_worker *worker,
178 enum run_as_cmd cmd, int fd)
4628484a 179{
7567352f 180 ssize_t len;
4628484a 181
7567352f
MD
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;
4628484a
MD
201}
202
203static
7567352f
MD
204int do_recv_fd(struct run_as_worker *worker,
205 enum run_as_cmd cmd, int *fd)
4628484a 206{
7567352f 207 ssize_t len;
4628484a 208
7567352f
MD
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);
da9ee832
JG
219 if (!len) {
220 return -1;
221 } else if (len < 0) {
7567352f
MD
222 PERROR("lttcomm_recv_fds_unix_sock");
223 return -1;
224 }
225 return 0;
4628484a
MD
226}
227
7567352f
MD
228/*
229 * Return < 0 on error, 0 if OK, 1 on hangup.
230 */
c2b75c49 231static
7567352f 232int handle_one_cmd(struct run_as_worker *worker)
c2b75c49 233{
7567352f
MD
234 int ret = 0;
235 struct run_as_data data;
236 ssize_t readlen, writelen;
df5b86c8 237 struct run_as_ret sendret;
7567352f
MD
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 }
c2b75c49 254
7567352f
MD
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);
1576d582 264 if (ret < 0) {
4c462e79 265 PERROR("setegid");
6d73c4ef 266 goto write_return;
1576d582 267 }
c2b75c49 268 }
7567352f
MD
269 if (data.uid != prev_euid) {
270 ret = seteuid(data.uid);
1576d582 271 if (ret < 0) {
4c462e79 272 PERROR("seteuid");
6d73c4ef 273 goto write_return;
1576d582 274 }
c2b75c49
MD
275 }
276 /*
277 * Also set umask to 0 for mkdir executable bit.
278 */
279 umask(0);
7567352f 280 ret = (*cmd)(&data);
6d73c4ef
MD
281
282write_return:
df5b86c8
MD
283 sendret.ret = ret;
284 sendret._errno = errno;
c2b75c49 285 /* send back return value */
7567352f
MD
286 writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
287 sizeof(sendret));
6cd525e8 288 if (writelen < sizeof(sendret)) {
7567352f
MD
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");
6cd525e8 329 return EXIT_FAILURE;
6cd525e8 330 }
7567352f
MD
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;
c2b75c49
MD
356}
357
60b6c79c 358static
7567352f
MD
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)
60b6c79c 363{
7567352f 364 ssize_t readlen, writelen;
df5b86c8 365 struct run_as_ret recvret;
60b6c79c 366
7567352f 367 pthread_mutex_lock(&worker_lock);
60b6c79c
MD
368 /*
369 * If we are non-root, we can only deal with our own uid.
370 */
371 if (geteuid() != 0) {
372 if (uid != geteuid()) {
df5b86c8
MD
373 recvret.ret = -1;
374 recvret._errno = EPERM;
60b6c79c
MD
375 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
376 uid, geteuid());
df5b86c8 377 goto end;
60b6c79c 378 }
60b6c79c
MD
379 }
380
7567352f
MD
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");
df5b86c8
MD
389 recvret.ret = -1;
390 recvret._errno = errno;
60b6c79c 391 goto end;
c2b75c49 392 }
7567352f 393
c2b75c49 394 /* receive return value */
7567352f
MD
395 readlen = lttcomm_recv_unix_sock(worker->sockpair[0], &recvret,
396 sizeof(recvret));
da9ee832
JG
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)) {
7567352f 403 PERROR("Error reading response from run_as");
df5b86c8
MD
404 recvret.ret = -1;
405 recvret._errno = errno;
6cd525e8 406 }
7567352f 407 if (do_recv_fd(worker, cmd, &recvret.ret)) {
df5b86c8 408 recvret.ret = -1;
da9ee832 409 recvret._errno = EIO;
4c462e79 410 }
7567352f 411
60b6c79c 412end:
7567352f 413 pthread_mutex_unlock(&worker_lock);
df5b86c8
MD
414 errno = recvret._errno;
415 return recvret.ret;
60b6c79c
MD
416}
417
2d85a600 418/*
7567352f 419 * This is for debugging ONLY and should not be considered secure.
2d85a600
MD
420 */
421static
7567352f
MD
422int run_as_noworker(enum run_as_cmd cmd,
423 struct run_as_data *data, uid_t uid, gid_t gid)
2d85a600 424{
df5b86c8 425 int ret, saved_errno;
5b73926f 426 mode_t old_mask;
7567352f 427 run_as_fct fct;
5b73926f 428
7567352f
MD
429 fct = run_as_enum_to_fct(cmd);
430 if (!fct) {
431 errno = -ENOSYS;
432 ret = -1;
433 goto end;
434 }
5b73926f 435 old_mask = umask(0);
7567352f 436 ret = fct(data);
df5b86c8 437 saved_errno = errno;
5b73926f 438 umask(old_mask);
df5b86c8 439 errno = saved_errno;
7567352f 440end:
5b73926f 441 return ret;
2d85a600
MD
442}
443
444static
7567352f
MD
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)
2d85a600 448{
7567352f
MD
449 int ret;
450
451 if (worker) {
452 DBG("Using run_as worker");
453 ret = run_as_cmd(worker, cmd, data, uid, gid);
2d85a600 454 } else {
7567352f
MD
455 DBG("Using run_as without worker");
456 ret = run_as_noworker(cmd, data, uid, gid);
2d85a600 457 }
7567352f 458 return ret;
2d85a600
MD
459}
460
90e535ef 461LTTNG_HIDDEN
e11d277b 462int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
60b6c79c 463{
7567352f
MD
464 struct run_as_worker *worker = global_worker;
465 struct run_as_data data;
60b6c79c
MD
466
467 DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
468 path, mode, uid, gid);
7567352f
MD
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);
60b6c79c
MD
473}
474
90e535ef 475LTTNG_HIDDEN
e11d277b 476int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
60b6c79c 477{
7567352f
MD
478 struct run_as_worker *worker = global_worker;
479 struct run_as_data data;
60b6c79c
MD
480
481 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
482 path, mode, uid, gid);
7567352f
MD
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);
60b6c79c
MD
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 */
90e535ef 493LTTNG_HIDDEN
e11d277b 494int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
60b6c79c 495{
7567352f
MD
496 struct run_as_worker *worker = global_worker;
497 struct run_as_data data;
c2b75c49 498
47fb7563
MD
499 DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
500 path, flags, mode, uid, gid);
7567352f
MD
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);
60b6c79c 506}
4628484a
MD
507
508LTTNG_HIDDEN
509int run_as_unlink(const char *path, uid_t uid, gid_t gid)
510{
7567352f
MD
511 struct run_as_worker *worker = global_worker;
512 struct run_as_data data;
4628484a
MD
513
514 DBG3("unlink() %s with for uid %d and gid %d",
515 path, uid, gid);
7567352f
MD
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);
4628484a
MD
519}
520
521LTTNG_HIDDEN
7567352f 522int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid)
4628484a 523{
7567352f
MD
524 struct run_as_worker *worker = global_worker;
525 struct run_as_data data;
4628484a 526
7567352f 527 DBG3("rmdir_recursive() %s with for uid %d and gid %d",
4628484a 528 path, uid, gid);
7567352f
MD
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;
4628484a 643}
This page took 0.076286 seconds and 5 git commands to generate.