Cleanup: remove duplicated implementation of rculfhash
[lttng-tools.git] / src / common / runas.c
CommitLineData
60b6c79c
MD
1/*
2 * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
3 * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 *
d14d33bf
AM
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.
60b6c79c
MD
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
d14d33bf 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
60b6c79c
MD
12 * more details.
13 *
d14d33bf
AM
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.
60b6c79c
MD
17 */
18
19#define _GNU_SOURCE
6c1c0768 20#define _LGPL_SOURCE
60b6c79c
MD
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>
c2b75c49 31#include <sched.h>
730389d9 32#include <sys/signal.h>
60b6c79c 33
90e535ef 34#include <common/common.h>
3fd15a74 35#include <common/utils.h>
e8fa9fb0 36#include <common/compat/getenv.h>
7567352f 37#include <common/sessiond-comm/unix.h>
60b6c79c 38
0857097f
DG
39#include "runas.h"
40
7567352f
MD
41struct run_as_data;
42typedef int (*run_as_fct)(struct run_as_data *data);
c2b75c49 43
e11d277b 44struct run_as_mkdir_data {
7567352f 45 char path[PATH_MAX];
60b6c79c
MD
46 mode_t mode;
47};
48
e11d277b 49struct run_as_open_data {
7567352f 50 char path[PATH_MAX];
60b6c79c
MD
51 int flags;
52 mode_t mode;
53};
54
4628484a 55struct run_as_unlink_data {
7567352f 56 char path[PATH_MAX];
4628484a
MD
57};
58
7567352f
MD
59struct run_as_rmdir_recursive_data {
60 char path[PATH_MAX];
61};
62
63enum run_as_cmd {
64 RUN_AS_MKDIR,
65 RUN_AS_OPEN,
66 RUN_AS_UNLINK,
67 RUN_AS_RMDIR_RECURSIVE,
68 RUN_AS_MKDIR_RECURSIVE,
69};
70
71struct run_as_data {
72 enum run_as_cmd cmd;
73 union {
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;
78 } u;
79 uid_t uid;
80 gid_t gid;
4628484a
MD
81};
82
df5b86c8
MD
83struct run_as_ret {
84 int ret;
85 int _errno;
86};
87
7567352f
MD
88struct run_as_worker {
89 pid_t pid; /* Worker PID. */
90 int sockpair[2];
91 char *procname;
92};
93
94/* Single global worker per process (for now). */
95static struct run_as_worker *global_worker;
96/* Lock protecting the worker. */
97static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
98
8f0044bf
MD
99#ifdef VALGRIND
100static
101int use_clone(void)
102{
103 return 0;
104}
105#else
106static
107int use_clone(void)
108{
e8fa9fb0 109 return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
8f0044bf
MD
110}
111#endif
112
d77dded2
JG
113LTTNG_HIDDEN
114int _utils_mkdir_recursive_unsafe(const char *path, mode_t mode);
115
60b6c79c
MD
116/*
117 * Create recursively directory using the FULL path.
118 */
119static
7567352f 120int _mkdir_recursive(struct run_as_data *data)
60b6c79c 121{
60b6c79c 122 const char *path;
60b6c79c 123 mode_t mode;
60b6c79c 124
7567352f
MD
125 path = data->u.mkdir.path;
126 mode = data->u.mkdir.mode;
60b6c79c 127
d77dded2
JG
128 /* Safe to call as we have transitioned to the requested uid/gid. */
129 return _utils_mkdir_recursive_unsafe(path, mode);
60b6c79c
MD
130}
131
132static
7567352f 133int _mkdir(struct run_as_data *data)
60b6c79c 134{
7567352f
MD
135 return mkdir(data->u.mkdir.path, data->u.mkdir.mode);
136}
7ce36756 137
7567352f
MD
138static
139int _open(struct run_as_data *data)
140{
141 return open(data->u.open.path, data->u.open.flags, data->u.open.mode);
142}
143
144static
145int _unlink(struct run_as_data *data)
146{
147 return unlink(data->u.unlink.path);
60b6c79c
MD
148}
149
150static
7567352f 151int _rmdir_recursive(struct run_as_data *data)
60b6c79c 152{
7567352f
MD
153 return utils_recursive_rmdir(data->u.rmdir_recursive.path);
154}
df5b86c8 155
7567352f
MD
156static
157run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd)
158{
159 switch (cmd) {
160 case RUN_AS_MKDIR:
161 return _mkdir;
162 case RUN_AS_OPEN:
163 return _open;
164 case RUN_AS_UNLINK:
165 return _unlink;
166 case RUN_AS_RMDIR_RECURSIVE:
167 return _rmdir_recursive;
168 case RUN_AS_MKDIR_RECURSIVE:
169 return _mkdir_recursive;
170 default:
171 ERR("Unknown command %d", (int) cmd)
172 return NULL;
173 }
60b6c79c
MD
174}
175
4628484a 176static
7567352f
MD
177int do_send_fd(struct run_as_worker *worker,
178 enum run_as_cmd cmd, int fd)
4628484a 179{
7567352f 180 ssize_t len;
4628484a 181
7567352f
MD
182 switch (cmd) {
183 case RUN_AS_OPEN:
184 break;
185 default:
186 return 0;
187 }
188 if (fd < 0) {
189 return 0;
190 }
191 len = lttcomm_send_fds_unix_sock(worker->sockpair[1], &fd, 1);
192 if (len < 0) {
193 PERROR("lttcomm_send_fds_unix_sock");
194 return -1;
195 }
196 if (close(fd) < 0) {
197 PERROR("close");
198 return -1;
199 }
200 return 0;
4628484a
MD
201}
202
203static
7567352f
MD
204int do_recv_fd(struct run_as_worker *worker,
205 enum run_as_cmd cmd, int *fd)
4628484a 206{
7567352f 207 ssize_t len;
4628484a 208
7567352f
MD
209 switch (cmd) {
210 case RUN_AS_OPEN:
211 break;
212 default:
213 return 0;
214 }
215 if (*fd < 0) {
216 return 0;
217 }
218 len = lttcomm_recv_fds_unix_sock(worker->sockpair[0], fd, 1);
219 if (len < 0) {
220 PERROR("lttcomm_recv_fds_unix_sock");
221 return -1;
222 }
223 return 0;
4628484a
MD
224}
225
7567352f
MD
226/*
227 * Return < 0 on error, 0 if OK, 1 on hangup.
228 */
c2b75c49 229static
7567352f 230int handle_one_cmd(struct run_as_worker *worker)
c2b75c49 231{
7567352f
MD
232 int ret = 0;
233 struct run_as_data data;
234 ssize_t readlen, writelen;
df5b86c8 235 struct run_as_ret sendret;
7567352f
MD
236 run_as_fct cmd;
237 uid_t prev_euid;
238
239 /* Read data */
240 readlen = lttcomm_recv_unix_sock(worker->sockpair[1], &data,
241 sizeof(data));
242 if (readlen == 0) {
243 /* hang up */
244 ret = 1;
245 goto end;
246 }
247 if (readlen < sizeof(data)) {
248 PERROR("lttcomm_recv_unix_sock error");
249 ret = -1;
250 goto end;
251 }
c2b75c49 252
7567352f
MD
253 cmd = run_as_enum_to_fct(data.cmd);
254 if (!cmd) {
255 ret = -1;
256 goto end;
257 }
258
259 prev_euid = getuid();
260 if (data.gid != getegid()) {
261 ret = setegid(data.gid);
1576d582 262 if (ret < 0) {
4c462e79 263 PERROR("setegid");
6d73c4ef 264 goto write_return;
1576d582 265 }
c2b75c49 266 }
7567352f
MD
267 if (data.uid != prev_euid) {
268 ret = seteuid(data.uid);
1576d582 269 if (ret < 0) {
4c462e79 270 PERROR("seteuid");
6d73c4ef 271 goto write_return;
1576d582 272 }
c2b75c49
MD
273 }
274 /*
275 * Also set umask to 0 for mkdir executable bit.
276 */
277 umask(0);
7567352f 278 ret = (*cmd)(&data);
6d73c4ef
MD
279
280write_return:
df5b86c8
MD
281 sendret.ret = ret;
282 sendret._errno = errno;
c2b75c49 283 /* send back return value */
7567352f
MD
284 writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
285 sizeof(sendret));
6cd525e8 286 if (writelen < sizeof(sendret)) {
7567352f
MD
287 PERROR("lttcomm_send_unix_sock error");
288 ret = -1;
289 goto end;
290 }
291 ret = do_send_fd(worker, data.cmd, ret);
292 if (ret) {
293 PERROR("do_send_fd error");
294 ret = -1;
295 goto end;
296 }
297 if (seteuid(prev_euid) < 0) {
298 PERROR("seteuid");
299 ret = -1;
300 goto end;
301 }
302 ret = 0;
303end:
304 return ret;
305}
306
307static
308int run_as_worker(struct run_as_worker *worker)
309{
310 int ret;
311 ssize_t writelen;
312 struct run_as_ret sendret;
313 size_t proc_orig_len;
314
315 /*
316 * Initialize worker. Set a different process cmdline.
317 */
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);
321
322 ret = pthread_setname_np(pthread_self(), DEFAULT_RUN_AS_WORKER_NAME);
323 if (ret) {
324 errno = ret;
325 ret = -1;
326 PERROR("pthread_setname_np");
6cd525e8 327 return EXIT_FAILURE;
6cd525e8 328 }
7567352f
MD
329
330 sendret.ret = 0;
331 sendret._errno = 0;
332 writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
333 sizeof(sendret));
334 if (writelen < sizeof(sendret)) {
335 PERROR("lttcomm_send_unix_sock error");
336 ret = EXIT_FAILURE;
337 goto end;
338 }
339
340 for (;;) {
341 ret = handle_one_cmd(worker);
342 if (ret < 0) {
343 ret = EXIT_FAILURE;
344 goto end;
345 } else if (ret > 0) {
346 break;
347 } else {
348 continue; /* Next command. */
349 }
350 }
351 ret = EXIT_SUCCESS;
352end:
353 return ret;
c2b75c49
MD
354}
355
60b6c79c 356static
7567352f
MD
357int run_as_cmd(struct run_as_worker *worker,
358 enum run_as_cmd cmd,
359 struct run_as_data *data,
360 uid_t uid, gid_t gid)
60b6c79c 361{
7567352f 362 ssize_t readlen, writelen;
df5b86c8 363 struct run_as_ret recvret;
60b6c79c 364
7567352f 365 pthread_mutex_lock(&worker_lock);
60b6c79c
MD
366 /*
367 * If we are non-root, we can only deal with our own uid.
368 */
369 if (geteuid() != 0) {
370 if (uid != geteuid()) {
df5b86c8
MD
371 recvret.ret = -1;
372 recvret._errno = EPERM;
60b6c79c
MD
373 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
374 uid, geteuid());
df5b86c8 375 goto end;
60b6c79c 376 }
60b6c79c
MD
377 }
378
7567352f
MD
379 data->cmd = cmd;
380 data->uid = uid;
381 data->gid = gid;
382
383 writelen = lttcomm_send_unix_sock(worker->sockpair[0], data,
384 sizeof(*data));
385 if (writelen < sizeof(*data)) {
386 PERROR("Error writing message to run_as");
df5b86c8
MD
387 recvret.ret = -1;
388 recvret._errno = errno;
60b6c79c 389 goto end;
c2b75c49 390 }
7567352f 391
c2b75c49 392 /* receive return value */
7567352f
MD
393 readlen = lttcomm_recv_unix_sock(worker->sockpair[0], &recvret,
394 sizeof(recvret));
df5b86c8 395 if (readlen < sizeof(recvret)) {
7567352f 396 PERROR("Error reading response from run_as");
df5b86c8
MD
397 recvret.ret = -1;
398 recvret._errno = errno;
6cd525e8 399 }
7567352f 400 if (do_recv_fd(worker, cmd, &recvret.ret)) {
df5b86c8 401 recvret.ret = -1;
7567352f 402 recvret._errno = -EIO;
4c462e79 403 }
7567352f 404
60b6c79c 405end:
7567352f 406 pthread_mutex_unlock(&worker_lock);
df5b86c8
MD
407 errno = recvret._errno;
408 return recvret.ret;
60b6c79c
MD
409}
410
2d85a600 411/*
7567352f 412 * This is for debugging ONLY and should not be considered secure.
2d85a600
MD
413 */
414static
7567352f
MD
415int run_as_noworker(enum run_as_cmd cmd,
416 struct run_as_data *data, uid_t uid, gid_t gid)
2d85a600 417{
df5b86c8 418 int ret, saved_errno;
5b73926f 419 mode_t old_mask;
7567352f 420 run_as_fct fct;
5b73926f 421
7567352f
MD
422 fct = run_as_enum_to_fct(cmd);
423 if (!fct) {
424 errno = -ENOSYS;
425 ret = -1;
426 goto end;
427 }
5b73926f 428 old_mask = umask(0);
7567352f 429 ret = fct(data);
df5b86c8 430 saved_errno = errno;
5b73926f 431 umask(old_mask);
df5b86c8 432 errno = saved_errno;
7567352f 433end:
5b73926f 434 return ret;
2d85a600
MD
435}
436
437static
7567352f
MD
438int run_as(struct run_as_worker *worker,
439 enum run_as_cmd cmd,
440 struct run_as_data *data, uid_t uid, gid_t gid)
2d85a600 441{
7567352f
MD
442 int ret;
443
444 if (worker) {
445 DBG("Using run_as worker");
446 ret = run_as_cmd(worker, cmd, data, uid, gid);
2d85a600 447 } else {
7567352f
MD
448 DBG("Using run_as without worker");
449 ret = run_as_noworker(cmd, data, uid, gid);
2d85a600 450 }
7567352f 451 return ret;
2d85a600
MD
452}
453
90e535ef 454LTTNG_HIDDEN
e11d277b 455int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
60b6c79c 456{
7567352f
MD
457 struct run_as_worker *worker = global_worker;
458 struct run_as_data data;
60b6c79c
MD
459
460 DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
461 path, mode, uid, gid);
7567352f
MD
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);
60b6c79c
MD
466}
467
90e535ef 468LTTNG_HIDDEN
e11d277b 469int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
60b6c79c 470{
7567352f
MD
471 struct run_as_worker *worker = global_worker;
472 struct run_as_data data;
60b6c79c
MD
473
474 DBG3("mkdir() %s with mode %d for uid %d and gid %d",
475 path, mode, uid, gid);
7567352f
MD
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);
60b6c79c
MD
480}
481
482/*
483 * Note: open_run_as is currently not working. We'd need to pass the fd
484 * opened in the child to the parent.
485 */
90e535ef 486LTTNG_HIDDEN
e11d277b 487int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
60b6c79c 488{
7567352f
MD
489 struct run_as_worker *worker = global_worker;
490 struct run_as_data data;
c2b75c49 491
47fb7563
MD
492 DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
493 path, flags, mode, uid, gid);
7567352f
MD
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);
60b6c79c 499}
4628484a
MD
500
501LTTNG_HIDDEN
502int run_as_unlink(const char *path, uid_t uid, gid_t gid)
503{
7567352f
MD
504 struct run_as_worker *worker = global_worker;
505 struct run_as_data data;
4628484a
MD
506
507 DBG3("unlink() %s with for uid %d and gid %d",
508 path, uid, gid);
7567352f
MD
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);
4628484a
MD
512}
513
514LTTNG_HIDDEN
7567352f 515int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid)
4628484a 516{
7567352f
MD
517 struct run_as_worker *worker = global_worker;
518 struct run_as_data data;
4628484a 519
7567352f 520 DBG3("rmdir_recursive() %s with for uid %d and gid %d",
4628484a 521 path, uid, gid);
7567352f
MD
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);
525}
526
527LTTNG_HIDDEN
528int run_as_create_worker(char *procname)
529{
530 pid_t pid;
531 int i, ret = 0;
532 ssize_t readlen;
533 struct run_as_ret recvret;
534 struct run_as_worker *worker;
535
536 if (!use_clone()) {
537 ret = 0;
538 goto end;
539 }
540 worker = zmalloc(sizeof(*worker));
541 if (!worker) {
542 ret = -ENOMEM;
543 goto end;
544 }
545 worker->procname = procname;
546 /* Create unix socket. */
547 if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
548 ret = -1;
549 goto error_sock;
550 }
551 /* Fork worker. */
552 pid = fork();
553 if (pid < 0) {
554 PERROR("fork");
555 ret = -1;
556 goto error_fork;
557 } else if (pid == 0) {
558 /* Child */
559
560 /* Just close, no shutdown. */
561 if (close(worker->sockpair[0])) {
562 PERROR("close");
563 exit(EXIT_FAILURE);
564 }
565 worker->sockpair[0] = -1;
566 ret = run_as_worker(worker);
567 if (lttcomm_close_unix_sock(worker->sockpair[1])) {
568 PERROR("close");
569 ret = -1;
570 }
571 worker->sockpair[1] = -1;
572 exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
573 } else {
574 /* Parent */
575
576 /* Just close, no shutdown. */
577 if (close(worker->sockpair[1])) {
578 PERROR("close");
579 ret = -1;
580 goto error_fork;
581 }
582 worker->sockpair[1] = -1;
583 worker->pid = pid;
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");
590 ret = -1;
591 goto error_fork;
592 }
593 global_worker = worker;
594 }
595end:
596 return ret;
597
598 /* Error handling. */
599error_fork:
600 for (i = 0; i < 2; i++) {
601 if (worker->sockpair[i] < 0) {
602 continue;
603 }
604 if (lttcomm_close_unix_sock(worker->sockpair[i])) {
605 PERROR("close");
606 }
607 worker->sockpair[i] = -1;
608 }
609error_sock:
610 free(worker);
611 return ret;
612}
613
614LTTNG_HIDDEN
615void run_as_destroy_worker(void)
616{
617 struct run_as_worker *worker = global_worker;
618 int status;
619 pid_t pid;
620
621 if (!worker) {
622 return;
623 }
624 /* Close unix socket */
625 if (lttcomm_close_unix_sock(worker->sockpair[0])) {
626 PERROR("close");
627 }
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) {
632 PERROR("wait");
633 }
634 free(worker);
635 global_worker = NULL;
4628484a 636}
This page took 0.064068 seconds and 4 git commands to generate.