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>
34 #include <common/common.h>
35 #include <common/utils.h>
36 #include <common/compat/getenv.h>
37 #include <common/sessiond-comm/unix.h>
42 typedef int (*run_as_fct
)(struct run_as_data
*data
);
44 struct run_as_mkdir_data
{
49 struct run_as_open_data
{
55 struct run_as_unlink_data
{
59 struct run_as_rmdir_recursive_data
{
67 RUN_AS_RMDIR_RECURSIVE
,
68 RUN_AS_MKDIR_RECURSIVE
,
74 struct run_as_mkdir_data mkdir
;
75 struct run_as_open_data open
;
76 struct run_as_unlink_data unlink
;
77 struct run_as_rmdir_recursive_data rmdir_recursive
;
88 struct run_as_worker
{
89 pid_t pid
; /* Worker PID. */
94 /* Single global worker per process (for now). */
95 static struct run_as_worker
*global_worker
;
96 /* Lock protecting the worker. */
97 static pthread_mutex_t worker_lock
= PTHREAD_MUTEX_INITIALIZER
;
109 return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
114 int _utils_mkdir_recursive_unsafe(const char *path
, mode_t mode
);
117 * Create recursively directory using the FULL path.
120 int _mkdir_recursive(struct run_as_data
*data
)
125 path
= data
->u
.mkdir
.path
;
126 mode
= data
->u
.mkdir
.mode
;
128 /* Safe to call as we have transitioned to the requested uid/gid. */
129 return _utils_mkdir_recursive_unsafe(path
, mode
);
133 int _mkdir(struct run_as_data
*data
)
135 return mkdir(data
->u
.mkdir
.path
, data
->u
.mkdir
.mode
);
139 int _open(struct run_as_data
*data
)
141 return open(data
->u
.open
.path
, data
->u
.open
.flags
, data
->u
.open
.mode
);
145 int _unlink(struct run_as_data
*data
)
147 return unlink(data
->u
.unlink
.path
);
151 int _rmdir_recursive(struct run_as_data
*data
)
153 return utils_recursive_rmdir(data
->u
.rmdir_recursive
.path
);
157 run_as_fct
run_as_enum_to_fct(enum run_as_cmd cmd
)
166 case RUN_AS_RMDIR_RECURSIVE
:
167 return _rmdir_recursive
;
168 case RUN_AS_MKDIR_RECURSIVE
:
169 return _mkdir_recursive
;
171 ERR("Unknown command %d", (int) cmd
)
177 int do_send_fd(struct run_as_worker
*worker
,
178 enum run_as_cmd cmd
, int fd
)
191 len
= lttcomm_send_fds_unix_sock(worker
->sockpair
[1], &fd
, 1);
193 PERROR("lttcomm_send_fds_unix_sock");
204 int do_recv_fd(struct run_as_worker
*worker
,
205 enum run_as_cmd cmd
, int *fd
)
218 len
= lttcomm_recv_fds_unix_sock(worker
->sockpair
[0], fd
, 1);
220 PERROR("lttcomm_recv_fds_unix_sock");
227 * Return < 0 on error, 0 if OK, 1 on hangup.
230 int handle_one_cmd(struct run_as_worker
*worker
)
233 struct run_as_data data
;
234 ssize_t readlen
, writelen
;
235 struct run_as_ret sendret
;
240 readlen
= lttcomm_recv_unix_sock(worker
->sockpair
[1], &data
,
247 if (readlen
< sizeof(data
)) {
248 PERROR("lttcomm_recv_unix_sock error");
253 cmd
= run_as_enum_to_fct(data
.cmd
);
259 prev_euid
= getuid();
260 if (data
.gid
!= getegid()) {
261 ret
= setegid(data
.gid
);
267 if (data
.uid
!= prev_euid
) {
268 ret
= seteuid(data
.uid
);
275 * Also set umask to 0 for mkdir executable bit.
282 sendret
._errno
= errno
;
283 /* send back return value */
284 writelen
= lttcomm_send_unix_sock(worker
->sockpair
[1], &sendret
,
286 if (writelen
< sizeof(sendret
)) {
287 PERROR("lttcomm_send_unix_sock error");
291 ret
= do_send_fd(worker
, data
.cmd
, ret
);
293 PERROR("do_send_fd error");
297 if (seteuid(prev_euid
) < 0) {
308 int run_as_worker(struct run_as_worker
*worker
)
312 struct run_as_ret sendret
;
313 size_t proc_orig_len
;
316 * Initialize worker. Set a different process cmdline.
318 proc_orig_len
= strlen(worker
->procname
);
319 memset(worker
->procname
, 0, proc_orig_len
);
320 strncpy(worker
->procname
, DEFAULT_RUN_AS_WORKER_NAME
, proc_orig_len
);
322 ret
= pthread_setname_np(pthread_self(), DEFAULT_RUN_AS_WORKER_NAME
);
326 PERROR("pthread_setname_np");
332 writelen
= lttcomm_send_unix_sock(worker
->sockpair
[1], &sendret
,
334 if (writelen
< sizeof(sendret
)) {
335 PERROR("lttcomm_send_unix_sock error");
341 ret
= handle_one_cmd(worker
);
345 } else if (ret
> 0) {
348 continue; /* Next command. */
357 int run_as_cmd(struct run_as_worker
*worker
,
359 struct run_as_data
*data
,
360 uid_t uid
, gid_t gid
)
362 ssize_t readlen
, writelen
;
363 struct run_as_ret recvret
;
365 pthread_mutex_lock(&worker_lock
);
367 * If we are non-root, we can only deal with our own uid.
369 if (geteuid() != 0) {
370 if (uid
!= geteuid()) {
372 recvret
._errno
= EPERM
;
373 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
383 writelen
= lttcomm_send_unix_sock(worker
->sockpair
[0], data
,
385 if (writelen
< sizeof(*data
)) {
386 PERROR("Error writing message to run_as");
388 recvret
._errno
= errno
;
392 /* receive return value */
393 readlen
= lttcomm_recv_unix_sock(worker
->sockpair
[0], &recvret
,
395 if (readlen
< sizeof(recvret
)) {
396 PERROR("Error reading response from run_as");
398 recvret
._errno
= errno
;
400 if (do_recv_fd(worker
, cmd
, &recvret
.ret
)) {
402 recvret
._errno
= -EIO
;
406 pthread_mutex_unlock(&worker_lock
);
407 errno
= recvret
._errno
;
412 * This is for debugging ONLY and should not be considered secure.
415 int run_as_noworker(enum run_as_cmd cmd
,
416 struct run_as_data
*data
, uid_t uid
, gid_t gid
)
418 int ret
, saved_errno
;
422 fct
= run_as_enum_to_fct(cmd
);
438 int run_as(struct run_as_worker
*worker
,
440 struct run_as_data
*data
, uid_t uid
, gid_t gid
)
445 DBG("Using run_as worker");
446 ret
= run_as_cmd(worker
, cmd
, data
, uid
, gid
);
448 DBG("Using run_as without worker");
449 ret
= run_as_noworker(cmd
, data
, uid
, gid
);
455 int run_as_mkdir_recursive(const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
457 struct run_as_worker
*worker
= global_worker
;
458 struct run_as_data data
;
460 DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
461 path
, mode
, uid
, gid
);
462 strncpy(data
.u
.mkdir
.path
, path
, PATH_MAX
- 1);
463 data
.u
.mkdir
.path
[PATH_MAX
- 1] = '\0';
464 data
.u
.mkdir
.mode
= mode
;
465 return run_as(worker
, RUN_AS_MKDIR_RECURSIVE
, &data
, uid
, gid
);
469 int run_as_mkdir(const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
471 struct run_as_worker
*worker
= global_worker
;
472 struct run_as_data data
;
474 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
475 path
, mode
, uid
, gid
);
476 strncpy(data
.u
.mkdir
.path
, path
, PATH_MAX
- 1);
477 data
.u
.mkdir
.path
[PATH_MAX
- 1] = '\0';
478 data
.u
.mkdir
.mode
= mode
;
479 return run_as(worker
, RUN_AS_MKDIR
, &data
, uid
, gid
);
483 * Note: open_run_as is currently not working. We'd need to pass the fd
484 * opened in the child to the parent.
487 int run_as_open(const char *path
, int flags
, mode_t mode
, uid_t uid
, gid_t gid
)
489 struct run_as_worker
*worker
= global_worker
;
490 struct run_as_data data
;
492 DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
493 path
, flags
, mode
, uid
, gid
);
494 strncpy(data
.u
.open
.path
, path
, PATH_MAX
- 1);
495 data
.u
.open
.path
[PATH_MAX
- 1] = '\0';
496 data
.u
.open
.flags
= flags
;
497 data
.u
.open
.mode
= mode
;
498 return run_as(worker
, RUN_AS_OPEN
, &data
, uid
, gid
);
502 int run_as_unlink(const char *path
, uid_t uid
, gid_t gid
)
504 struct run_as_worker
*worker
= global_worker
;
505 struct run_as_data data
;
507 DBG3("unlink() %s with for uid %d and gid %d",
509 strncpy(data
.u
.unlink
.path
, path
, PATH_MAX
- 1);
510 data
.u
.unlink
.path
[PATH_MAX
- 1] = '\0';
511 return run_as(worker
, RUN_AS_UNLINK
, &data
, uid
, gid
);
515 int run_as_rmdir_recursive(const char *path
, uid_t uid
, gid_t gid
)
517 struct run_as_worker
*worker
= global_worker
;
518 struct run_as_data data
;
520 DBG3("rmdir_recursive() %s with for uid %d and gid %d",
522 strncpy(data
.u
.rmdir_recursive
.path
, path
, PATH_MAX
- 1);
523 data
.u
.rmdir_recursive
.path
[PATH_MAX
- 1] = '\0';
524 return run_as(worker
, RUN_AS_RMDIR_RECURSIVE
, &data
, uid
, gid
);
528 int run_as_create_worker(char *procname
)
533 struct run_as_ret recvret
;
534 struct run_as_worker
*worker
;
540 worker
= zmalloc(sizeof(*worker
));
545 worker
->procname
= procname
;
546 /* Create unix socket. */
547 if (lttcomm_create_anon_unix_socketpair(worker
->sockpair
) < 0) {
557 } else if (pid
== 0) {
560 /* Just close, no shutdown. */
561 if (close(worker
->sockpair
[0])) {
565 worker
->sockpair
[0] = -1;
566 ret
= run_as_worker(worker
);
567 if (lttcomm_close_unix_sock(worker
->sockpair
[1])) {
571 worker
->sockpair
[1] = -1;
572 exit(ret
? EXIT_FAILURE
: EXIT_SUCCESS
);
576 /* Just close, no shutdown. */
577 if (close(worker
->sockpair
[1])) {
582 worker
->sockpair
[1] = -1;
584 /* Wait for worker to become ready. */
585 readlen
= lttcomm_recv_unix_sock(worker
->sockpair
[0],
586 &recvret
, sizeof(recvret
));
587 if (readlen
< sizeof(recvret
)) {
588 ERR("readlen: %zd", readlen
);
589 PERROR("Error reading response from run_as at creation");
593 global_worker
= worker
;
598 /* Error handling. */
600 for (i
= 0; i
< 2; i
++) {
601 if (worker
->sockpair
[i
] < 0) {
604 if (lttcomm_close_unix_sock(worker
->sockpair
[i
])) {
607 worker
->sockpair
[i
] = -1;
615 void run_as_destroy_worker(void)
617 struct run_as_worker
*worker
= global_worker
;
624 /* Close unix socket */
625 if (lttcomm_close_unix_sock(worker
->sockpair
[0])) {
628 worker
->sockpair
[0] = -1;
629 /* Wait for worker. */
630 pid
= waitpid(worker
->pid
, &status
, 0);
631 if (pid
< 0 || !WIFEXITED(status
) || WEXITSTATUS(status
) != 0) {
635 global_worker
= NULL
;