Bump LTTng MI schema to 3.0
[deliverable/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 _LGPL_SOURCE
20#include <errno.h>
21#include <limits.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/wait.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <unistd.h>
29#include <fcntl.h>
30#include <sched.h>
31#include <sys/signal.h>
32#include <assert.h>
33#include <signal.h>
34
35#include <common/common.h>
36#include <common/utils.h>
37#include <common/compat/getenv.h>
38#include <common/compat/prctl.h>
39#include <common/sessiond-comm/unix.h>
40
41#include "runas.h"
42
43struct run_as_data;
44typedef int (*run_as_fct)(struct run_as_data *data);
45
46struct run_as_mkdir_data {
47 char path[PATH_MAX];
48 mode_t mode;
49};
50
51struct run_as_open_data {
52 char path[PATH_MAX];
53 int flags;
54 mode_t mode;
55};
56
57struct run_as_unlink_data {
58 char path[PATH_MAX];
59};
60
61struct run_as_rmdir_recursive_data {
62 char path[PATH_MAX];
63};
64
65enum run_as_cmd {
66 RUN_AS_MKDIR,
67 RUN_AS_OPEN,
68 RUN_AS_UNLINK,
69 RUN_AS_RMDIR_RECURSIVE,
70 RUN_AS_MKDIR_RECURSIVE,
71};
72
73struct run_as_data {
74 enum run_as_cmd cmd;
75 union {
76 struct run_as_mkdir_data mkdir;
77 struct run_as_open_data open;
78 struct run_as_unlink_data unlink;
79 struct run_as_rmdir_recursive_data rmdir_recursive;
80 } u;
81 uid_t uid;
82 gid_t gid;
83};
84
85struct run_as_ret {
86 int ret;
87 int _errno;
88};
89
90struct run_as_worker {
91 pid_t pid; /* Worker PID. */
92 int sockpair[2];
93 char *procname;
94};
95
96/* Single global worker per process (for now). */
97static struct run_as_worker *global_worker;
98/* Lock protecting the worker. */
99static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
100
101#ifdef VALGRIND
102static
103int use_clone(void)
104{
105 return 0;
106}
107#else
108static
109int use_clone(void)
110{
111 return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
112}
113#endif
114
115LTTNG_HIDDEN
116int _utils_mkdir_recursive_unsafe(const char *path, mode_t mode);
117
118/*
119 * Create recursively directory using the FULL path.
120 */
121static
122int _mkdir_recursive(struct run_as_data *data)
123{
124 const char *path;
125 mode_t mode;
126
127 path = data->u.mkdir.path;
128 mode = data->u.mkdir.mode;
129
130 /* Safe to call as we have transitioned to the requested uid/gid. */
131 return _utils_mkdir_recursive_unsafe(path, mode);
132}
133
134static
135int _mkdir(struct run_as_data *data)
136{
137 return mkdir(data->u.mkdir.path, data->u.mkdir.mode);
138}
139
140static
141int _open(struct run_as_data *data)
142{
143 return open(data->u.open.path, data->u.open.flags, data->u.open.mode);
144}
145
146static
147int _unlink(struct run_as_data *data)
148{
149 return unlink(data->u.unlink.path);
150}
151
152static
153int _rmdir_recursive(struct run_as_data *data)
154{
155 return utils_recursive_rmdir(data->u.rmdir_recursive.path);
156}
157
158static
159run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd)
160{
161 switch (cmd) {
162 case RUN_AS_MKDIR:
163 return _mkdir;
164 case RUN_AS_OPEN:
165 return _open;
166 case RUN_AS_UNLINK:
167 return _unlink;
168 case RUN_AS_RMDIR_RECURSIVE:
169 return _rmdir_recursive;
170 case RUN_AS_MKDIR_RECURSIVE:
171 return _mkdir_recursive;
172 default:
173 ERR("Unknown command %d", (int) cmd)
174 return NULL;
175 }
176}
177
178static
179int do_send_fd(struct run_as_worker *worker,
180 enum run_as_cmd cmd, int fd)
181{
182 ssize_t len;
183
184 switch (cmd) {
185 case RUN_AS_OPEN:
186 break;
187 default:
188 return 0;
189 }
190 if (fd < 0) {
191 return 0;
192 }
193 len = lttcomm_send_fds_unix_sock(worker->sockpair[1], &fd, 1);
194 if (len < 0) {
195 PERROR("lttcomm_send_fds_unix_sock");
196 return -1;
197 }
198 if (close(fd) < 0) {
199 PERROR("close");
200 return -1;
201 }
202 return 0;
203}
204
205static
206int do_recv_fd(struct run_as_worker *worker,
207 enum run_as_cmd cmd, int *fd)
208{
209 ssize_t len;
210
211 switch (cmd) {
212 case RUN_AS_OPEN:
213 break;
214 default:
215 return 0;
216 }
217 if (*fd < 0) {
218 return 0;
219 }
220 len = lttcomm_recv_fds_unix_sock(worker->sockpair[0], fd, 1);
221 if (!len) {
222 return -1;
223 } else if (len < 0) {
224 PERROR("lttcomm_recv_fds_unix_sock");
225 return -1;
226 }
227 return 0;
228}
229
230/*
231 * Return < 0 on error, 0 if OK, 1 on hangup.
232 */
233static
234int handle_one_cmd(struct run_as_worker *worker)
235{
236 int ret = 0;
237 struct run_as_data data;
238 ssize_t readlen, writelen;
239 struct run_as_ret sendret;
240 run_as_fct cmd;
241 uid_t prev_euid;
242
243 /* Read data */
244 readlen = lttcomm_recv_unix_sock(worker->sockpair[1], &data,
245 sizeof(data));
246 if (readlen == 0) {
247 /* hang up */
248 ret = 1;
249 goto end;
250 }
251 if (readlen < sizeof(data)) {
252 PERROR("lttcomm_recv_unix_sock error");
253 ret = -1;
254 goto end;
255 }
256
257 cmd = run_as_enum_to_fct(data.cmd);
258 if (!cmd) {
259 ret = -1;
260 goto end;
261 }
262
263 prev_euid = getuid();
264 if (data.gid != getegid()) {
265 ret = setegid(data.gid);
266 if (ret < 0) {
267 PERROR("setegid");
268 goto write_return;
269 }
270 }
271 if (data.uid != prev_euid) {
272 ret = seteuid(data.uid);
273 if (ret < 0) {
274 PERROR("seteuid");
275 goto write_return;
276 }
277 }
278 /*
279 * Also set umask to 0 for mkdir executable bit.
280 */
281 umask(0);
282 ret = (*cmd)(&data);
283
284write_return:
285 sendret.ret = ret;
286 sendret._errno = errno;
287 /* send back return value */
288 writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
289 sizeof(sendret));
290 if (writelen < sizeof(sendret)) {
291 PERROR("lttcomm_send_unix_sock error");
292 ret = -1;
293 goto end;
294 }
295 ret = do_send_fd(worker, data.cmd, ret);
296 if (ret) {
297 PERROR("do_send_fd error");
298 ret = -1;
299 goto end;
300 }
301 if (seteuid(prev_euid) < 0) {
302 PERROR("seteuid");
303 ret = -1;
304 goto end;
305 }
306 ret = 0;
307end:
308 return ret;
309}
310
311static
312int run_as_worker(struct run_as_worker *worker)
313{
314 int ret;
315 ssize_t writelen;
316 struct run_as_ret sendret;
317 size_t proc_orig_len;
318
319 /*
320 * Initialize worker. Set a different process cmdline.
321 */
322 proc_orig_len = strlen(worker->procname);
323 memset(worker->procname, 0, proc_orig_len);
324 strncpy(worker->procname, DEFAULT_RUN_AS_WORKER_NAME, proc_orig_len);
325
326 ret = lttng_prctl(PR_SET_NAME,
327 (unsigned long) DEFAULT_RUN_AS_WORKER_NAME, 0, 0, 0);
328 if (ret && ret != -ENOSYS) {
329 /* Don't fail as this is not essential. */
330 PERROR("prctl PR_SET_NAME");
331 ret = 0;
332 }
333
334 sendret.ret = 0;
335 sendret._errno = 0;
336 writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
337 sizeof(sendret));
338 if (writelen < sizeof(sendret)) {
339 PERROR("lttcomm_send_unix_sock error");
340 ret = EXIT_FAILURE;
341 goto end;
342 }
343
344 for (;;) {
345 ret = handle_one_cmd(worker);
346 if (ret < 0) {
347 ret = EXIT_FAILURE;
348 goto end;
349 } else if (ret > 0) {
350 break;
351 } else {
352 continue; /* Next command. */
353 }
354 }
355 ret = EXIT_SUCCESS;
356end:
357 return ret;
358}
359
360static
361int run_as_cmd(struct run_as_worker *worker,
362 enum run_as_cmd cmd,
363 struct run_as_data *data,
364 uid_t uid, gid_t gid)
365{
366 ssize_t readlen, writelen;
367 struct run_as_ret recvret;
368
369 /*
370 * If we are non-root, we can only deal with our own uid.
371 */
372 if (geteuid() != 0) {
373 if (uid != geteuid()) {
374 recvret.ret = -1;
375 recvret._errno = EPERM;
376 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
377 (int) uid, (int) geteuid());
378 goto end;
379 }
380 }
381
382 data->cmd = cmd;
383 data->uid = uid;
384 data->gid = gid;
385
386 writelen = lttcomm_send_unix_sock(worker->sockpair[0], data,
387 sizeof(*data));
388 if (writelen < sizeof(*data)) {
389 PERROR("Error writing message to run_as");
390 recvret.ret = -1;
391 recvret._errno = errno;
392 goto end;
393 }
394
395 /* receive return value */
396 readlen = lttcomm_recv_unix_sock(worker->sockpair[0], &recvret,
397 sizeof(recvret));
398 if (!readlen) {
399 ERR("Run-as worker has hung-up during run_as_cmd");
400 recvret.ret = -1;
401 recvret._errno = EIO;
402 goto end;
403 } else if (readlen < sizeof(recvret)) {
404 PERROR("Error reading response from run_as");
405 recvret.ret = -1;
406 recvret._errno = errno;
407 }
408 if (do_recv_fd(worker, cmd, &recvret.ret)) {
409 recvret.ret = -1;
410 recvret._errno = EIO;
411 }
412
413end:
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(enum run_as_cmd cmd, struct run_as_data *data, uid_t uid, gid_t gid)
446{
447 int ret;
448
449 if (use_clone()) {
450 DBG("Using run_as worker");
451 pthread_mutex_lock(&worker_lock);
452 assert(global_worker);
453 ret = run_as_cmd(global_worker, cmd, data, uid, gid);
454 pthread_mutex_unlock(&worker_lock);
455
456 } else {
457 DBG("Using run_as without worker");
458 ret = run_as_noworker(cmd, data, uid, gid);
459 }
460 return ret;
461}
462
463LTTNG_HIDDEN
464int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
465{
466 struct run_as_data data;
467
468 DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
469 path, (int) mode, (int) uid, (int) gid);
470 strncpy(data.u.mkdir.path, path, PATH_MAX - 1);
471 data.u.mkdir.path[PATH_MAX - 1] = '\0';
472 data.u.mkdir.mode = mode;
473 return run_as(RUN_AS_MKDIR_RECURSIVE, &data, uid, gid);
474}
475
476LTTNG_HIDDEN
477int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
478{
479 struct run_as_data data;
480
481 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
482 path, (int) mode, (int) uid, (int) 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(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_data data;
497
498 DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
499 path, flags, (int) mode, (int) uid, (int) gid);
500 strncpy(data.u.open.path, path, PATH_MAX - 1);
501 data.u.open.path[PATH_MAX - 1] = '\0';
502 data.u.open.flags = flags;
503 data.u.open.mode = mode;
504 return run_as(RUN_AS_OPEN, &data, uid, gid);
505}
506
507LTTNG_HIDDEN
508int run_as_unlink(const char *path, uid_t uid, gid_t gid)
509{
510 struct run_as_data data;
511
512 DBG3("unlink() %s with for uid %d and gid %d",
513 path, (int) uid, (int) gid);
514 strncpy(data.u.unlink.path, path, PATH_MAX - 1);
515 data.u.unlink.path[PATH_MAX - 1] = '\0';
516 return run_as(RUN_AS_UNLINK, &data, uid, gid);
517}
518
519LTTNG_HIDDEN
520int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid)
521{
522 struct run_as_data data;
523
524 DBG3("rmdir_recursive() %s with for uid %d and gid %d",
525 path, (int) uid, (int) gid);
526 strncpy(data.u.rmdir_recursive.path, path, PATH_MAX - 1);
527 data.u.rmdir_recursive.path[PATH_MAX - 1] = '\0';
528 return run_as(RUN_AS_RMDIR_RECURSIVE, &data, uid, gid);
529}
530
531static
532int reset_sighandler(void)
533{
534 int sig;
535
536 DBG("Resetting run_as worker signal handlers to default");
537 for (sig = 1; sig <= 31; sig++) {
538 (void) signal(sig, SIG_DFL);
539 }
540 return 0;
541}
542
543static
544void worker_sighandler(int sig)
545{
546 const char *signame;
547
548 /*
549 * The worker will its parent's signals since they are part of the same
550 * process group. However, in the case of SIGINT and SIGTERM, we want
551 * to give the worker a chance to teardown gracefully when its parent
552 * closes the command socket.
553 */
554 switch (sig) {
555 case SIGINT:
556 signame = "SIGINT";
557 break;
558 case SIGTERM:
559 signame = "SIGTERM";
560 break;
561 default:
562 signame = "Unknown";
563 }
564
565 DBG("run_as worker received signal %s", signame);
566}
567
568static
569int set_worker_sighandlers(void)
570{
571 int ret = 0;
572 sigset_t sigset;
573 struct sigaction sa;
574
575 if ((ret = sigemptyset(&sigset)) < 0) {
576 PERROR("sigemptyset");
577 goto end;
578 }
579
580 sa.sa_handler = worker_sighandler;
581 sa.sa_mask = sigset;
582 sa.sa_flags = 0;
583 if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) {
584 PERROR("sigaction SIGINT");
585 goto end;
586 }
587
588 if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
589 PERROR("sigaction SIGTERM");
590 goto end;
591 }
592
593 DBG("run_as signal handler set for SIGTERM and SIGINT");
594end:
595 return ret;
596}
597
598LTTNG_HIDDEN
599int run_as_create_worker(char *procname)
600{
601 pid_t pid;
602 int i, ret = 0;
603 ssize_t readlen;
604 struct run_as_ret recvret;
605 struct run_as_worker *worker;
606
607 pthread_mutex_lock(&worker_lock);
608 assert(!global_worker);
609 if (!use_clone()) {
610 /*
611 * Don't initialize a worker, all run_as tasks will be performed
612 * in the current process.
613 */
614 ret = 0;
615 goto end;
616 }
617 worker = zmalloc(sizeof(*worker));
618 if (!worker) {
619 ret = -ENOMEM;
620 goto end;
621 }
622 worker->procname = procname;
623 /* Create unix socket. */
624 if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
625 ret = -1;
626 goto error_sock;
627 }
628 /* Fork worker. */
629 pid = fork();
630 if (pid < 0) {
631 PERROR("fork");
632 ret = -1;
633 goto error_fork;
634 } else if (pid == 0) {
635 /* Child */
636
637 reset_sighandler();
638
639 set_worker_sighandlers();
640
641 /* The child has no use for this lock. */
642 pthread_mutex_unlock(&worker_lock);
643 /* Just close, no shutdown. */
644 if (close(worker->sockpair[0])) {
645 PERROR("close");
646 exit(EXIT_FAILURE);
647 }
648 worker->sockpair[0] = -1;
649 ret = run_as_worker(worker);
650 if (lttcomm_close_unix_sock(worker->sockpair[1])) {
651 PERROR("close");
652 ret = -1;
653 }
654 worker->sockpair[1] = -1;
655 LOG(ret ? PRINT_ERR : PRINT_DBG, "run_as worker exiting (ret = %d)", ret);
656 exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
657 } else {
658 /* Parent */
659
660 /* Just close, no shutdown. */
661 if (close(worker->sockpair[1])) {
662 PERROR("close");
663 ret = -1;
664 goto error_fork;
665 }
666 worker->sockpair[1] = -1;
667 worker->pid = pid;
668 /* Wait for worker to become ready. */
669 readlen = lttcomm_recv_unix_sock(worker->sockpair[0],
670 &recvret, sizeof(recvret));
671 if (readlen < sizeof(recvret)) {
672 ERR("readlen: %zd", readlen);
673 PERROR("Error reading response from run_as at creation");
674 ret = -1;
675 goto error_fork;
676 }
677 global_worker = worker;
678 }
679end:
680 pthread_mutex_unlock(&worker_lock);
681 return ret;
682
683 /* Error handling. */
684error_fork:
685 for (i = 0; i < 2; i++) {
686 if (worker->sockpair[i] < 0) {
687 continue;
688 }
689 if (lttcomm_close_unix_sock(worker->sockpair[i])) {
690 PERROR("close");
691 }
692 worker->sockpair[i] = -1;
693 }
694error_sock:
695 free(worker);
696 pthread_mutex_unlock(&worker_lock);
697 return ret;
698}
699
700LTTNG_HIDDEN
701void run_as_destroy_worker(void)
702{
703 struct run_as_worker *worker = global_worker;
704
705 DBG("Destroying run_as worker");
706 pthread_mutex_lock(&worker_lock);
707 if (!worker) {
708 goto end;
709 }
710 /* Close unix socket */
711 DBG("Closing run_as worker socket");
712 if (lttcomm_close_unix_sock(worker->sockpair[0])) {
713 PERROR("close");
714 }
715 worker->sockpair[0] = -1;
716 /* Wait for worker. */
717 for (;;) {
718 int status;
719 pid_t wait_ret;
720
721 wait_ret = waitpid(worker->pid, &status, 0);
722 if (wait_ret < 0) {
723 if (errno == EINTR) {
724 continue;
725 }
726 PERROR("waitpid");
727 break;
728 }
729
730 if (WIFEXITED(status)) {
731 LOG(WEXITSTATUS(status) == 0 ? PRINT_DBG : PRINT_ERR,
732 DEFAULT_RUN_AS_WORKER_NAME " terminated with status code %d",
733 WEXITSTATUS(status));
734 break;
735 } else if (WIFSIGNALED(status)) {
736 ERR(DEFAULT_RUN_AS_WORKER_NAME " was killed by signal %d",
737 WTERMSIG(status));
738 break;
739 }
740 }
741 free(worker);
742 global_worker = NULL;
743end:
744 pthread_mutex_unlock(&worker_lock);
745}
This page took 0.027722 seconds and 5 git commands to generate.