2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
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.
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
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.
26 #include <sys/types.h>
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>
45 typedef int (*run_as_fct
)(struct run_as_data
*data
);
47 struct run_as_mkdir_data
{
52 struct run_as_open_data
{
58 struct run_as_unlink_data
{
62 struct run_as_rmdir_recursive_data
{
70 RUN_AS_RMDIR_RECURSIVE
,
71 RUN_AS_MKDIR_RECURSIVE
,
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
;
91 struct run_as_worker
{
92 pid_t pid
; /* Worker PID. */
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
;
112 return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
117 int _utils_mkdir_recursive_unsafe(const char *path
, mode_t mode
);
120 * Create recursively directory using the FULL path.
123 int _mkdir_recursive(struct run_as_data
*data
)
128 path
= data
->u
.mkdir
.path
;
129 mode
= data
->u
.mkdir
.mode
;
131 /* Safe to call as we have transitioned to the requested uid/gid. */
132 return _utils_mkdir_recursive_unsafe(path
, mode
);
136 int _mkdir(struct run_as_data
*data
)
138 return mkdir(data
->u
.mkdir
.path
, data
->u
.mkdir
.mode
);
142 int _open(struct run_as_data
*data
)
144 return open(data
->u
.open
.path
, data
->u
.open
.flags
, data
->u
.open
.mode
);
148 int _unlink(struct run_as_data
*data
)
150 return unlink(data
->u
.unlink
.path
);
154 int _rmdir_recursive(struct run_as_data
*data
)
156 return utils_recursive_rmdir(data
->u
.rmdir_recursive
.path
);
160 run_as_fct
run_as_enum_to_fct(enum run_as_cmd cmd
)
169 case RUN_AS_RMDIR_RECURSIVE
:
170 return _rmdir_recursive
;
171 case RUN_AS_MKDIR_RECURSIVE
:
172 return _mkdir_recursive
;
174 ERR("Unknown command %d", (int) cmd
);
180 int do_send_fd(struct run_as_worker
*worker
,
181 enum run_as_cmd cmd
, int fd
)
194 len
= lttcomm_send_fds_unix_sock(worker
->sockpair
[1], &fd
, 1);
196 PERROR("lttcomm_send_fds_unix_sock");
207 int do_recv_fd(struct run_as_worker
*worker
,
208 enum run_as_cmd cmd
, int *fd
)
221 len
= lttcomm_recv_fds_unix_sock(worker
->sockpair
[0], fd
, 1);
224 } else if (len
< 0) {
225 PERROR("lttcomm_recv_fds_unix_sock");
232 * Return < 0 on error, 0 if OK, 1 on hangup.
235 int handle_one_cmd(struct run_as_worker
*worker
)
238 struct run_as_data data
;
239 ssize_t readlen
, writelen
;
240 struct run_as_ret sendret
;
245 readlen
= lttcomm_recv_unix_sock(worker
->sockpair
[1], &data
,
252 if (readlen
< sizeof(data
)) {
253 PERROR("lttcomm_recv_unix_sock error");
258 cmd
= run_as_enum_to_fct(data
.cmd
);
264 prev_euid
= getuid();
265 if (data
.gid
!= getegid()) {
266 ret
= setegid(data
.gid
);
272 if (data
.uid
!= prev_euid
) {
273 ret
= seteuid(data
.uid
);
280 * Also set umask to 0 for mkdir executable bit.
287 sendret
._errno
= errno
;
288 /* send back return value */
289 writelen
= lttcomm_send_unix_sock(worker
->sockpair
[1], &sendret
,
291 if (writelen
< sizeof(sendret
)) {
292 PERROR("lttcomm_send_unix_sock error");
296 ret
= do_send_fd(worker
, data
.cmd
, ret
);
298 PERROR("do_send_fd error");
302 if (seteuid(prev_euid
) < 0) {
313 int run_as_worker(struct run_as_worker
*worker
)
317 struct run_as_ret sendret
;
318 size_t proc_orig_len
;
321 * Initialize worker. Set a different process cmdline.
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
);
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");
337 writelen
= lttcomm_send_unix_sock(worker
->sockpair
[1], &sendret
,
339 if (writelen
< sizeof(sendret
)) {
340 PERROR("lttcomm_send_unix_sock error");
346 ret
= handle_one_cmd(worker
);
350 } else if (ret
> 0) {
353 continue; /* Next command. */
362 int run_as_cmd(struct run_as_worker
*worker
,
364 struct run_as_data
*data
,
365 uid_t uid
, gid_t gid
)
367 ssize_t readlen
, writelen
;
368 struct run_as_ret recvret
;
371 * If we are non-root, we can only deal with our own uid.
373 if (geteuid() != 0) {
374 if (uid
!= geteuid()) {
376 recvret
._errno
= EPERM
;
377 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
378 (int) uid
, (int) geteuid());
387 writelen
= lttcomm_send_unix_sock(worker
->sockpair
[0], data
,
389 if (writelen
< sizeof(*data
)) {
390 PERROR("Error writing message to run_as");
392 recvret
._errno
= errno
;
396 /* receive return value */
397 readlen
= lttcomm_recv_unix_sock(worker
->sockpair
[0], &recvret
,
400 ERR("Run-as worker has hung-up during run_as_cmd");
402 recvret
._errno
= EIO
;
404 } else if (readlen
< sizeof(recvret
)) {
405 PERROR("Error reading response from run_as");
407 recvret
._errno
= errno
;
409 if (do_recv_fd(worker
, cmd
, &recvret
.ret
)) {
411 recvret
._errno
= EIO
;
415 errno
= recvret
._errno
;
420 * This is for debugging ONLY and should not be considered secure.
423 int run_as_noworker(enum run_as_cmd cmd
,
424 struct run_as_data
*data
, uid_t uid
, gid_t gid
)
426 int ret
, saved_errno
;
430 fct
= run_as_enum_to_fct(cmd
);
446 int run_as(enum run_as_cmd cmd
, struct run_as_data
*data
, uid_t uid
, gid_t gid
)
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
);
458 DBG("Using run_as without worker");
459 ret
= run_as_noworker(cmd
, data
, uid
, gid
);
465 int run_as_mkdir_recursive(const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
467 struct run_as_data data
;
469 memset(&data
, 0, sizeof(data
));
470 DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
471 path
, (int) mode
, (int) uid
, (int) gid
);
472 strncpy(data
.u
.mkdir
.path
, path
, PATH_MAX
- 1);
473 data
.u
.mkdir
.path
[PATH_MAX
- 1] = '\0';
474 data
.u
.mkdir
.mode
= mode
;
475 return run_as(RUN_AS_MKDIR_RECURSIVE
, &data
, uid
, gid
);
479 int run_as_mkdir(const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
481 struct run_as_data data
;
483 memset(&data
, 0, sizeof(data
));
484 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
485 path
, (int) mode
, (int) uid
, (int) gid
);
486 strncpy(data
.u
.mkdir
.path
, path
, PATH_MAX
- 1);
487 data
.u
.mkdir
.path
[PATH_MAX
- 1] = '\0';
488 data
.u
.mkdir
.mode
= mode
;
489 return run_as(RUN_AS_MKDIR
, &data
, uid
, gid
);
493 int run_as_open(const char *path
, int flags
, mode_t mode
, uid_t uid
, gid_t gid
)
495 struct run_as_data data
;
497 memset(&data
, 0, sizeof(data
));
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
);
508 int run_as_unlink(const char *path
, uid_t uid
, gid_t gid
)
510 struct run_as_data data
;
512 memset(&data
, 0, sizeof(data
));
513 DBG3("unlink() %s with for uid %d and gid %d",
514 path
, (int) uid
, (int) 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
);
521 int run_as_rmdir_recursive(const char *path
, uid_t uid
, gid_t gid
)
523 struct run_as_data data
;
525 DBG3("rmdir_recursive() %s with for uid %d and gid %d",
526 path
, (int) uid
, (int) 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
);
533 int reset_sighandler(void)
537 DBG("Resetting run_as worker signal handlers to default");
538 for (sig
= 1; sig
<= 31; sig
++) {
539 (void) signal(sig
, SIG_DFL
);
545 void worker_sighandler(int sig
)
550 * The worker will inherit its parent's signals since they are part of
551 * the same process group. However, in the case of SIGINT and SIGTERM,
552 * we want to give the worker a chance to teardown gracefully when its
553 * parent closes the command socket.
567 DBG("run_as worker received signal %s", signame
);
569 DBG("run_as_worker received signal %d", sig
);
574 int set_worker_sighandlers(void)
580 if ((ret
= sigemptyset(&sigset
)) < 0) {
581 PERROR("sigemptyset");
585 sa
.sa_handler
= worker_sighandler
;
588 if ((ret
= sigaction(SIGINT
, &sa
, NULL
)) < 0) {
589 PERROR("sigaction SIGINT");
593 if ((ret
= sigaction(SIGTERM
, &sa
, NULL
)) < 0) {
594 PERROR("sigaction SIGTERM");
598 DBG("run_as signal handler set for SIGTERM and SIGINT");
604 int run_as_create_worker(char *procname
)
609 struct run_as_ret recvret
;
610 struct run_as_worker
*worker
;
612 pthread_mutex_lock(&worker_lock
);
613 assert(!global_worker
);
616 * Don't initialize a worker, all run_as tasks will be performed
617 * in the current process.
622 worker
= zmalloc(sizeof(*worker
));
627 worker
->procname
= procname
;
628 /* Create unix socket. */
629 if (lttcomm_create_anon_unix_socketpair(worker
->sockpair
) < 0) {
639 } else if (pid
== 0) {
644 set_worker_sighandlers();
646 /* The child has no use for this lock. */
647 pthread_mutex_unlock(&worker_lock
);
648 /* Just close, no shutdown. */
649 if (close(worker
->sockpair
[0])) {
653 worker
->sockpair
[0] = -1;
654 ret
= run_as_worker(worker
);
655 if (lttcomm_close_unix_sock(worker
->sockpair
[1])) {
659 worker
->sockpair
[1] = -1;
660 LOG(ret
? PRINT_ERR
: PRINT_DBG
, "run_as worker exiting (ret = %d)", ret
);
661 exit(ret
? EXIT_FAILURE
: EXIT_SUCCESS
);
665 /* Just close, no shutdown. */
666 if (close(worker
->sockpair
[1])) {
671 worker
->sockpair
[1] = -1;
673 /* Wait for worker to become ready. */
674 readlen
= lttcomm_recv_unix_sock(worker
->sockpair
[0],
675 &recvret
, sizeof(recvret
));
676 if (readlen
< sizeof(recvret
)) {
677 ERR("readlen: %zd", readlen
);
678 PERROR("Error reading response from run_as at creation");
682 global_worker
= worker
;
685 pthread_mutex_unlock(&worker_lock
);
688 /* Error handling. */
690 for (i
= 0; i
< 2; i
++) {
691 if (worker
->sockpair
[i
] < 0) {
694 if (lttcomm_close_unix_sock(worker
->sockpair
[i
])) {
697 worker
->sockpair
[i
] = -1;
701 pthread_mutex_unlock(&worker_lock
);
706 void run_as_destroy_worker(void)
708 struct run_as_worker
*worker
= global_worker
;
710 DBG("Destroying run_as worker");
711 pthread_mutex_lock(&worker_lock
);
715 /* Close unix socket */
716 DBG("Closing run_as worker socket");
717 if (lttcomm_close_unix_sock(worker
->sockpair
[0])) {
720 worker
->sockpair
[0] = -1;
721 /* Wait for worker. */
726 wait_ret
= waitpid(worker
->pid
, &status
, 0);
728 if (errno
== EINTR
) {
735 if (WIFEXITED(status
)) {
736 LOG(WEXITSTATUS(status
) == 0 ? PRINT_DBG
: PRINT_ERR
,
737 DEFAULT_RUN_AS_WORKER_NAME
" terminated with status code %d",
738 WEXITSTATUS(status
));
740 } else if (WIFSIGNALED(status
)) {
741 ERR(DEFAULT_RUN_AS_WORKER_NAME
" was killed by signal %d",
747 global_worker
= NULL
;
749 pthread_mutex_unlock(&worker_lock
);