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