Port: Don't use SIGUNUSED which is not defined on Solaris
[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 #include <assert.h>
34 #include <sys/prctl.h>
35
36 #include <common/common.h>
37 #include <common/utils.h>
38 #include <common/compat/getenv.h>
39 #include <common/sessiond-comm/unix.h>
40
41 #include "runas.h"
42
43 struct run_as_data;
44 typedef int (*run_as_fct)(struct run_as_data *data);
45
46 struct run_as_mkdir_data {
47 char path[PATH_MAX];
48 mode_t mode;
49 };
50
51 struct run_as_open_data {
52 char path[PATH_MAX];
53 int flags;
54 mode_t mode;
55 };
56
57 struct run_as_unlink_data {
58 char path[PATH_MAX];
59 };
60
61 struct run_as_rmdir_recursive_data {
62 char path[PATH_MAX];
63 };
64
65 enum 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
73 struct 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
85 struct run_as_ret {
86 int ret;
87 int _errno;
88 };
89
90 struct 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). */
97 static struct run_as_worker *global_worker;
98 /* Lock protecting the worker. */
99 static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
100
101 #ifdef VALGRIND
102 static
103 int use_clone(void)
104 {
105 return 0;
106 }
107 #else
108 static
109 int use_clone(void)
110 {
111 return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
112 }
113 #endif
114
115 LTTNG_HIDDEN
116 int _utils_mkdir_recursive_unsafe(const char *path, mode_t mode);
117
118 /*
119 * Create recursively directory using the FULL path.
120 */
121 static
122 int _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
134 static
135 int _mkdir(struct run_as_data *data)
136 {
137 return mkdir(data->u.mkdir.path, data->u.mkdir.mode);
138 }
139
140 static
141 int _open(struct run_as_data *data)
142 {
143 return open(data->u.open.path, data->u.open.flags, data->u.open.mode);
144 }
145
146 static
147 int _unlink(struct run_as_data *data)
148 {
149 return unlink(data->u.unlink.path);
150 }
151
152 static
153 int _rmdir_recursive(struct run_as_data *data)
154 {
155 return utils_recursive_rmdir(data->u.rmdir_recursive.path);
156 }
157
158 static
159 run_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
178 static
179 int 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
205 static
206 int 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 */
233 static
234 int 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
284 write_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;
307 end:
308 return ret;
309 }
310
311 static
312 int 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 = prctl(PR_SET_NAME, DEFAULT_RUN_AS_WORKER_NAME, 0, 0, 0);
327 if (ret) {
328 /* Don't fail as this is not essential. */
329 PERROR("prctl PR_SET_NAME");
330 ret = 0;
331 }
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;
355 end:
356 return ret;
357 }
358
359 static
360 int 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)
364 {
365 ssize_t readlen, writelen;
366 struct run_as_ret recvret;
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()) {
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
412 end:
413 errno = recvret._errno;
414 return recvret.ret;
415 }
416
417 /*
418 * This is for debugging ONLY and should not be considered secure.
419 */
420 static
421 int run_as_noworker(enum run_as_cmd cmd,
422 struct run_as_data *data, uid_t uid, gid_t gid)
423 {
424 int ret, saved_errno;
425 mode_t old_mask;
426 run_as_fct fct;
427
428 fct = run_as_enum_to_fct(cmd);
429 if (!fct) {
430 errno = -ENOSYS;
431 ret = -1;
432 goto end;
433 }
434 old_mask = umask(0);
435 ret = fct(data);
436 saved_errno = errno;
437 umask(old_mask);
438 errno = saved_errno;
439 end:
440 return ret;
441 }
442
443 static
444 int run_as(enum run_as_cmd cmd, struct run_as_data *data, uid_t uid, gid_t gid)
445 {
446 int ret;
447
448 if (use_clone()) {
449 DBG("Using run_as worker");
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
455 } else {
456 DBG("Using run_as without worker");
457 ret = run_as_noworker(cmd, data, uid, gid);
458 }
459 return ret;
460 }
461
462 LTTNG_HIDDEN
463 int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
464 {
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(RUN_AS_MKDIR_RECURSIVE, &data, uid, gid);
473 }
474
475 LTTNG_HIDDEN
476 int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
477 {
478 struct run_as_data data;
479
480 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
481 path, mode, uid, gid);
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;
485 return run_as(RUN_AS_MKDIR, &data, uid, gid);
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 */
492 LTTNG_HIDDEN
493 int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
494 {
495 struct run_as_data data;
496
497 DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
498 path, flags, mode, uid, gid);
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;
503 return run_as(RUN_AS_OPEN, &data, uid, gid);
504 }
505
506 LTTNG_HIDDEN
507 int run_as_unlink(const char *path, uid_t uid, gid_t gid)
508 {
509 struct run_as_data data;
510
511 DBG3("unlink() %s with for uid %d and gid %d",
512 path, uid, gid);
513 strncpy(data.u.unlink.path, path, PATH_MAX - 1);
514 data.u.unlink.path[PATH_MAX - 1] = '\0';
515 return run_as(RUN_AS_UNLINK, &data, uid, gid);
516 }
517
518 LTTNG_HIDDEN
519 int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid)
520 {
521 struct run_as_data data;
522
523 DBG3("rmdir_recursive() %s with for uid %d and gid %d",
524 path, uid, gid);
525 strncpy(data.u.rmdir_recursive.path, path, PATH_MAX - 1);
526 data.u.rmdir_recursive.path[PATH_MAX - 1] = '\0';
527 return run_as(RUN_AS_RMDIR_RECURSIVE, &data, uid, gid);
528 }
529
530 static
531 void reset_sighandler(void)
532 {
533 int sig;
534
535 DBG("Resetting run_as worker signal handlers to default");
536 for (sig = 1; sig <= 31; sig++) {
537 (void) signal(sig, SIG_DFL);
538 }
539 }
540
541 static
542 void worker_sighandler(int sig)
543 {
544 const char *signame;
545
546 /*
547 * The worker will its parent's signals since they are part of the same
548 * process group. However, in the case of SIGINT and SIGTERM, we want
549 * to give the worker a chance to teardown gracefully when its parent
550 * closes the command socket.
551 */
552 switch (sig) {
553 case SIGINT:
554 signame = "SIGINT";
555 break;
556 case SIGTERM:
557 signame = "SIGTERM";
558 break;
559 default:
560 signame = "Unknown";
561 }
562
563 DBG("run_as worker received signal %s", signame);
564 }
565
566 static
567 int set_worker_sighandlers(void)
568 {
569 int ret = 0;
570 sigset_t sigset;
571 struct sigaction sa;
572
573 if ((ret = sigemptyset(&sigset)) < 0) {
574 PERROR("sigemptyset");
575 goto end;
576 }
577
578 sa.sa_handler = worker_sighandler;
579 sa.sa_mask = sigset;
580 sa.sa_flags = 0;
581 if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) {
582 PERROR("sigaction SIGINT");
583 goto end;
584 }
585
586 if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
587 PERROR("sigaction SIGTERM");
588 goto end;
589 }
590
591 DBG("run_as signal handler set for SIGTERM and SIGINT");
592 end:
593 return ret;
594 }
595
596 LTTNG_HIDDEN
597 int run_as_create_worker(char *procname)
598 {
599 pid_t pid;
600 int i, ret = 0;
601 ssize_t readlen;
602 struct run_as_ret recvret;
603 struct run_as_worker *worker;
604
605 pthread_mutex_lock(&worker_lock);
606 assert(!global_worker);
607 if (!use_clone()) {
608 /*
609 * Don't initialize a worker, all run_as tasks will be performed
610 * in the current process.
611 */
612 ret = 0;
613 goto end;
614 }
615 worker = zmalloc(sizeof(*worker));
616 if (!worker) {
617 ret = -ENOMEM;
618 goto end;
619 }
620 worker->procname = procname;
621 /* Create unix socket. */
622 if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
623 ret = -1;
624 goto error_sock;
625 }
626 /* Fork worker. */
627 pid = fork();
628 if (pid < 0) {
629 PERROR("fork");
630 ret = -1;
631 goto error_fork;
632 } else if (pid == 0) {
633 /* Child */
634
635 reset_sighandler();
636
637 set_worker_sighandlers();
638
639 /* The child has no use for this lock. */
640 pthread_mutex_unlock(&worker_lock);
641 /* Just close, no shutdown. */
642 if (close(worker->sockpair[0])) {
643 PERROR("close");
644 exit(EXIT_FAILURE);
645 }
646 worker->sockpair[0] = -1;
647 ret = run_as_worker(worker);
648 if (lttcomm_close_unix_sock(worker->sockpair[1])) {
649 PERROR("close");
650 ret = -1;
651 }
652 worker->sockpair[1] = -1;
653 exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
654 } else {
655 /* Parent */
656
657 /* Just close, no shutdown. */
658 if (close(worker->sockpair[1])) {
659 PERROR("close");
660 ret = -1;
661 goto error_fork;
662 }
663 worker->sockpair[1] = -1;
664 worker->pid = pid;
665 /* Wait for worker to become ready. */
666 readlen = lttcomm_recv_unix_sock(worker->sockpair[0],
667 &recvret, sizeof(recvret));
668 if (readlen < sizeof(recvret)) {
669 ERR("readlen: %zd", readlen);
670 PERROR("Error reading response from run_as at creation");
671 ret = -1;
672 goto error_fork;
673 }
674 global_worker = worker;
675 }
676 end:
677 pthread_mutex_unlock(&worker_lock);
678 return ret;
679
680 /* Error handling. */
681 error_fork:
682 for (i = 0; i < 2; i++) {
683 if (worker->sockpair[i] < 0) {
684 continue;
685 }
686 if (lttcomm_close_unix_sock(worker->sockpair[i])) {
687 PERROR("close");
688 }
689 worker->sockpair[i] = -1;
690 }
691 error_sock:
692 free(worker);
693 pthread_mutex_unlock(&worker_lock);
694 return ret;
695 }
696
697 LTTNG_HIDDEN
698 void run_as_destroy_worker(void)
699 {
700 struct run_as_worker *worker = global_worker;
701
702 pthread_mutex_lock(&worker_lock);
703 if (!worker) {
704 goto end;
705 }
706 /* Close unix socket */
707 if (lttcomm_close_unix_sock(worker->sockpair[0])) {
708 PERROR("close");
709 }
710 worker->sockpair[0] = -1;
711 /* Wait for worker. */
712 for (;;) {
713 int status;
714 pid_t wait_ret;
715
716 wait_ret = waitpid(worker->pid, &status, 0);
717 if (wait_ret < 0) {
718 if (errno == EINTR) {
719 continue;
720 }
721 PERROR("waitpid");
722 break;
723 }
724
725 if (WIFEXITED(status)) {
726 LOG(WEXITSTATUS(status) == 0 ? PRINT_DBG : PRINT_ERR,
727 DEFAULT_RUN_AS_WORKER_NAME " terminated with status code %d",
728 WEXITSTATUS(status));
729 break;
730 } else if (WIFSIGNALED(status)) {
731 ERR(DEFAULT_RUN_AS_WORKER_NAME " was killed by signal %d",
732 WTERMSIG(status));
733 break;
734 }
735 }
736 free(worker);
737 global_worker = NULL;
738 end:
739 pthread_mutex_unlock(&worker_lock);
740 }
This page took 0.043079 seconds and 4 git commands to generate.