Add logging to 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>
749b7a0c 33#include <assert.h>
60b6c79c 34
90e535ef 35#include <common/common.h>
3fd15a74 36#include <common/utils.h>
e8fa9fb0 37#include <common/compat/getenv.h>
7567352f 38#include <common/sessiond-comm/unix.h>
60b6c79c 39
0857097f
DG
40#include "runas.h"
41
7567352f
MD
42struct run_as_data;
43typedef int (*run_as_fct)(struct run_as_data *data);
c2b75c49 44
e11d277b 45struct run_as_mkdir_data {
7567352f 46 char path[PATH_MAX];
60b6c79c
MD
47 mode_t mode;
48};
49
e11d277b 50struct run_as_open_data {
7567352f 51 char path[PATH_MAX];
60b6c79c
MD
52 int flags;
53 mode_t mode;
54};
55
4628484a 56struct run_as_unlink_data {
7567352f 57 char path[PATH_MAX];
4628484a
MD
58};
59
7567352f
MD
60struct run_as_rmdir_recursive_data {
61 char path[PATH_MAX];
62};
63
64enum run_as_cmd {
65 RUN_AS_MKDIR,
66 RUN_AS_OPEN,
67 RUN_AS_UNLINK,
68 RUN_AS_RMDIR_RECURSIVE,
69 RUN_AS_MKDIR_RECURSIVE,
70};
71
72struct run_as_data {
73 enum run_as_cmd cmd;
74 union {
75 struct run_as_mkdir_data mkdir;
76 struct run_as_open_data open;
77 struct run_as_unlink_data unlink;
78 struct run_as_rmdir_recursive_data rmdir_recursive;
79 } u;
80 uid_t uid;
81 gid_t gid;
4628484a
MD
82};
83
df5b86c8
MD
84struct run_as_ret {
85 int ret;
86 int _errno;
87};
88
7567352f
MD
89struct run_as_worker {
90 pid_t pid; /* Worker PID. */
91 int sockpair[2];
92 char *procname;
93};
94
95/* Single global worker per process (for now). */
96static struct run_as_worker *global_worker;
97/* Lock protecting the worker. */
98static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
99
8f0044bf
MD
100#ifdef VALGRIND
101static
102int use_clone(void)
103{
104 return 0;
105}
106#else
107static
108int use_clone(void)
109{
e8fa9fb0 110 return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
8f0044bf
MD
111}
112#endif
113
d77dded2
JG
114LTTNG_HIDDEN
115int _utils_mkdir_recursive_unsafe(const char *path, mode_t mode);
116
60b6c79c
MD
117/*
118 * Create recursively directory using the FULL path.
119 */
120static
7567352f 121int _mkdir_recursive(struct run_as_data *data)
60b6c79c 122{
60b6c79c 123 const char *path;
60b6c79c 124 mode_t mode;
60b6c79c 125
7567352f
MD
126 path = data->u.mkdir.path;
127 mode = data->u.mkdir.mode;
60b6c79c 128
d77dded2
JG
129 /* Safe to call as we have transitioned to the requested uid/gid. */
130 return _utils_mkdir_recursive_unsafe(path, mode);
60b6c79c
MD
131}
132
133static
7567352f 134int _mkdir(struct run_as_data *data)
60b6c79c 135{
7567352f
MD
136 return mkdir(data->u.mkdir.path, data->u.mkdir.mode);
137}
7ce36756 138
7567352f
MD
139static
140int _open(struct run_as_data *data)
141{
142 return open(data->u.open.path, data->u.open.flags, data->u.open.mode);
143}
144
145static
146int _unlink(struct run_as_data *data)
147{
148 return unlink(data->u.unlink.path);
60b6c79c
MD
149}
150
151static
7567352f 152int _rmdir_recursive(struct run_as_data *data)
60b6c79c 153{
7567352f
MD
154 return utils_recursive_rmdir(data->u.rmdir_recursive.path);
155}
df5b86c8 156
7567352f
MD
157static
158run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd)
159{
160 switch (cmd) {
161 case RUN_AS_MKDIR:
162 return _mkdir;
163 case RUN_AS_OPEN:
164 return _open;
165 case RUN_AS_UNLINK:
166 return _unlink;
167 case RUN_AS_RMDIR_RECURSIVE:
168 return _rmdir_recursive;
169 case RUN_AS_MKDIR_RECURSIVE:
170 return _mkdir_recursive;
171 default:
172 ERR("Unknown command %d", (int) cmd)
173 return NULL;
174 }
60b6c79c
MD
175}
176
4628484a 177static
7567352f
MD
178int do_send_fd(struct run_as_worker *worker,
179 enum run_as_cmd cmd, int fd)
4628484a 180{
7567352f 181 ssize_t len;
4628484a 182
7567352f
MD
183 switch (cmd) {
184 case RUN_AS_OPEN:
185 break;
186 default:
187 return 0;
188 }
189 if (fd < 0) {
190 return 0;
191 }
192 len = lttcomm_send_fds_unix_sock(worker->sockpair[1], &fd, 1);
193 if (len < 0) {
194 PERROR("lttcomm_send_fds_unix_sock");
195 return -1;
196 }
197 if (close(fd) < 0) {
198 PERROR("close");
199 return -1;
200 }
201 return 0;
4628484a
MD
202}
203
204static
7567352f
MD
205int do_recv_fd(struct run_as_worker *worker,
206 enum run_as_cmd cmd, int *fd)
4628484a 207{
7567352f 208 ssize_t len;
4628484a 209
7567352f
MD
210 switch (cmd) {
211 case RUN_AS_OPEN:
212 break;
213 default:
214 return 0;
215 }
216 if (*fd < 0) {
217 return 0;
218 }
219 len = lttcomm_recv_fds_unix_sock(worker->sockpair[0], fd, 1);
da9ee832
JG
220 if (!len) {
221 return -1;
222 } else if (len < 0) {
7567352f
MD
223 PERROR("lttcomm_recv_fds_unix_sock");
224 return -1;
225 }
226 return 0;
4628484a
MD
227}
228
7567352f
MD
229/*
230 * Return < 0 on error, 0 if OK, 1 on hangup.
231 */
c2b75c49 232static
7567352f 233int handle_one_cmd(struct run_as_worker *worker)
c2b75c49 234{
7567352f
MD
235 int ret = 0;
236 struct run_as_data data;
237 ssize_t readlen, writelen;
df5b86c8 238 struct run_as_ret sendret;
7567352f
MD
239 run_as_fct cmd;
240 uid_t prev_euid;
241
242 /* Read data */
243 readlen = lttcomm_recv_unix_sock(worker->sockpair[1], &data,
244 sizeof(data));
245 if (readlen == 0) {
246 /* hang up */
247 ret = 1;
248 goto end;
249 }
250 if (readlen < sizeof(data)) {
251 PERROR("lttcomm_recv_unix_sock error");
252 ret = -1;
253 goto end;
254 }
c2b75c49 255
7567352f
MD
256 cmd = run_as_enum_to_fct(data.cmd);
257 if (!cmd) {
258 ret = -1;
259 goto end;
260 }
261
262 prev_euid = getuid();
263 if (data.gid != getegid()) {
264 ret = setegid(data.gid);
1576d582 265 if (ret < 0) {
4c462e79 266 PERROR("setegid");
6d73c4ef 267 goto write_return;
1576d582 268 }
c2b75c49 269 }
7567352f
MD
270 if (data.uid != prev_euid) {
271 ret = seteuid(data.uid);
1576d582 272 if (ret < 0) {
4c462e79 273 PERROR("seteuid");
6d73c4ef 274 goto write_return;
1576d582 275 }
c2b75c49
MD
276 }
277 /*
278 * Also set umask to 0 for mkdir executable bit.
279 */
280 umask(0);
7567352f 281 ret = (*cmd)(&data);
6d73c4ef
MD
282
283write_return:
df5b86c8
MD
284 sendret.ret = ret;
285 sendret._errno = errno;
c2b75c49 286 /* send back return value */
7567352f
MD
287 writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
288 sizeof(sendret));
6cd525e8 289 if (writelen < sizeof(sendret)) {
7567352f
MD
290 PERROR("lttcomm_send_unix_sock error");
291 ret = -1;
292 goto end;
293 }
294 ret = do_send_fd(worker, data.cmd, ret);
295 if (ret) {
296 PERROR("do_send_fd error");
297 ret = -1;
298 goto end;
299 }
300 if (seteuid(prev_euid) < 0) {
301 PERROR("seteuid");
302 ret = -1;
303 goto end;
304 }
305 ret = 0;
306end:
307 return ret;
308}
309
310static
311int run_as_worker(struct run_as_worker *worker)
312{
313 int ret;
314 ssize_t writelen;
315 struct run_as_ret sendret;
316 size_t proc_orig_len;
317
318 /*
319 * Initialize worker. Set a different process cmdline.
320 */
321 proc_orig_len = strlen(worker->procname);
322 memset(worker->procname, 0, proc_orig_len);
323 strncpy(worker->procname, DEFAULT_RUN_AS_WORKER_NAME, proc_orig_len);
324
325 ret = pthread_setname_np(pthread_self(), DEFAULT_RUN_AS_WORKER_NAME);
326 if (ret) {
327 errno = ret;
328 ret = -1;
329 PERROR("pthread_setname_np");
6cd525e8 330 return EXIT_FAILURE;
6cd525e8 331 }
7567352f
MD
332
333 sendret.ret = 0;
334 sendret._errno = 0;
335 writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
336 sizeof(sendret));
337 if (writelen < sizeof(sendret)) {
338 PERROR("lttcomm_send_unix_sock error");
339 ret = EXIT_FAILURE;
340 goto end;
341 }
342
343 for (;;) {
344 ret = handle_one_cmd(worker);
345 if (ret < 0) {
346 ret = EXIT_FAILURE;
347 goto end;
348 } else if (ret > 0) {
349 break;
350 } else {
351 continue; /* Next command. */
352 }
353 }
354 ret = EXIT_SUCCESS;
355end:
356 return ret;
c2b75c49
MD
357}
358
60b6c79c 359static
7567352f
MD
360int run_as_cmd(struct run_as_worker *worker,
361 enum run_as_cmd cmd,
362 struct run_as_data *data,
363 uid_t uid, gid_t gid)
60b6c79c 364{
7567352f 365 ssize_t readlen, writelen;
df5b86c8 366 struct run_as_ret recvret;
60b6c79c
MD
367
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:
df5b86c8
MD
413 errno = recvret._errno;
414 return recvret.ret;
60b6c79c
MD
415}
416
2d85a600 417/*
7567352f 418 * This is for debugging ONLY and should not be considered secure.
2d85a600
MD
419 */
420static
7567352f
MD
421int run_as_noworker(enum run_as_cmd cmd,
422 struct run_as_data *data, uid_t uid, gid_t gid)
2d85a600 423{
df5b86c8 424 int ret, saved_errno;
5b73926f 425 mode_t old_mask;
7567352f 426 run_as_fct fct;
5b73926f 427
7567352f
MD
428 fct = run_as_enum_to_fct(cmd);
429 if (!fct) {
430 errno = -ENOSYS;
431 ret = -1;
432 goto end;
433 }
5b73926f 434 old_mask = umask(0);
7567352f 435 ret = fct(data);
df5b86c8 436 saved_errno = errno;
5b73926f 437 umask(old_mask);
df5b86c8 438 errno = saved_errno;
7567352f 439end:
5b73926f 440 return ret;
2d85a600
MD
441}
442
443static
749b7a0c 444int run_as(enum run_as_cmd cmd, struct run_as_data *data, uid_t uid, gid_t gid)
2d85a600 445{
7567352f
MD
446 int ret;
447
749b7a0c 448 if (use_clone()) {
7567352f 449 DBG("Using run_as worker");
749b7a0c
JG
450 pthread_mutex_lock(&worker_lock);
451 assert(global_worker);
452 ret = run_as_cmd(global_worker, cmd, data, uid, gid);
453 pthread_mutex_unlock(&worker_lock);
454
2d85a600 455 } else {
7567352f
MD
456 DBG("Using run_as without worker");
457 ret = run_as_noworker(cmd, data, uid, gid);
2d85a600 458 }
7567352f 459 return ret;
2d85a600
MD
460}
461
90e535ef 462LTTNG_HIDDEN
e11d277b 463int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
60b6c79c 464{
7567352f 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;
749b7a0c 472 return run_as(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 478 struct run_as_data data;
60b6c79c
MD
479
480 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
481 path, mode, uid, gid);
7567352f
MD
482 strncpy(data.u.mkdir.path, path, PATH_MAX - 1);
483 data.u.mkdir.path[PATH_MAX - 1] = '\0';
484 data.u.mkdir.mode = mode;
749b7a0c 485 return run_as(RUN_AS_MKDIR, &data, uid, gid);
60b6c79c
MD
486}
487
488/*
489 * Note: open_run_as is currently not working. We'd need to pass the fd
490 * opened in the child to the parent.
491 */
90e535ef 492LTTNG_HIDDEN
e11d277b 493int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
60b6c79c 494{
7567352f 495 struct run_as_data data;
c2b75c49 496
47fb7563
MD
497 DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
498 path, flags, mode, uid, gid);
7567352f
MD
499 strncpy(data.u.open.path, path, PATH_MAX - 1);
500 data.u.open.path[PATH_MAX - 1] = '\0';
501 data.u.open.flags = flags;
502 data.u.open.mode = mode;
749b7a0c 503 return run_as(RUN_AS_OPEN, &data, uid, gid);
60b6c79c 504}
4628484a
MD
505
506LTTNG_HIDDEN
507int run_as_unlink(const char *path, uid_t uid, gid_t gid)
508{
7567352f 509 struct run_as_data data;
4628484a
MD
510
511 DBG3("unlink() %s with for uid %d and gid %d",
512 path, uid, gid);
7567352f
MD
513 strncpy(data.u.unlink.path, path, PATH_MAX - 1);
514 data.u.unlink.path[PATH_MAX - 1] = '\0';
749b7a0c 515 return run_as(RUN_AS_UNLINK, &data, uid, gid);
4628484a
MD
516}
517
518LTTNG_HIDDEN
7567352f 519int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid)
4628484a 520{
7567352f 521 struct run_as_data data;
4628484a 522
7567352f 523 DBG3("rmdir_recursive() %s with for uid %d and gid %d",
4628484a 524 path, uid, gid);
7567352f
MD
525 strncpy(data.u.rmdir_recursive.path, path, PATH_MAX - 1);
526 data.u.rmdir_recursive.path[PATH_MAX - 1] = '\0';
749b7a0c 527 return run_as(RUN_AS_RMDIR_RECURSIVE, &data, uid, gid);
7567352f
MD
528}
529
f8f66d38
JG
530static
531void reset_sighandler(void)
532{
533 int sig;
534
978d5d79 535 DBG("Resetting run_as worker signal handlers to default");
f8f66d38
JG
536 for (sig = SIGHUP; sig <= SIGUNUSED; sig++) {
537 /* Skip unblockable signals. */
538 if (sig == SIGKILL || sig == SIGSTOP) {
539 continue;
540 }
541 if (signal(sig, SIG_DFL) == SIG_ERR) {
542 PERROR("reset signal %d", sig);
543 }
544 }
545}
546
7567352f
MD
547LTTNG_HIDDEN
548int run_as_create_worker(char *procname)
549{
550 pid_t pid;
551 int i, ret = 0;
552 ssize_t readlen;
553 struct run_as_ret recvret;
554 struct run_as_worker *worker;
555
749b7a0c
JG
556 pthread_mutex_lock(&worker_lock);
557 assert(!global_worker);
7567352f 558 if (!use_clone()) {
749b7a0c
JG
559 /*
560 * Don't initialize a worker, all run_as tasks will be performed
561 * in the current process.
562 */
7567352f
MD
563 ret = 0;
564 goto end;
565 }
566 worker = zmalloc(sizeof(*worker));
567 if (!worker) {
568 ret = -ENOMEM;
569 goto end;
570 }
571 worker->procname = procname;
572 /* Create unix socket. */
573 if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
574 ret = -1;
575 goto error_sock;
576 }
577 /* Fork worker. */
578 pid = fork();
579 if (pid < 0) {
580 PERROR("fork");
581 ret = -1;
582 goto error_fork;
583 } else if (pid == 0) {
584 /* Child */
585
f8f66d38
JG
586 reset_sighandler();
587
749b7a0c
JG
588 /* The child has no use for this lock. */
589 pthread_mutex_unlock(&worker_lock);
7567352f
MD
590 /* Just close, no shutdown. */
591 if (close(worker->sockpair[0])) {
592 PERROR("close");
593 exit(EXIT_FAILURE);
594 }
595 worker->sockpair[0] = -1;
596 ret = run_as_worker(worker);
597 if (lttcomm_close_unix_sock(worker->sockpair[1])) {
598 PERROR("close");
599 ret = -1;
600 }
601 worker->sockpair[1] = -1;
978d5d79 602 LOG(ret ? PRINT_ERR : PRINT_DBG, "run_as worker exiting (ret = %d)", ret);
7567352f
MD
603 exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
604 } else {
605 /* Parent */
606
607 /* Just close, no shutdown. */
608 if (close(worker->sockpair[1])) {
609 PERROR("close");
610 ret = -1;
611 goto error_fork;
612 }
613 worker->sockpair[1] = -1;
614 worker->pid = pid;
615 /* Wait for worker to become ready. */
616 readlen = lttcomm_recv_unix_sock(worker->sockpair[0],
617 &recvret, sizeof(recvret));
618 if (readlen < sizeof(recvret)) {
619 ERR("readlen: %zd", readlen);
620 PERROR("Error reading response from run_as at creation");
621 ret = -1;
622 goto error_fork;
623 }
624 global_worker = worker;
625 }
626end:
749b7a0c 627 pthread_mutex_unlock(&worker_lock);
7567352f
MD
628 return ret;
629
630 /* Error handling. */
631error_fork:
632 for (i = 0; i < 2; i++) {
633 if (worker->sockpair[i] < 0) {
634 continue;
635 }
636 if (lttcomm_close_unix_sock(worker->sockpair[i])) {
637 PERROR("close");
638 }
639 worker->sockpair[i] = -1;
640 }
641error_sock:
642 free(worker);
749b7a0c 643 pthread_mutex_unlock(&worker_lock);
7567352f
MD
644 return ret;
645}
646
647LTTNG_HIDDEN
648void run_as_destroy_worker(void)
649{
650 struct run_as_worker *worker = global_worker;
7567352f 651
978d5d79 652 DBG("Destroying run_as worker");
749b7a0c 653 pthread_mutex_lock(&worker_lock);
7567352f 654 if (!worker) {
749b7a0c 655 goto end;
7567352f
MD
656 }
657 /* Close unix socket */
978d5d79 658 DBG("Closing run_as worker socket");
7567352f
MD
659 if (lttcomm_close_unix_sock(worker->sockpair[0])) {
660 PERROR("close");
661 }
662 worker->sockpair[0] = -1;
663 /* Wait for worker. */
f8f66d38
JG
664 for (;;) {
665 int status;
666 pid_t wait_ret;
667
668 wait_ret = waitpid(worker->pid, &status, 0);
669 if (wait_ret < 0) {
670 if (errno == EINTR) {
671 continue;
672 }
673 PERROR("waitpid");
674 break;
675 }
676
677 if (WIFEXITED(status)) {
678 LOG(WEXITSTATUS(status) == 0 ? PRINT_DBG : PRINT_ERR,
679 DEFAULT_RUN_AS_WORKER_NAME " terminated with status code %d",
680 WEXITSTATUS(status));
681 break;
682 } else if (WIFSIGNALED(status)) {
683 ERR(DEFAULT_RUN_AS_WORKER_NAME " was killed by signal %d",
684 WTERMSIG(status));
685 break;
686 }
7567352f
MD
687 }
688 free(worker);
689 global_worker = NULL;
749b7a0c
JG
690end:
691 pthread_mutex_unlock(&worker_lock);
4628484a 692}
This page took 0.067507 seconds and 4 git commands to generate.