Cleanup: remove duplicated implementation of rculfhash
[lttng-tools.git] / src / common / runas.c
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 _GNU_SOURCE
20 #define _LGPL_SOURCE
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>
31 #include <sched.h>
32 #include <sys/signal.h>
33
34 #include <common/common.h>
35 #include <common/utils.h>
36 #include <common/compat/getenv.h>
37 #include <common/sessiond-comm/unix.h>
38
39 #include "runas.h"
40
41 struct run_as_data;
42 typedef int (*run_as_fct)(struct run_as_data *data);
43
44 struct run_as_mkdir_data {
45 char path[PATH_MAX];
46 mode_t mode;
47 };
48
49 struct run_as_open_data {
50 char path[PATH_MAX];
51 int flags;
52 mode_t mode;
53 };
54
55 struct run_as_unlink_data {
56 char path[PATH_MAX];
57 };
58
59 struct run_as_rmdir_recursive_data {
60 char path[PATH_MAX];
61 };
62
63 enum 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
71 struct 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;
81 };
82
83 struct run_as_ret {
84 int ret;
85 int _errno;
86 };
87
88 struct 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). */
95 static struct run_as_worker *global_worker;
96 /* Lock protecting the worker. */
97 static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
98
99 #ifdef VALGRIND
100 static
101 int use_clone(void)
102 {
103 return 0;
104 }
105 #else
106 static
107 int use_clone(void)
108 {
109 return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
110 }
111 #endif
112
113 LTTNG_HIDDEN
114 int _utils_mkdir_recursive_unsafe(const char *path, mode_t mode);
115
116 /*
117 * Create recursively directory using the FULL path.
118 */
119 static
120 int _mkdir_recursive(struct run_as_data *data)
121 {
122 const char *path;
123 mode_t mode;
124
125 path = data->u.mkdir.path;
126 mode = data->u.mkdir.mode;
127
128 /* Safe to call as we have transitioned to the requested uid/gid. */
129 return _utils_mkdir_recursive_unsafe(path, mode);
130 }
131
132 static
133 int _mkdir(struct run_as_data *data)
134 {
135 return mkdir(data->u.mkdir.path, data->u.mkdir.mode);
136 }
137
138 static
139 int _open(struct run_as_data *data)
140 {
141 return open(data->u.open.path, data->u.open.flags, data->u.open.mode);
142 }
143
144 static
145 int _unlink(struct run_as_data *data)
146 {
147 return unlink(data->u.unlink.path);
148 }
149
150 static
151 int _rmdir_recursive(struct run_as_data *data)
152 {
153 return utils_recursive_rmdir(data->u.rmdir_recursive.path);
154 }
155
156 static
157 run_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 }
174 }
175
176 static
177 int do_send_fd(struct run_as_worker *worker,
178 enum run_as_cmd cmd, int fd)
179 {
180 ssize_t len;
181
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;
201 }
202
203 static
204 int do_recv_fd(struct run_as_worker *worker,
205 enum run_as_cmd cmd, int *fd)
206 {
207 ssize_t len;
208
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;
224 }
225
226 /*
227 * Return < 0 on error, 0 if OK, 1 on hangup.
228 */
229 static
230 int handle_one_cmd(struct run_as_worker *worker)
231 {
232 int ret = 0;
233 struct run_as_data data;
234 ssize_t readlen, writelen;
235 struct run_as_ret sendret;
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 }
252
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);
262 if (ret < 0) {
263 PERROR("setegid");
264 goto write_return;
265 }
266 }
267 if (data.uid != prev_euid) {
268 ret = seteuid(data.uid);
269 if (ret < 0) {
270 PERROR("seteuid");
271 goto write_return;
272 }
273 }
274 /*
275 * Also set umask to 0 for mkdir executable bit.
276 */
277 umask(0);
278 ret = (*cmd)(&data);
279
280 write_return:
281 sendret.ret = ret;
282 sendret._errno = errno;
283 /* send back return value */
284 writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
285 sizeof(sendret));
286 if (writelen < sizeof(sendret)) {
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;
303 end:
304 return ret;
305 }
306
307 static
308 int 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");
327 return EXIT_FAILURE;
328 }
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;
352 end:
353 return ret;
354 }
355
356 static
357 int 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)
361 {
362 ssize_t readlen, writelen;
363 struct run_as_ret recvret;
364
365 pthread_mutex_lock(&worker_lock);
366 /*
367 * If we are non-root, we can only deal with our own uid.
368 */
369 if (geteuid() != 0) {
370 if (uid != geteuid()) {
371 recvret.ret = -1;
372 recvret._errno = EPERM;
373 ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
374 uid, geteuid());
375 goto end;
376 }
377 }
378
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");
387 recvret.ret = -1;
388 recvret._errno = errno;
389 goto end;
390 }
391
392 /* receive return value */
393 readlen = lttcomm_recv_unix_sock(worker->sockpair[0], &recvret,
394 sizeof(recvret));
395 if (readlen < sizeof(recvret)) {
396 PERROR("Error reading response from run_as");
397 recvret.ret = -1;
398 recvret._errno = errno;
399 }
400 if (do_recv_fd(worker, cmd, &recvret.ret)) {
401 recvret.ret = -1;
402 recvret._errno = -EIO;
403 }
404
405 end:
406 pthread_mutex_unlock(&worker_lock);
407 errno = recvret._errno;
408 return recvret.ret;
409 }
410
411 /*
412 * This is for debugging ONLY and should not be considered secure.
413 */
414 static
415 int run_as_noworker(enum run_as_cmd cmd,
416 struct run_as_data *data, uid_t uid, gid_t gid)
417 {
418 int ret, saved_errno;
419 mode_t old_mask;
420 run_as_fct fct;
421
422 fct = run_as_enum_to_fct(cmd);
423 if (!fct) {
424 errno = -ENOSYS;
425 ret = -1;
426 goto end;
427 }
428 old_mask = umask(0);
429 ret = fct(data);
430 saved_errno = errno;
431 umask(old_mask);
432 errno = saved_errno;
433 end:
434 return ret;
435 }
436
437 static
438 int 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)
441 {
442 int ret;
443
444 if (worker) {
445 DBG("Using run_as worker");
446 ret = run_as_cmd(worker, cmd, data, uid, gid);
447 } else {
448 DBG("Using run_as without worker");
449 ret = run_as_noworker(cmd, data, uid, gid);
450 }
451 return ret;
452 }
453
454 LTTNG_HIDDEN
455 int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
456 {
457 struct run_as_worker *worker = global_worker;
458 struct run_as_data data;
459
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);
466 }
467
468 LTTNG_HIDDEN
469 int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
470 {
471 struct run_as_worker *worker = global_worker;
472 struct run_as_data data;
473
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);
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 */
486 LTTNG_HIDDEN
487 int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
488 {
489 struct run_as_worker *worker = global_worker;
490 struct run_as_data data;
491
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);
499 }
500
501 LTTNG_HIDDEN
502 int run_as_unlink(const char *path, uid_t uid, gid_t gid)
503 {
504 struct run_as_worker *worker = global_worker;
505 struct run_as_data data;
506
507 DBG3("unlink() %s with for uid %d and gid %d",
508 path, uid, gid);
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);
512 }
513
514 LTTNG_HIDDEN
515 int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid)
516 {
517 struct run_as_worker *worker = global_worker;
518 struct run_as_data data;
519
520 DBG3("rmdir_recursive() %s with for uid %d and gid %d",
521 path, uid, gid);
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
527 LTTNG_HIDDEN
528 int 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 }
595 end:
596 return ret;
597
598 /* Error handling. */
599 error_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 }
609 error_sock:
610 free(worker);
611 return ret;
612 }
613
614 LTTNG_HIDDEN
615 void 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;
636 }
This page took 0.041392 seconds and 4 git commands to generate.