Cleanup: uid and gid are never used by run_as_noworker()
[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 <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/unix.h>
40#include <common/defaults.h>
41
42#include "runas.h"
43
44struct run_as_data;
45typedef int (*run_as_fct)(struct run_as_data *data);
46
47struct run_as_mkdir_data {
48 char path[PATH_MAX];
49 mode_t mode;
50};
51
52struct run_as_open_data {
53 char path[PATH_MAX];
54 int flags;
55 mode_t mode;
56};
57
58struct run_as_unlink_data {
59 char path[PATH_MAX];
60};
61
62struct run_as_rmdir_recursive_data {
63 char path[PATH_MAX];
64};
65
66enum 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
74struct 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
86struct run_as_ret {
87 int ret;
88 int _errno;
89};
90
91struct 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). */
98static struct run_as_worker *global_worker;
99/* Lock protecting the worker. */
100static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
101
102#ifdef VALGRIND
103static
104int use_clone(void)
105{
106 return 0;
107}
108#else
109static
110int use_clone(void)
111{
112 return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
113}
114#endif
115
116LTTNG_HIDDEN
117int _utils_mkdir_recursive_unsafe(const char *path, mode_t mode);
118
119/*
120 * Create recursively directory using the FULL path.
121 */
122static
123int _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
135static
136int _mkdir(struct run_as_data *data)
137{
138 return mkdir(data->u.mkdir.path, data->u.mkdir.mode);
139}
140
141static
142int _open(struct run_as_data *data)
143{
144 return open(data->u.open.path, data->u.open.flags, data->u.open.mode);
145}
146
147static
148int _unlink(struct run_as_data *data)
149{
150 return unlink(data->u.unlink.path);
151}
152
153static
154int _rmdir_recursive(struct run_as_data *data)
155{
156 return utils_recursive_rmdir(data->u.rmdir_recursive.path);
157}
158
159static
160run_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
179static
180int 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
206static
207int 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 */
234static
235int 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
285write_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;
308end:
309 return ret;
310}
311
312static
313int 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 }
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, struct run_as_data *data)
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;
439end:
440 return ret;
441}
442
443static
444int 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);
458 }
459 return ret;
460}
461
462LTTNG_HIDDEN
463int 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 memset(&data, 0, sizeof(data));
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 memset(&data, 0, sizeof(data));
482 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
483 path, (int) mode, (int) uid, (int) 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
490LTTNG_HIDDEN
491int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
492{
493 struct run_as_data data;
494
495 memset(&data, 0, sizeof(data));
496 DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
497 path, flags, (int) mode, (int) uid, (int) gid);
498 strncpy(data.u.open.path, path, PATH_MAX - 1);
499 data.u.open.path[PATH_MAX - 1] = '\0';
500 data.u.open.flags = flags;
501 data.u.open.mode = mode;
502 return run_as(RUN_AS_OPEN, &data, uid, gid);
503}
504
505LTTNG_HIDDEN
506int run_as_unlink(const char *path, uid_t uid, gid_t gid)
507{
508 struct run_as_data data;
509
510 memset(&data, 0, sizeof(data));
511 DBG3("unlink() %s with for uid %d and gid %d",
512 path, (int) uid, (int) 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
518LTTNG_HIDDEN
519int 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, (int) uid, (int) 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
530static
531int 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 return 0;
540}
541
542static
543void worker_sighandler(int sig)
544{
545 const char *signame;
546
547 /*
548 * The worker will inherit its parent's signals since they are part of
549 * the same process group. However, in the case of SIGINT and SIGTERM,
550 * we want to give the worker a chance to teardown gracefully when its
551 * parent closes the command socket.
552 */
553 switch (sig) {
554 case SIGINT:
555 signame = "SIGINT";
556 break;
557 case SIGTERM:
558 signame = "SIGTERM";
559 break;
560 default:
561 signame = NULL;
562 }
563
564 if (signame) {
565 DBG("run_as worker received signal %s", signame);
566 } else {
567 DBG("run_as_worker received signal %d", sig);
568 }
569}
570
571static
572int set_worker_sighandlers(void)
573{
574 int ret = 0;
575 sigset_t sigset;
576 struct sigaction sa;
577
578 if ((ret = sigemptyset(&sigset)) < 0) {
579 PERROR("sigemptyset");
580 goto end;
581 }
582
583 sa.sa_handler = worker_sighandler;
584 sa.sa_mask = sigset;
585 sa.sa_flags = 0;
586 if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) {
587 PERROR("sigaction SIGINT");
588 goto end;
589 }
590
591 if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
592 PERROR("sigaction SIGTERM");
593 goto end;
594 }
595
596 DBG("run_as signal handler set for SIGTERM and SIGINT");
597end:
598 return ret;
599}
600
601LTTNG_HIDDEN
602int run_as_create_worker(char *procname)
603{
604 pid_t pid;
605 int i, ret = 0;
606 ssize_t readlen;
607 struct run_as_ret recvret;
608 struct run_as_worker *worker;
609
610 pthread_mutex_lock(&worker_lock);
611 assert(!global_worker);
612 if (!use_clone()) {
613 /*
614 * Don't initialize a worker, all run_as tasks will be performed
615 * in the current process.
616 */
617 ret = 0;
618 goto end;
619 }
620 worker = zmalloc(sizeof(*worker));
621 if (!worker) {
622 ret = -ENOMEM;
623 goto end;
624 }
625 worker->procname = procname;
626 /* Create unix socket. */
627 if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
628 ret = -1;
629 goto error_sock;
630 }
631 /* Fork worker. */
632 pid = fork();
633 if (pid < 0) {
634 PERROR("fork");
635 ret = -1;
636 goto error_fork;
637 } else if (pid == 0) {
638 /* Child */
639
640 reset_sighandler();
641
642 set_worker_sighandlers();
643
644 /* The child has no use for this lock. */
645 pthread_mutex_unlock(&worker_lock);
646 /* Just close, no shutdown. */
647 if (close(worker->sockpair[0])) {
648 PERROR("close");
649 exit(EXIT_FAILURE);
650 }
651 worker->sockpair[0] = -1;
652 ret = run_as_worker(worker);
653 if (lttcomm_close_unix_sock(worker->sockpair[1])) {
654 PERROR("close");
655 ret = -1;
656 }
657 worker->sockpair[1] = -1;
658 LOG(ret ? PRINT_ERR : PRINT_DBG, "run_as worker exiting (ret = %d)", ret);
659 exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
660 } else {
661 /* Parent */
662
663 /* Just close, no shutdown. */
664 if (close(worker->sockpair[1])) {
665 PERROR("close");
666 ret = -1;
667 goto error_fork;
668 }
669 worker->sockpair[1] = -1;
670 worker->pid = pid;
671 /* Wait for worker to become ready. */
672 readlen = lttcomm_recv_unix_sock(worker->sockpair[0],
673 &recvret, sizeof(recvret));
674 if (readlen < sizeof(recvret)) {
675 ERR("readlen: %zd", readlen);
676 PERROR("Error reading response from run_as at creation");
677 ret = -1;
678 goto error_fork;
679 }
680 global_worker = worker;
681 }
682end:
683 pthread_mutex_unlock(&worker_lock);
684 return ret;
685
686 /* Error handling. */
687error_fork:
688 for (i = 0; i < 2; i++) {
689 if (worker->sockpair[i] < 0) {
690 continue;
691 }
692 if (lttcomm_close_unix_sock(worker->sockpair[i])) {
693 PERROR("close");
694 }
695 worker->sockpair[i] = -1;
696 }
697error_sock:
698 free(worker);
699 pthread_mutex_unlock(&worker_lock);
700 return ret;
701}
702
703LTTNG_HIDDEN
704void run_as_destroy_worker(void)
705{
706 struct run_as_worker *worker = global_worker;
707
708 DBG("Destroying run_as worker");
709 pthread_mutex_lock(&worker_lock);
710 if (!worker) {
711 goto end;
712 }
713 /* Close unix socket */
714 DBG("Closing run_as worker socket");
715 if (lttcomm_close_unix_sock(worker->sockpair[0])) {
716 PERROR("close");
717 }
718 worker->sockpair[0] = -1;
719 /* Wait for worker. */
720 for (;;) {
721 int status;
722 pid_t wait_ret;
723
724 wait_ret = waitpid(worker->pid, &status, 0);
725 if (wait_ret < 0) {
726 if (errno == EINTR) {
727 continue;
728 }
729 PERROR("waitpid");
730 break;
731 }
732
733 if (WIFEXITED(status)) {
734 LOG(WEXITSTATUS(status) == 0 ? PRINT_DBG : PRINT_ERR,
735 DEFAULT_RUN_AS_WORKER_NAME " terminated with status code %d",
736 WEXITSTATUS(status));
737 break;
738 } else if (WIFSIGNALED(status)) {
739 ERR(DEFAULT_RUN_AS_WORKER_NAME " was killed by signal %d",
740 WTERMSIG(status));
741 break;
742 }
743 }
744 free(worker);
745 global_worker = NULL;
746end:
747 pthread_mutex_unlock(&worker_lock);
748}
This page took 0.025367 seconds and 4 git commands to generate.