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.
27 #include <sys/types.h>
32 #include <sys/signal.h>
35 #include <common/common.h>
36 #include <common/utils.h>
37 #include <common/compat/getenv.h>
38 #include <common/sessiond-comm/unix.h>
43 typedef int (*run_as_fct
)(struct run_as_data
*data
);
45 struct run_as_mkdir_data
{
50 struct run_as_open_data
{
56 struct run_as_unlink_data
{
60 struct run_as_rmdir_recursive_data
{
68 RUN_AS_RMDIR_RECURSIVE
,
69 RUN_AS_MKDIR_RECURSIVE
,
75 struct run_as_mkdir_data mkdir
;
76 struct run_as_open_data open
;
77 struct run_as_unlink_data unlink
;
78 struct run_as_rmdir_recursive_data rmdir_recursive
;
89 struct run_as_worker
{
90 pid_t pid
; /* Worker PID. */
95 /* Single global worker per process (for now). */
96 static struct run_as_worker
*global_worker
;
97 /* Lock protecting the worker. */
98 static pthread_mutex_t worker_lock
= PTHREAD_MUTEX_INITIALIZER
;
110 return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
115 int _utils_mkdir_recursive_unsafe(const char *path
, mode_t mode
);
118 * Create recursively directory using the FULL path.
121 int _mkdir_recursive(struct run_as_data
*data
)
126 path
= data
->u
.mkdir
.path
;
127 mode
= data
->u
.mkdir
.mode
;
129 /* Safe to call as we have transitioned to the requested uid/gid. */
130 return _utils_mkdir_recursive_unsafe(path
, mode
);
134 int _mkdir(struct run_as_data
*data
)
136 return mkdir(data
->u
.mkdir
.path
, data
->u
.mkdir
.mode
);
140 int _open(struct run_as_data
*data
)
142 return open(data
->u
.open
.path
, data
->u
.open
.flags
, data
->u
.open
.mode
);
146 int _unlink(struct run_as_data
*data
)
148 return unlink(data
->u
.unlink
.path
);
152 int _rmdir_recursive(struct run_as_data
*data
)
154 return utils_recursive_rmdir(data
->u
.rmdir_recursive
.path
);
158 run_as_fct
run_as_enum_to_fct(enum run_as_cmd cmd
)
167 case RUN_AS_RMDIR_RECURSIVE
:
168 return _rmdir_recursive
;
169 case RUN_AS_MKDIR_RECURSIVE
:
170 return _mkdir_recursive
;
172 ERR("Unknown command %d", (int) cmd
)
178 int do_send_fd(struct run_as_worker
*worker
,
179 enum run_as_cmd cmd
, int fd
)
192 len
= lttcomm_send_fds_unix_sock(worker
->sockpair
[1], &fd
, 1);
194 PERROR("lttcomm_send_fds_unix_sock");
205 int do_recv_fd(struct run_as_worker
*worker
,
206 enum run_as_cmd cmd
, int *fd
)
219 len
= lttcomm_recv_fds_unix_sock(worker
->sockpair
[0], fd
, 1);
222 } else if (len
< 0) {
223 PERROR("lttcomm_recv_fds_unix_sock");
230 * Return < 0 on error, 0 if OK, 1 on hangup.
233 int handle_one_cmd(struct run_as_worker
*worker
)
236 struct run_as_data data
;
237 ssize_t readlen
, writelen
;
238 struct run_as_ret sendret
;
243 readlen
= lttcomm_recv_unix_sock(worker
->sockpair
[1], &data
,
250 if (readlen
< sizeof(data
)) {
251 PERROR("lttcomm_recv_unix_sock error");
256 cmd
= run_as_enum_to_fct(data
.cmd
);
262 prev_euid
= getuid();
263 if (data
.gid
!= getegid()) {
264 ret
= setegid(data
.gid
);
270 if (data
.uid
!= prev_euid
) {
271 ret
= seteuid(data
.uid
);
278 * Also set umask to 0 for mkdir executable bit.
285 sendret
._errno
= errno
;
286 /* send back return value */
287 writelen
= lttcomm_send_unix_sock(worker
->sockpair
[1], &sendret
,
289 if (writelen
< sizeof(sendret
)) {
290 PERROR("lttcomm_send_unix_sock error");
294 ret
= do_send_fd(worker
, data
.cmd
, ret
);
296 PERROR("do_send_fd error");
300 if (seteuid(prev_euid
) < 0) {
311 int run_as_worker(struct run_as_worker
*worker
)
315 struct run_as_ret sendret
;
316 size_t proc_orig_len
;
319 * Initialize worker. Set a different process cmdline.
321 proc_orig_len
= strlen(worker
->procname
);
322 memset(worker
->procname
, 0, proc_orig_len
);
323 strncpy(worker
->procname
, DEFAULT_RUN_AS_WORKER_NAME
, proc_orig_len
);
325 ret
= pthread_setname_np(pthread_self(), DEFAULT_RUN_AS_WORKER_NAME
);
329 PERROR("pthread_setname_np");
335 writelen
= lttcomm_send_unix_sock(worker
->sockpair
[1], &sendret
,
337 if (writelen
< sizeof(sendret
)) {
338 PERROR("lttcomm_send_unix_sock error");
344 ret
= handle_one_cmd(worker
);
348 } else if (ret
> 0) {
351 continue; /* Next command. */
360 int run_as_cmd(struct run_as_worker
*worker
,
362 struct run_as_data
*data
,
363 uid_t uid
, gid_t gid
)
365 ssize_t readlen
, writelen
;
366 struct run_as_ret recvret
;
369 * If we are non-root, we can only deal with our own uid.
371 if (geteuid() != 0) {
372 if (uid
!= geteuid()) {
374 recvret
._errno
= EPERM
;
375 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
385 writelen
= lttcomm_send_unix_sock(worker
->sockpair
[0], data
,
387 if (writelen
< sizeof(*data
)) {
388 PERROR("Error writing message to run_as");
390 recvret
._errno
= errno
;
394 /* receive return value */
395 readlen
= lttcomm_recv_unix_sock(worker
->sockpair
[0], &recvret
,
398 ERR("Run-as worker has hung-up during run_as_cmd");
400 recvret
._errno
= EIO
;
402 } else if (readlen
< sizeof(recvret
)) {
403 PERROR("Error reading response from run_as");
405 recvret
._errno
= errno
;
407 if (do_recv_fd(worker
, cmd
, &recvret
.ret
)) {
409 recvret
._errno
= EIO
;
413 errno
= recvret
._errno
;
418 * This is for debugging ONLY and should not be considered secure.
421 int run_as_noworker(enum run_as_cmd cmd
,
422 struct run_as_data
*data
, uid_t uid
, gid_t gid
)
424 int ret
, saved_errno
;
428 fct
= run_as_enum_to_fct(cmd
);
444 int run_as(enum run_as_cmd cmd
, struct run_as_data
*data
, uid_t uid
, gid_t gid
)
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
);
456 DBG("Using run_as without worker");
457 ret
= run_as_noworker(cmd
, data
, uid
, gid
);
463 int run_as_mkdir_recursive(const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
465 struct run_as_data data
;
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
);
476 int run_as_mkdir(const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
478 struct run_as_data data
;
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
);
489 * Note: open_run_as is currently not working. We'd need to pass the fd
490 * opened in the child to the parent.
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 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
);
507 int run_as_unlink(const char *path
, uid_t uid
, gid_t gid
)
509 struct run_as_data data
;
511 DBG3("unlink() %s with for uid %d and gid %d",
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
);
519 int run_as_rmdir_recursive(const char *path
, uid_t uid
, gid_t gid
)
521 struct run_as_data data
;
523 DBG3("rmdir_recursive() %s with for uid %d and gid %d",
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
);
531 void reset_sighandler(void)
535 DBG("Resetting run_as worker signal handlers to default");
536 for (sig
= SIGHUP
; sig
<= SIGUNUSED
; sig
++) {
537 /* Skip unblockable signals. */
538 if (sig
== SIGKILL
|| sig
== SIGSTOP
) {
541 if (signal(sig
, SIG_DFL
) == SIG_ERR
) {
542 PERROR("reset signal %d", sig
);
548 int run_as_create_worker(char *procname
)
553 struct run_as_ret recvret
;
554 struct run_as_worker
*worker
;
556 pthread_mutex_lock(&worker_lock
);
557 assert(!global_worker
);
560 * Don't initialize a worker, all run_as tasks will be performed
561 * in the current process.
566 worker
= zmalloc(sizeof(*worker
));
571 worker
->procname
= procname
;
572 /* Create unix socket. */
573 if (lttcomm_create_anon_unix_socketpair(worker
->sockpair
) < 0) {
583 } else if (pid
== 0) {
588 /* The child has no use for this lock. */
589 pthread_mutex_unlock(&worker_lock
);
590 /* Just close, no shutdown. */
591 if (close(worker
->sockpair
[0])) {
595 worker
->sockpair
[0] = -1;
596 ret
= run_as_worker(worker
);
597 if (lttcomm_close_unix_sock(worker
->sockpair
[1])) {
601 worker
->sockpair
[1] = -1;
602 LOG(ret
? PRINT_ERR
: PRINT_DBG
, "run_as worker exiting (ret = %d)", ret
);
603 exit(ret
? EXIT_FAILURE
: EXIT_SUCCESS
);
607 /* Just close, no shutdown. */
608 if (close(worker
->sockpair
[1])) {
613 worker
->sockpair
[1] = -1;
615 /* Wait for worker to become ready. */
616 readlen
= lttcomm_recv_unix_sock(worker
->sockpair
[0],
617 &recvret
, sizeof(recvret
));
618 if (readlen
< sizeof(recvret
)) {
619 ERR("readlen: %zd", readlen
);
620 PERROR("Error reading response from run_as at creation");
624 global_worker
= worker
;
627 pthread_mutex_unlock(&worker_lock
);
630 /* Error handling. */
632 for (i
= 0; i
< 2; i
++) {
633 if (worker
->sockpair
[i
] < 0) {
636 if (lttcomm_close_unix_sock(worker
->sockpair
[i
])) {
639 worker
->sockpair
[i
] = -1;
643 pthread_mutex_unlock(&worker_lock
);
648 void run_as_destroy_worker(void)
650 struct run_as_worker
*worker
= global_worker
;
652 DBG("Destroying run_as worker");
653 pthread_mutex_lock(&worker_lock
);
657 /* Close unix socket */
658 DBG("Closing run_as worker socket");
659 if (lttcomm_close_unix_sock(worker
->sockpair
[0])) {
662 worker
->sockpair
[0] = -1;
663 /* Wait for worker. */
668 wait_ret
= waitpid(worker
->pid
, &status
, 0);
670 if (errno
== EINTR
) {
677 if (WIFEXITED(status
)) {
678 LOG(WEXITSTATUS(status
) == 0 ? PRINT_DBG
: PRINT_ERR
,
679 DEFAULT_RUN_AS_WORKER_NAME
" terminated with status code %d",
680 WEXITSTATUS(status
));
682 } else if (WIFSIGNALED(status
)) {
683 ERR(DEFAULT_RUN_AS_WORKER_NAME
" was killed by signal %d",
689 global_worker
= NULL
;
691 pthread_mutex_unlock(&worker_lock
);