Blame view

privileged.c 49.7 KB
redmine authored
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
    clsync - file tree sync utility based on inotify/kqueue
    
    Copyright (C) 2013-2014 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

redmine authored
20 21 22
#include "common.h"			// ctx.h
#include "ctx.h"			// ctx_t
#include "error.h"			// debug()
redmine authored
23
#include "syscalls.h"			// read_inf()/write_inf()
redmine authored
24
#include "main.h"			// ncpus
redmine authored
25
#include "pthreadex.h"			// pthread_*_shared()
redmine authored
26
#include "malloc.h"			// xmalloc()
redmine authored
27

redmine authored
28
#ifdef CAPABILITIES_SUPPORT
redmine authored
29 30 31 32 33 34 35
# include <pthread.h>			// pthread_create()
# include <sys/inotify.h>		// inotify_init()
# include <sys/types.h>			// fts_open()
# include <sys/stat.h>			// fts_open()
# include <fts.h>			// fts_open()
# include <errno.h>			// errno
# include <sys/capability.h>		// capset()
redmine authored
36 37 38
# ifdef CGROUP_SUPPORT
#  include "cgroup.h"			// clsync_cgroup_deinit()
# endif
redmine authored
39 40 41
#endif

#include <unistd.h>			// execvp()
redmine authored
42
#include <glib.h>			// g_atomic_int_set()
redmine authored
43

redmine authored
44
#ifdef UNSHARE_SUPPORT
redmine authored
45
# include <sched.h>			// unshare()
redmine authored
46 47
#endif

redmine authored
48 49 50 51 52 53 54
#ifndef HL_LOCKS
# ifdef HL_LOCK_TRIES_AUTO
#  undef HL_LOCK_TRIES_AUTO
# endif
#endif

#ifdef HL_LOCK_TRIES_AUTO
redmine authored
55 56
# include <time.h>			// time()
# include <math.h>			// fabs()
redmine authored
57 58 59 60
#endif

#include "privileged.h"

redmine authored
61
#ifdef SECCOMP_SUPPORT
redmine authored
62
# include <syscall.h>			// __NR_*
redmine authored
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
# include <sys/prctl.h>			// prctl()
# include <linux/filter.h>		// struct sock_filter
# include <linux/seccomp.h>		// SECCOMP_RET_*

#define syscall_nr (offsetof(struct seccomp_data, nr))

/* Read: http://www.rawether.net/support/bpfhelp.htm */
# define SECCOMP_COPY_SYSCALL_TO_ACCUM				\
	BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_nr)

# define SECCOMP_ALLOW						\
	BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)

# define SECCOMP_DENY						\
	BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP)

# define SECCOMP_ALLOW_ACCUM_SYSCALL(syscall)			\
	BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_##syscall, 0, 1),	\
	SECCOMP_ALLOW

redmine authored
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
# define FILTER_TABLE_NONPRIV						\
	SECCOMP_ALLOW_ACCUM_SYSCALL(futex),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(inotify_init1),			\
	SECCOMP_ALLOW_ACCUM_SYSCALL(alarm),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(stat),		/* unused */	\
	SECCOMP_ALLOW_ACCUM_SYSCALL(fstat),		/* unused */	\
	SECCOMP_ALLOW_ACCUM_SYSCALL(lstat),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(open),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(write),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(close),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(wait4),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(unlink),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(tgkill),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(clock_gettime),			\
	SECCOMP_ALLOW_ACCUM_SYSCALL(rt_sigreturn),			\
	SECCOMP_ALLOW_ACCUM_SYSCALL(brk),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(mmap),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(munmap),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(wait4),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(rmdir),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(exit_group),			\
	SECCOMP_ALLOW_ACCUM_SYSCALL(select),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(read),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(rt_sigprocmask),			\
	SECCOMP_ALLOW_ACCUM_SYSCALL(rt_sigaction),			\
	SECCOMP_ALLOW_ACCUM_SYSCALL(nanosleep),				\
redmine authored
109
	SECCOMP_ALLOW_ACCUM_SYSCALL(shmdt),				\
redmine authored
110
	SECCOMP_ALLOW_ACCUM_SYSCALL(clone),		/* for --threading */		\
redmine authored
111 112 113 114
	SECCOMP_ALLOW_ACCUM_SYSCALL(set_robust_list),	/* for --threading? */		\
	SECCOMP_ALLOW_ACCUM_SYSCALL(madvise),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(exit),				\

redmine authored
115

redmine authored
116 117 118 119 120 121 122

/* Syscalls allowed to non-privileged thread */
static struct sock_filter filter_table[] = {
	SECCOMP_COPY_SYSCALL_TO_ACCUM,
	FILTER_TABLE_NONPRIV
	SECCOMP_DENY,
};
redmine authored
123 124 125 126 127 128
static struct sock_filter filter_w_mprotect_table[] = {
	SECCOMP_COPY_SYSCALL_TO_ACCUM,
	FILTER_TABLE_NONPRIV
	SECCOMP_ALLOW_ACCUM_SYSCALL(mprotect),
	SECCOMP_DENY,
};
redmine authored
129

redmine authored
130 131 132
int nonprivileged_seccomp_init(ctx_t *ctx_p) {
	struct sock_fprog *filter_p;
	struct sock_fprog filter = {
redmine authored
133 134 135
		.len = (unsigned short)(sizeof(filter_table)/sizeof(filter_table[0])),
		.filter = filter_table,
	};
redmine authored
136 137 138 139 140
	struct sock_fprog filter_w_mprotect = {
		.len = (unsigned short)(sizeof(filter_w_mprotect_table)/sizeof(filter_w_mprotect_table[0])),
		.filter = filter_w_mprotect_table,
	};

redmine authored
141 142
	debug(5, "enabling the seccomp");

redmine authored
143
	filter_p = (ctx_p->flags[PERMIT_MPROTECT] ? &filter_w_mprotect : &filter);
redmine authored
144 145

	SAFE (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0),			return -1);
redmine authored
146
	SAFE (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, filter_p),	return -1);
redmine authored
147 148 149 150 151

	return 0;
}
#endif

redmine authored
152
int (*privileged_fork_execvp)(const char *file, char *const argv[]);
redmine authored
153
int (*privileged_kill_child)(pid_t pid, int sig, char ignoreerrors);
redmine authored
154 155

#ifdef CAPABILITIES_SUPPORT
redmine authored
156
pid_t		helper_pid = 0;
redmine authored
157 158 159 160 161 162 163 164
pthread_t	privileged_thread;
pthread_mutex_t	*pthread_mutex_privileged_p;
pthread_mutex_t	*pthread_mutex_action_signal_p;
pthread_mutex_t	*pthread_mutex_action_entrance_p;
pthread_mutex_t	*pthread_mutex_runner_p;
pthread_cond_t	*pthread_cond_privileged_p;
pthread_cond_t	*pthread_cond_action_p;
pthread_cond_t	*pthread_cond_runner_p;
redmine authored
165

redmine authored
166
# ifdef READWRITE_SIGNALLING
redmine authored
167 168 169 170
int priv_read_fd;
int priv_write_fd;
int nonp_read_fd;
int nonp_write_fd;
redmine authored
171
# endif
redmine authored
172

redmine authored
173 174 175
enum privileged_action {
	PA_UNKNOWN = 0,

redmine authored
176 177
	PA_SETUP,

redmine authored
178 179 180 181 182 183 184 185 186 187 188 189 190
	PA_DIE,

	PA_FTS_OPEN,
	PA_FTS_READ,
	PA_FTS_CLOSE,

	PA_INOTIFY_INIT,
	PA_INOTIFY_INIT1,
	PA_INOTIFY_ADD_WATCH,
	PA_INOTIFY_RM_WATCH,

	PA_FORK_EXECVP,

redmine authored
191
	PA_KILL_CHILD,
redmine authored
192 193

	PA_CLSYNC_CGROUP_DEINIT,
redmine authored
194 195

	PA_WAITPID,
redmine authored
196 197
};

redmine authored
198
struct pa_fts_open_arg {
redmine authored
199 200 201
	char		_path_argv[MAXARGUMENTS+1][PATH_MAX];
	char		*path_argv[MAXARGUMENTS+1];
	char *const	*path_argv_p;
redmine authored
202 203 204
	int options;
	int (*compar)(const FTSENT **, const FTSENT **);
};
redmine authored
205

redmine authored
206 207
struct pa_inotify_add_watch_arg {
	int fd;
redmine authored
208 209
	char		 pathname[PATH_MAX];
	const char	*pathname_p;
redmine authored
210 211
	uint32_t mask;
};
redmine authored
212

redmine authored
213 214 215 216 217 218
struct pa_inotify_rm_watch_arg {
	int fd;
	int wd;
};

struct pa_fork_execvp_arg {
redmine authored
219 220 221 222 223
	char		 file[PATH_MAX];
	const char	*file_p;
	char		_argv[MAXARGUMENTS+1][BUFSIZ];
	char		*argv[MAXARGUMENTS+1];
	char *const	*argv_p;
redmine authored
224 225 226 227 228
};

struct pa_kill_child_arg {
	pid_t pid;
	int   signal;
redmine authored
229
	char  ignoreerrors;
redmine authored
230 231
};

redmine authored
232 233 234 235 236 237 238
struct pa_waitpid_arg {
	pid_t  pid;
	int    status;
	int    options;
};

struct pa_arg {
redmine authored
239 240 241 242 243
	struct pa_fts_open_arg		 fts_open;
	struct pa_inotify_add_watch_arg	 inotify_add_watch;
	struct pa_inotify_rm_watch_arg	 inotify_rm_watch;
	struct pa_fork_execvp_arg	 fork_execvp;
	struct pa_kill_child_arg	 kill_child;
redmine authored
244
	struct pa_waitpid_arg		 waitpid;
redmine authored
245 246 247 248 249 250
	void				*void_v;
	ctx_t				*ctx_p;
	uint32_t			 uint32_v;
};

# ifdef HL_LOCKS
redmine authored
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
enum highload_lock_id {
	HLLOCK_HANDLER = 0,

	HLLOCK_MAX
};
typedef enum highlock_lock_id hllockid_t;

enum highlock_lock_state {
	HLLS_UNREADY	= 0x00,
	HLLS_READY	= 0x01,
	HLLS_FALLBACK	= 0x02,
	HLLS_SIGNAL	= 0x04,
	HLLS_GOTSIGNAL	= 0x08,
	HLLS_WORKING	= 0x10,
};
typedef enum highlock_lock_state hllock_state_t;

redmine authored
268
struct hl_lock {
redmine authored
269 270 271 272 273
	volatile int			locallock_hl_setstate_ifstate;
	volatile int			enabled;
	volatile int			count_wait[HLLOCK_MAX];
	volatile int			count_signal[HLLOCK_MAX];
	volatile hllock_state_t		state[HLLOCK_MAX];
redmine authored
274
#  ifdef HL_LOCK_TRIES_AUTO
redmine authored
275 276 277 278
	volatile unsigned long		tries[PC_MAX];
	volatile unsigned long		count[PC_MAX];
	volatile unsigned long		delay[PC_MAX];
	volatile double			tries_step[PC_MAX];
redmine authored
279

redmine authored
280
#   define				tries_cur tries[callid]
redmine authored
281
#  else
redmine authored
282 283
	volatile unsigned long		tries;
#   define				tries_cur tries
redmine authored
284
#  endif
redmine authored
285
};
redmine authored
286
# endif
redmine authored
287

redmine authored
288 289 290 291 292
struct pa_fts_read_ret {
	FTSENT		ftsent;
	char		fts_accpath[PATH_MAX];
	char		fts_path[PATH_MAX];
	char		fts_name[PATH_MAX];
redmine authored
293
};
redmine authored
294
struct pa_ret {
redmine authored
295 296
	struct stat		stat;
	struct pa_fts_read_ret	fts_read;
redmine authored
297
};
redmine authored
298
struct cmd {
redmine authored
299
	volatile struct pa_arg		 arg;
redmine authored
300 301
	volatile enum privileged_action	 action;
# ifdef HL_LOCKS
redmine authored
302
	volatile unsigned long		 hl_lock_tries;
redmine authored
303
# endif
redmine authored
304
};
redmine authored
305
struct cmd_ret {
redmine authored
306
	volatile struct pa_ret		 ret_buf;
redmine authored
307 308 309 310 311 312 313 314
	volatile void			*ret;
	volatile int			 _errno;
};
volatile struct cmd		*cmd_p;
volatile struct cmd_ret		*cmd_ret_p;
# ifdef HL_LOCKS
volatile struct hl_lock		*hl_lock_p;
# endif
redmine authored
315

redmine authored
316
# ifdef HL_LOCKS
redmine authored
317 318 319
static inline void hl_lock_init(volatile struct hl_lock *hl_lock_p) {
	debug(10, "");
	hl_lock_p->enabled = 1;
redmine authored
320 321 322 323
#  ifdef HL_LOCK_TRIES_AUTO
	int i;
	i = 0;
	while (i < PC_MAX) {
redmine authored
324 325 326
		hl_lock_p->tries[i]		= HL_LOCK_TRIES_INITIAL;
		hl_lock_p->delay[i]		= ((unsigned long)~0)>>2;
		hl_lock_p->tries_step[i]	= HL_LOCK_AUTO_K;
redmine authored
327 328 329
		i++;
	}
#  else
redmine authored
330
	hl_lock_p->tries	= HL_LOCK_TRIES_INITIAL;
redmine authored
331 332 333
#  endif
	return;
}
redmine authored
334
# endif
redmine authored
335

redmine authored
336 337 338 339 340
struct pa_options {
	synchandler_args_t args[SHARGS_MAX];
	char *label;
	char *exithookfile;
	char *preexithookfile;
redmine authored
341 342
	char *permitted_hookfile[MAXPERMITTEDHOOKFILES+1];
	int   permitted_hookfiles;
redmine authored
343
	int   isprocsplitting;
redmine authored
344
	int   shm_mprotect;
redmine authored
345 346
};

redmine authored
347
FTS *(*_privileged_fts_open)		(
redmine authored
348 349 350
		char * const *path_argv,
		int options,
		int (*compar)(const FTSENT **, const FTSENT **)
redmine authored
351
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
352
		, int callid
redmine authored
353
# endif
redmine authored
354 355 356 357
	);

FTSENT *(*_privileged_fts_read)		(
		FTS *ftsp
redmine authored
358
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
359
		, int callid
redmine authored
360
# endif
redmine authored
361 362 363 364
	);

int (*_privileged_fts_close)		(
		FTS *ftsp
redmine authored
365
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
366
		, int callid
redmine authored
367
# endif
redmine authored
368 369
	);

redmine authored
370 371
int (*_privileged_inotify_init)		();
int (*_privileged_inotify_init1)	(int flags);
redmine authored
372

redmine authored
373
int (*_privileged_inotify_add_watch)	(
redmine authored
374 375 376
		int fd,
		const char *pathname,
		uint32_t mask
redmine authored
377
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
378
		, int callid
redmine authored
379
# endif
redmine authored
380 381
	);

redmine authored
382
int (*_privileged_inotify_rm_watch)	(
redmine authored
383 384 385 386
		int fd,
		int wd
	);

redmine authored
387
int (*_privileged_clsync_cgroup_deinit)	(ctx_t *ctx_p);
redmine authored
388

redmine authored
389
pid_t (*_privileged_waitpid)		(pid_t pid, int *status, int options);
redmine authored
390

redmine authored
391 392 393 394 395 396 397
int cap_enable(__u32 caps) {
	debug(1, "Enabling Linux capabilities 0x%x", caps);
	struct __user_cap_header_struct	cap_hdr = {0};
	struct __user_cap_data_struct	cap_dat = {0};

	cap_hdr.version = _LINUX_CAPABILITY_VERSION;
	if (capget(&cap_hdr, &cap_dat) < 0) {
Barak A. Pearlmutter authored
398
		error("Cannot get capabilities with capget()");
redmine authored
399 400 401 402 403 404 405 406 407 408 409 410 411 412
		return errno;
	}

	debug(3, "old: cap.eff == 0x%04x; new: cap.eff == 0x%04x", cap_dat.effective, cap_dat.effective|caps);
	cap_dat.effective |= caps;

	if (capset(&cap_hdr, &cap_dat) < 0) {
		error("Cannot set capabilities with capset().");
		return errno;
	}

	return 0;
}

redmine authored
413
int cap_drop(ctx_t *ctx_p, __u32 caps) {
Barak A. Pearlmutter authored
414
	debug(1, "Dropping all Linux capabilities but 0x%x", caps);
redmine authored
415 416 417 418 419 420

	struct __user_cap_header_struct	cap_hdr = {0};
	struct __user_cap_data_struct	cap_dat = {0};

	cap_hdr.version = _LINUX_CAPABILITY_VERSION;
	if (capget(&cap_hdr, &cap_dat) < 0) {
Barak A. Pearlmutter authored
421
		error_or_debug((ctx_p->flags[CAP_PRESERVE] != CAP_PRESERVE_TRY) ? -1 : 3, "Cannot get capabilities with capget()");
redmine authored
422 423
		return errno;
	}
redmine authored
424 425
	debug(3, "old: cap.eff == 0x%04x; cap.prm == 0x%04x; cap.inh == 0x%04x.",
		cap_dat.effective, cap_dat.permitted, cap_dat.inheritable);
redmine authored
426

redmine authored
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
	switch (ctx_p->flags[CAPS_INHERIT]) {
		case CI_PERMITTED:
			cap_dat.inheritable = cap_dat.permitted;
			break;
		case CI_DONTTOUCH:
			break;
		case CI_CLSYNC:
			cap_dat.inheritable = caps;
			break;
		case CI_EMPTY:
			cap_dat.inheritable = 0;
			break;
	}
	cap_dat.effective  = caps;
	cap_dat.permitted  = caps;
redmine authored
442

redmine authored
443
	debug(3, "new: cap.eff == 0x%04x; cap.prm == 0x%04x; cap.inh == 0x%04x.",
redmine authored
444 445 446
		cap_dat.effective, cap_dat.permitted, cap_dat.inheritable);

	if (capset(&cap_hdr, &cap_dat) < 0) {
redmine authored
447
		error_or_debug((ctx_p->flags[CAP_PRESERVE] != CAP_PRESERVE_TRY) ? -1 : 3, "Cannot set capabilities with capset().");
redmine authored
448 449 450 451 452 453
		return errno;
	}

	return 0;
}

redmine authored
454
#endif
redmine authored
455
int __privileged_kill_child_itself(pid_t child_pid, int signal, char ignoreerrors) {
redmine authored
456 457 458 459 460
	// Checking if it's a child
	if (waitpid(child_pid, NULL, WNOHANG)>=0) {
		debug(3, "Sending signal %u to child process with pid %u.",
			signal, child_pid);
		if (kill(child_pid, signal)) {
redmine authored
461 462
			if (!ignoreerrors)
				error("Got error while kill(%u, %u)", child_pid, signal);
redmine authored
463 464 465
			return errno;
		}

redmine authored
466
		waitpid_timed(child_pid, NULL, SLEEP_SECONDS, 0);
redmine authored
467 468 469 470 471
	} else
		return ENOENT;

	return 0;
}
redmine authored
472
#ifdef CAPABILITIES_SUPPORT
redmine authored
473

redmine authored
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
int pa_strcmp(const char *s1, const char *s2, int isexpanded) {
	if (isexpanded)
		return strcmp(s1, s2);

	{
		const char *s1_start = NULL;
		const char *s2_start = NULL;
		while (1) {
			while (1) {
				if (!*s1 || !*s2) {
					if (!*s1 && s1_start != NULL)
						return 0;
					return *s1 != *s2;
				}

				if (*s1 == '%') {
					s1++;
					while (*s1 && *s1 != '%') s1++;
					s1++;
					s1_start = s1;
					s2_start = s2;
					continue;
				}

				if (*s1 != *s2)
					break;

				s1++;
				s2++;
			}

			if (s2_start == NULL)
				break;

			s2_start++;
			s1 = s1_start;
			s2 = s2_start;
		}

		return *s1 != *s2;
	}
}

redmine authored
517
int privileged_execvp_check_arguments(struct pa_options *opts, const char *u_file, char *const *u_argv) {
redmine authored
518 519
	int a_i;
	size_t u_argc;
redmine authored
520 521
	synchandler_args_t *args = opts->args;
	
redmine authored
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
	debug(9, "");

	// Counting the number of arguments
	u_argc = 0;
	while (u_argv[u_argc] != NULL) u_argc++;

	a_i = 0;
	do {
		int i;
		int    argc;
		char **argv;
		char  *isexpanded;

		argc       = args[a_i].c;
		argv       = args[a_i].v;
		isexpanded = args[a_i].isexpanded;

redmine authored
539
		debug(8, "Checking the number of arguments: %i <> %i", argc, u_argc);
redmine authored
540 541 542
		if (argc != u_argc)
			continue;

redmine authored
543 544
		critical_on (!argc);

redmine authored
545
		debug(8, "Checking the execution file: \"%s\" <> \"%s\"; isexpanded == %i", argv[0], u_file, isexpanded[0]);
redmine authored
546 547 548 549
		if (pa_strcmp(argv[0], u_file, isexpanded[0])) {
			debug(1, "The file to be executed didn't match (argv[0] != u_file): \"%s\" != \"%s\"", argv[0], u_file);
			break;
		}
redmine authored
550

redmine authored
551
		debug(8, "Checking arguments");
redmine authored
552 553
		i = 1;
		while (i < argc) {
redmine authored
554 555
			if (pa_strcmp(argv[i], u_argv[i], isexpanded[i])) {
				debug(1, "An argument #%i didn't match (argv[%i] != u_argv[%i]): \"%s\" != \"%s\"", argv[i], argv[i]);
redmine authored
556
				break;
redmine authored
557
			}
redmine authored
558 559 560 561 562 563 564 565 566 567
			i++;
		}

		// All arguments right?
		if (i == argc)
			break;

		// No? Ok the next "shargs".
	} while (++a_i < SHARGS_MAX);

redmine authored
568 569
	if (a_i < SHARGS_MAX)
		return 0;
redmine authored
570

redmine authored
571 572 573 574
	if (u_argc == 2) {
		int i;

		if ((opts->exithookfile != NULL) || (opts->preexithookfile != NULL)) {
redmine authored
575 576 577 578 579 580 581 582 583 584
			if (!strcmp(opts->label, u_argv[1])) {
				if (opts->exithookfile != NULL)
					if (!strcmp(opts->exithookfile,    u_file))
						return 0;
				if (opts->preexithookfile != NULL)
					if (!strcmp(opts->preexithookfile, u_file))
						return 0;
			}
		}

redmine authored
585
		i = 0;
redmine authored
586 587 588 589 590 591 592
		while (i < opts->permitted_hookfiles) {
			if (!strcmp(opts->permitted_hookfile[i], u_file))
				return 0;
			i++;
		}
	}

redmine authored
593
	debug(1, "a_i == %i; SHARGS_MAX == %i; u_argc == %i", SHARGS_MAX, a_i, u_argc);
redmine authored
594 595
	critical("Arguments are wrong. This should happend only on hacking attack.");
	return EPERM;
redmine authored
596 597
}

redmine authored
598 599
int pa_setup(struct pa_options *opts, ctx_t *ctx_p, uid_t *exec_uid_p, gid_t *exec_gid_p) {
	synchandler_args_t *args = opts->args;
redmine authored
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
	int a_i;

	a_i = 0;
	do {
		int i, argc_s;
		char **argv_s, **argv_d, *isex_s, *isex_d;

		argc_s = ctx_p->synchandler_args[a_i].c;
		argv_s = ctx_p->synchandler_args[a_i].v;
		isex_s = ctx_p->synchandler_args[a_i].isexpanded;
		argv_d = args[a_i].v;
		isex_d = args[a_i].isexpanded;

		if (argc_s >= MAXARGUMENTS)
			critical("Too many arguments");

		if (argc_s < 1)
redmine authored
617
			continue;
redmine authored
618

redmine authored
619
		argv_d[0] = strdup_protect(ctx_p->handlerfpath, PROT_READ);
redmine authored
620 621 622

		i = 0;
		while (i < argc_s) {
redmine authored
623
			argv_d[i+1] = strdup_protect(argv_s[i], PROT_READ);
redmine authored
624 625 626 627 628 629 630 631 632 633 634 635 636
			isex_d[i+1] = isex_s[i];
			i++;
		}
		i++;
		argv_d[i] = NULL;
		args[a_i].c = i;

		a_i++;
	} while (++a_i < SHARGS_MAX);

	*exec_uid_p = ctx_p->synchandler_uid;
	*exec_gid_p = ctx_p->synchandler_gid;

redmine authored
637
	opts->label = strdup_protect(ctx_p->label, PROT_READ);
redmine authored
638
	if (ctx_p->exithookfile != NULL)
redmine authored
639
		opts->exithookfile = strdup_protect(ctx_p->exithookfile, PROT_READ);
redmine authored
640
	if (ctx_p->preexithookfile != NULL)
redmine authored
641
		opts->preexithookfile = strdup_protect(ctx_p->preexithookfile, PROT_READ);
redmine authored
642

redmine authored
643 644 645 646
	{
		int i = 0;

		while (i < ctx_p->permitted_hookfiles) {
redmine authored
647
			opts->permitted_hookfile[i] = strdup_protect(ctx_p->permitted_hookfile[i], PROT_READ);
redmine authored
648 649 650 651 652
			i++;
		}
		opts->permitted_hookfile[i] = NULL;
		opts->permitted_hookfiles   = i;
	}
redmine authored
653

redmine authored
654 655 656
	return 0;
}

redmine authored
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
int pa_unsetup(struct pa_options *opts) {
	free(opts->exithookfile);
	free(opts->preexithookfile);
	free(opts->label);

	{
		int a_i = 0;
		do {
			int i;

			i = 0;
			while (i < opts->args[a_i].c) {
				free(opts->args[a_i].v[i]);
				i++;
			}
		} while(++a_i < SHARGS_MAX);
	}

	{
		int i = 0;
		while (i < opts->permitted_hookfiles) {
			free(opts->permitted_hookfile[i]);
			i++;
		}
	}

	return 0;
}

redmine authored
686
# ifdef HL_LOCKS
redmine authored
687 688

static inline int hl_isanswered(int lockid) {
redmine authored
689
	return hl_lock_p->count_wait[lockid] == hl_lock_p->count_signal[lockid]+1;
redmine authored
690 691 692
}

static inline int hl_isready(int lockid) {
redmine authored
693
	return hl_lock_p->count_wait[lockid] == hl_lock_p->count_signal[lockid];
redmine authored
694 695 696
}

static inline void hl_setstate(int lockid, hllock_state_t stateid) {
redmine authored
697
	g_atomic_int_set(&hl_lock_p->state[lockid], stateid);
redmine authored
698 699 700
}

int hl_setstate_ifstate(int lockid, hllock_state_t stateid_new, hllock_state_t stateid_old_mask) {
redmine authored
701
	volatile int *local_lock_p = &hl_lock_p->locallock_hl_setstate_ifstate;
redmine authored
702
	debug(90, "%i, 0x%o, 0x%o", lockid, stateid_new, stateid_old_mask);
redmine authored
703

redmine authored
704
	if (*local_lock_p)
redmine authored
705 706
		return 0;

redmine authored
707 708 709 710 711
	debug(92, "%i", *local_lock_p);
	g_atomic_int_inc(local_lock_p);
	debug(92, "%i", *local_lock_p);
	if (*local_lock_p != 1) {
		g_atomic_int_dec_and_test(local_lock_p);
redmine authored
712 713 714
		return 0;
	}

redmine authored
715
	if (!(hl_lock_p->state[lockid]&stateid_old_mask)) {
redmine authored
716
		g_atomic_int_dec_and_test(local_lock_p);
redmine authored
717 718 719
		return 0;
	}

redmine authored
720
	debug(50, "success");
redmine authored
721
	g_atomic_int_set(&hl_lock_p->state[lockid], stateid_new);
redmine authored
722 723
	g_atomic_int_dec_and_test(local_lock_p);
#undef local_lock
redmine authored
724 725 726
	return 1;
}

redmine authored
727 728
static inline int hl_wait(
		int lockid
redmine authored
729
#  ifdef HL_LOCK_TRIES_AUTO
redmine authored
730
		, unsigned long hl_lock_tries
redmine authored
731
#  endif
redmine authored
732
) {
redmine authored
733 734 735 736
	volatile long try = 0;

	debug(15, "");

redmine authored
737
	while (hl_lock_p->state[lockid] == HLLS_GOTSIGNAL);
redmine authored
738 739
	while (!hl_isready(lockid));
	hl_setstate(lockid, HLLS_READY);
redmine authored
740
	hl_lock_p->count_wait[lockid]++;
redmine authored
741

redmine authored
742
	while (try++ < hl_lock_tries)
redmine authored
743
		if (hl_lock_p->state[lockid] == HLLS_SIGNAL) {
redmine authored
744 745 746 747 748 749
			hl_setstate(lockid, HLLS_GOTSIGNAL);
			debug(15, "got signal");
			return 1;
		}

	while (!hl_setstate_ifstate(lockid, HLLS_FALLBACK, HLLS_READY|HLLS_SIGNAL));
redmine authored
750
	debug(14, "fallback: hl_lock_p->count_wait[%u] == %u; hl_lock_p->count_signal[%u] = %u", lockid, hl_lock_p->count_wait[lockid], lockid, hl_lock_p->count_signal[lockid]);
redmine authored
751 752 753 754 755
	return 0;
}

static inline int hl_signal(int lockid) {
	debug(15, "%u", lockid);
redmine authored
756
	hl_lock_p->count_signal[lockid]++;
redmine authored
757 758

	if (hl_setstate_ifstate(lockid, HLLS_SIGNAL, HLLS_READY)) {
redmine authored
759 760
		while (hl_lock_p->state[lockid] != HLLS_GOTSIGNAL) {
			if (hl_lock_p->state[lockid] == HLLS_FALLBACK) {
redmine authored
761 762 763
				debug(15, "fallback");
				return 0;
			}
redmine authored
764
			debug(95, "state == %i != %i, %i", hl_lock_p->state[lockid], HLLS_GOTSIGNAL, HLLS_FALLBACK);
redmine authored
765
		}
redmine authored
766 767 768 769 770 771 772 773 774
		debug(15, "the signal is sent");
		hl_setstate(lockid, HLLS_WORKING);
		return 1;
	}

	debug(15, "not ready");
	return 0;
}

redmine authored
775 776 777 778 779 780
void hl_shutdown(int lockid) {
	debug(1, "");

#  ifdef PARANOID
	critical_on (HLLOCK_MAX != 1);	// TODO: do this on compile time (not on running time)
#   ifdef HL_LOCK_TRIES_AUTO
redmine authored
781
	memset((void *)hl_lock_p->tries, 0, sizeof(hl_lock_p->tries));
redmine authored
782
#   else
redmine authored
783
	hl_lock_p->tries = 0;
redmine authored
784 785
#   endif
#  endif
redmine authored
786 787
	hl_lock_p->state[lockid]	 = HLLS_FALLBACK;
	hl_lock_p->enabled 	 = 0;
redmine authored
788 789 790 791

	return;
}

redmine authored
792
# endif
redmine authored
793

redmine authored
794
static int helper_isalive_cache;
redmine authored
795
static inline int helper_isalive_proc() {
redmine authored
796 797 798 799 800 801 802 803 804 805
	int rc;
	debug(12, "helper_pid == %u", helper_pid);

	if ((rc=waitpid(helper_pid, NULL, WNOHANG))>=0)
		return helper_isalive_cache=1;

	debug(1, "waitpid(%u, NULL, WNOHANG) => %i", helper_pid, rc);

	return helper_isalive_cache=0;
}
redmine authored
806 807 808 809 810 811 812 813 814 815 816 817 818 819
static inline int helper_isalive_thread() {
	int rc;
	debug(12, "");

	if ((rc=pthread_kill(privileged_thread, 0)))
		return helper_isalive_cache=0;

	debug(1, "pthread_kill(privileged_thread, 0) => %i", helper_pid, rc);

	return helper_isalive_cache=1;
}
static inline int helper_isalive() {
	return helper_pid ? helper_isalive_proc() : helper_isalive_thread();
}
redmine authored
820 821

int privileged_check() {
redmine authored
822 823
	if (helper_pid)
		critical_on(!helper_isalive_proc());
redmine authored
824 825 826 827
	return 0;
}

int privileged_handler(ctx_t *ctx_p)
redmine authored
828
{
redmine authored
829
# ifdef READWRITE_SIGNALLING
redmine authored
830
	char buf[1] = {0};
redmine authored
831 832
# else
	struct timespec wait_timeout = {0};
redmine authored
833
# endif
redmine authored
834

redmine authored
835
	int setup = 0;
redmine authored
836 837
	uid_t exec_uid = 65535;
	gid_t exec_gid = 65535;
redmine authored
838 839
	
	struct pa_options *opts;
redmine authored
840
	int use_args_check = 0;
redmine authored
841 842 843
	int helper_isrunning = 1;

	opts  = calloc_align(1, sizeof(*opts));
redmine authored
844 845 846 847 848 849
	opts->isprocsplitting = (ctx_p->flags[SPLITTING] == SM_PROCESS);
	opts->shm_mprotect    =  ctx_p->flags[SHM_MPROTECT];

	if (opts->isprocsplitting) {
		sigset_t sigset;
		sigemptyset(&sigset);
redmine authored
850 851 852 853

/*	Do not uncomment this. This causes handler closing on any terminal
	signal to parent process. In turn it causes: https://github.com/xaionaro/clsync/issues/104

redmine authored
854 855 856 857 858
		sigaddset(&sigset, SIGALRM);
		sigaddset(&sigset, SIGHUP);
		sigaddset(&sigset, SIGQUIT);
		sigaddset(&sigset, SIGTERM);
		sigaddset(&sigset, SIGINT);
redmine authored
859 860 861 862 863
*/
# ifndef __linux__
#  error There's no automatical mechanism that guarantees handler closing on non-linux. Don't use process splitting!
# endif

redmine authored
864
# ifdef __linux__
redmine authored
865
		sigaddset(&sigset, SIGCHLD);
redmine authored
866
# endif
redmine authored
867
		critical_on(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL));
redmine authored
868 869

# ifndef __linux__
redmine authored
870
		critical_on(!parent_isalive());
redmine authored
871
# endif
redmine authored
872
	} else {
redmine authored
873
		register_blockthread();
redmine authored
874
		pthread_setname_np(pthread_self(), "clsync-helper");
redmine authored
875
	}
redmine authored
876
	cap_drop(ctx_p, ctx_p->caps);
redmine authored
877 878

	debug(2, "Syncing with the runner");
redmine authored
879
	pthread_mutex_lock(pthread_mutex_privileged_p);
redmine authored
880 881

	// Waiting for runner to get ready for signal
redmine authored
882 883
	pthread_mutex_lock(pthread_mutex_runner_p);
	pthread_mutex_unlock(pthread_mutex_runner_p);
redmine authored
884 885

	// Sending the signal that we're ready
redmine authored
886
	pthread_cond_signal(pthread_cond_runner_p);
redmine authored
887 888 889

	// The loop
	debug(2, "Running the loop");
redmine authored
890 891 892
	while (helper_isrunning) {
		errno = 0;

redmine authored
893
		// Waiting for command
redmine authored
894
		debug(10, "Waiting for command");
redmine authored
895 896 897 898
		if (opts->shm_mprotect) {
			mprotect((void *)cmd_p,		sizeof(*cmd_p),		PROT_WRITE);
			mprotect((void *)cmd_ret_p,	sizeof(*cmd_ret_p),	PROT_READ);
		}
redmine authored
899
# ifdef HL_LOCKS
redmine authored
900 901
		debug(25, "hl_lock_p->enabled == %i", hl_lock_p->enabled);
		if (!hl_lock_p->enabled || !hl_wait(
redmine authored
902
			HLLOCK_HANDLER
redmine authored
903
#  ifdef HL_LOCK_TRIES_AUTO
redmine authored
904
			, hl_lock_p->tries[HLLOCK_HANDLER]
redmine authored
905
#  endif
redmine authored
906
		)) {
redmine authored
907 908
			if (opts->isprocsplitting)
				critical_on(!parent_isalive());
redmine authored
909 910
# endif
# ifdef READWRITE_SIGNALLING
redmine authored
911
#  warning		READWRITE_SIGNALLING can cause process hanging on clsync shutdown
redmine authored
912
			read_inf(priv_read_fd, buf, 1);
redmine authored
913
# else
redmine authored
914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
			int rc;
			while (1) {
				rc = pthread_cond_timedwait(pthread_cond_privileged_p, pthread_mutex_privileged_p, &wait_timeout);
				if (!rc)
					break;
				if (rc != ETIMEDOUT)
					critical("Got error while pthread_cond_timedwait()");
				debug(10, "pthread_cond_timedwait() timed out");

				if (opts->isprocsplitting)
					exit_on(!parent_isalive());

				{
					debug(20, "Resetting wait_timeout");
					struct timeval now;
					gettimeofday(&now, NULL);
					wait_timeout.tv_sec	= now.tv_sec  + SLEEP_SECONDS;
					wait_timeout.tv_nsec	= now.tv_usec * 1000;
				}
			}
redmine authored
934 935
# endif
# ifdef HL_LOCKS
redmine authored
936
			if (hl_lock_p->enabled)
redmine authored
937
				hl_setstate(HLLOCK_HANDLER, HLLS_WORKING);
redmine authored
938
		}
redmine authored
939
# endif
redmine authored
940 941 942 943
		if (opts->shm_mprotect) {
			mprotect((void *)cmd_p,		sizeof(*cmd_p),		PROT_READ);
			mprotect((void *)cmd_ret_p,	sizeof(*cmd_ret_p),	PROT_WRITE);
		}
redmine authored
944

redmine authored
945
		debug(10, "Got command %u (euid:egid => %i:%i)", cmd_p->action, geteuid(), getegid());
redmine authored
946

redmine authored
947
		if (!setup && cmd_p->action != PA_SETUP)
redmine authored
948 949
			critical("A try to use commands before PA_SETUP");

redmine authored
950
		switch (cmd_p->action) {
redmine authored
951
			case PA_SETUP: {
redmine authored
952
				debug(20, "PA_SETUP");
redmine authored
953 954 955
				if (setup)
					critical("Double privileged_handler setuping. It can be if somebody is trying to hack the clsync.");

redmine authored
956
				critical_on(pa_setup(opts, cmd_p->arg.ctx_p, &exec_uid, &exec_gid));
redmine authored
957
				mprotect(opts, sizeof(*opts), PROT_READ);
redmine authored
958
				use_args_check = cmd_p->arg.ctx_p->flags[CHECK_EXECVP_ARGS];
redmine authored
959
				cap_drop(ctx_p, ctx_p->caps);	// TODO: Find out why "permission denined" without this line
redmine authored
960
				setup++;
redmine authored
961
				critical_on(errno);
redmine authored
962 963 964
				break;
			}
			case PA_DIE:
redmine authored
965 966
				debug(20, "PA_DIE");
				helper_isrunning = 0;
redmine authored
967 968
				break;
			case PA_FTS_OPEN: {
redmine authored
969
				volatile struct pa_fts_open_arg *arg_p = &cmd_p->arg.fts_open;
redmine authored
970 971 972 973 974 975 976
				char *const *path_argv_p =(void *)(
						opts->isprocsplitting 	?
						arg_p->path_argv 	:
						arg_p->path_argv_p
					);

				debug(20, "PA_FTS_OPEN (%s)", *path_argv_p);
redmine authored
977
				if (arg_p->compar != NULL)
redmine authored
978
					critical("\"arg_p->compar != NULL\" (arg_p->compar == %p) is forbidden because may be used to run an arbitrary code in the privileged thread.", arg_p->compar);
redmine authored
979

redmine authored
980
				cmd_ret_p->ret = fts_open(path_argv_p, arg_p->options, NULL);
redmine authored
981
				debug(21, "/PA_FTS_OPEN => %p", cmd_ret_p->ret);
redmine authored
982 983
				break;
			}
redmine authored
984 985 986 987
			case PA_FTS_READ: {
				debug(20, "PA_FTS_READ(%p)", cmd_p->arg.void_v);
				FTSENT *ret = fts_read(cmd_p->arg.void_v);
				if (ret == NULL) {
redmine authored
988 989 990 991 992 993
					cmd_ret_p->ret = NULL;
					debug(10, "cmd_ret_p->ret == NULL");
					break;
				}
				if (!opts->isprocsplitting) {	// Is the thread-splitting?
					cmd_ret_p->ret = ret;
redmine authored
994 995 996
					break;
				}

redmine authored
997 998
				{	// Is the process splitting?
					struct pa_fts_read_ret *ret_buf = (void *)&cmd_ret_p->ret_buf.fts_read;
redmine authored
999
					memcpy(&ret_buf->ftsent, ret, sizeof(ret_buf->ftsent));
redmine authored
1000
					cmd_ret_p->ret = &ret_buf->ftsent;
redmine authored
1001 1002 1003 1004 1005 1006
					debug(25, "fts_path == <%s>", ret_buf->fts_path);
					strncpy(ret_buf->fts_accpath, ret->fts_accpath, sizeof(ret_buf->fts_accpath));
					strncpy(ret_buf->fts_path,    ret->fts_path,    sizeof(ret_buf->fts_path));
					ret_buf->ftsent.fts_accpath = ret_buf->fts_accpath;
					ret_buf->ftsent.fts_path    = ret_buf->fts_path;
				}
redmine authored
1007
				break;
redmine authored
1008
			}
redmine authored
1009
			case PA_FTS_CLOSE:
redmine authored
1010
				debug(20, "PA_FTS_CLOSE");
redmine authored
1011
				cmd_ret_p->ret = (void *)(long)fts_close(cmd_p->arg.void_v);
redmine authored
1012 1013
				break;
			case PA_INOTIFY_INIT:
redmine authored
1014
				debug(20, "PA_INOTIFY_INIT");
redmine authored
1015
				cmd_ret_p->ret = (void *)(long)inotify_init();
redmine authored
1016 1017
				break;
			case PA_INOTIFY_INIT1:
redmine authored
1018
				debug(20, "PA_INOTIFY_INIT1");
redmine authored
1019
				cmd_ret_p->ret = (void *)(long)inotify_init1(cmd_p->arg.uint32_v);
redmine authored
1020 1021
				break;
			case PA_INOTIFY_ADD_WATCH: {
redmine authored
1022
				struct pa_inotify_add_watch_arg *arg_p = (void *)&cmd_p->arg.inotify_add_watch;
redmine authored
1023 1024 1025
				const char *pathname = (opts->isprocsplitting ? arg_p->pathname : arg_p->pathname_p);
				debug(20, "PA_INOTIFY_ADD_WATCH(%u, <%s>, 0x%o)", arg_p->fd, pathname, arg_p->mask);
				cmd_ret_p->ret = (void *)(long)inotify_add_watch( arg_p->fd, pathname, arg_p->mask);
redmine authored
1026 1027 1028
				break;
			}
			case PA_INOTIFY_RM_WATCH: {
redmine authored
1029 1030
				debug(20, "PA_INOTIFY_RM_WATCH");
				struct pa_inotify_rm_watch_arg *arg_p = (void *)&cmd_p->arg.inotify_rm_watch;
redmine authored
1031
				cmd_ret_p->ret = (void *)(long)inotify_rm_watch(arg_p->fd, arg_p->wd);
redmine authored
1032 1033 1034
				break;
			}
			case PA_FORK_EXECVP: {
redmine authored
1035
				struct pa_fork_execvp_arg *arg_p = (void *)&cmd_p->arg.fork_execvp;
redmine authored
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
				const char *file;
				char *const *argv;

				if (opts->isprocsplitting) {
					file = arg_p->file;
					argv = arg_p->argv;
				} else {
					file = arg_p->file_p;
					argv = arg_p->argv_p;
				}

redmine authored
1047 1048
				debug(20, "PA_FORK_EXECVP (\"%s\", argv)", file);

redmine authored
1049
				if (use_args_check)
redmine authored
1050 1051
					privileged_execvp_check_arguments(opts, file, argv);

redmine authored
1052 1053 1054 1055 1056
				pid_t pid = fork();
				switch (pid) {
					case -1: 
						error("Cannot fork().");
						break;
redmine authored
1057 1058 1059
					case  0: {
						int rc;
						(void)rc;	// anti-warning on ./configure --enable-debug=no
redmine authored
1060 1061 1062
#ifdef ANTIPARANOID
						if (ctx_p->privileged_gid != exec_gid)
#endif
redmine authored
1063 1064 1065 1066
						{
							rc = setgid(exec_gid);
							debug(4, "setgid(%u) == %i", exec_gid, rc);
						}
redmine authored
1067 1068 1069 1070

#ifdef ANTIPARANOID
						if (ctx_p->privileged_uid != exec_uid)
#endif
redmine authored
1071 1072 1073 1074
						{
							rc = setuid(exec_uid);
							debug(4, "setuid(%u) == %i", exec_uid, rc);
						}
redmine authored
1075

redmine authored
1076 1077
						debug(3, "execvp(\"%s\", argv)", file);
						exit(execvp(file, argv));
redmine authored
1078
					}
redmine authored
1079
				}
redmine authored
1080
				cmd_ret_p->ret = (void *)(long)pid;
redmine authored
1081
				debug(21, "/PA_FORK_EXECVP");
redmine authored
1082 1083
				break;
			}
redmine authored
1084
			case PA_KILL_CHILD: {
redmine authored
1085 1086
				debug(20, "PA_KILL_CHILD");
				struct pa_kill_child_arg *arg_p = (void *)&cmd_p->arg.kill_child;
redmine authored
1087
				cmd_ret_p->ret = (void *)(long)__privileged_kill_child_itself(arg_p->pid, arg_p->signal, arg_p->ignoreerrors);
redmine authored
1088 1089
				break;
			}
redmine authored
1090 1091
# ifdef CGROUP_SUPPORT
			case PA_CLSYNC_CGROUP_DEINIT: {
redmine authored
1092
				debug(20, "PA_CLSYNC_CGROUP_DEINIT");
redmine authored
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
				/*
				 * That is strange, but setuid() doesn't work
				 * without fork() in case of enabled seccomp
				 * filter. So sorry for this hacky thing.
				 *
				 * TODO: fix that.
				 */
				int status;
				pid_t pid = fork();
				switch (pid) {
					case -1: 
						error("Cannot fork().");
						break;
					case  0:
						debug(4, "setgid(0) == %i", setgid(0));
						debug(4, "setuid(0) == %i", setuid(0));
redmine authored
1109
						exit(clsync_cgroup_deinit(cmd_p->arg.void_v));
redmine authored
1110 1111 1112 1113 1114 1115 1116 1117 1118
				}

				if (waitpid(pid, &status, 0) != pid) {
					switch (errno) {
						case ECHILD:
							debug(2, "Child %u has already died.", pid);
							break;
						default:
							error("Cannot waitid().");
redmine authored
1119 1120
							cmd_ret_p->_errno = errno;
							cmd_ret_p->ret    = (void *)(long)errno;
redmine authored
1121 1122
					}
				}
redmine authored
1123

redmine authored
1124 1125 1126 1127
				// Return
				int exitcode = WEXITSTATUS(status);
				debug(3, "execution completed with exitcode %i", exitcode);

redmine authored
1128 1129
				cmd_ret_p->_errno = exitcode;
				cmd_ret_p->ret    = (void *)(long)exitcode;
redmine authored
1130

redmine authored
1131 1132 1133
				break;
			}
# endif
redmine authored
1134 1135 1136 1137 1138 1139
			case PA_WAITPID: {
				struct pa_waitpid_arg *arg_p = (void *)&cmd_p->arg.waitpid;
				debug(20, "PA_WAITPID(%u, 0x%o)", arg_p->pid, arg_p->options);
				cmd_ret_p->ret = (void *)(long)waitpid(arg_p->pid, &arg_p->status, arg_p->options);
				break;
			}
redmine authored
1140 1141 1142 1143
			default:
				critical("Unknown command type \"%u\". It's a buffer overflow (which means a security problem) or just an internal error.");
		}

redmine authored
1144 1145
		cmd_ret_p->_errno = errno;
		debug(10, "Result: %p, errno: %u. Sending the signal to non-privileged thread/process.", cmd_ret_p->ret, cmd_ret_p->_errno);
redmine authored
1146
# ifdef HL_LOCKS
redmine authored
1147
		if (!hl_lock_p->enabled) {
redmine authored
1148 1149 1150
# endif
# ifndef __linux__
			critical_on(!parent_isalive());
redmine authored
1151 1152
# endif
# ifdef READWRITE_SIGNALLING
redmine authored
1153
			write_inf(nonp_write_fd, buf, 1);
redmine authored
1154
# else
redmine authored
1155 1156 1157
			critical_on (pthread_mutex_lock(pthread_mutex_action_signal_p));
			critical_on (pthread_mutex_unlock(pthread_mutex_action_signal_p));
			critical_on (pthread_cond_signal(pthread_cond_action_p));
redmine authored
1158 1159
# endif
# ifdef HL_LOCKS
redmine authored
1160
		}
redmine authored
1161
# endif
redmine authored
1162 1163
	}

redmine authored
1164
	pa_unsetup(opts);
redmine authored
1165
# ifdef HL_LOCKS
redmine authored
1166
	hl_shutdown(HLLOCK_HANDLER);
redmine authored
1167
# endif
redmine authored
1168
	pthread_mutex_unlock(pthread_mutex_privileged_p);
redmine authored
1169 1170 1171 1172
	debug(2, "Finished");
	return 0;
}

redmine authored
1173
static inline int privileged_action(
redmine authored
1174
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1175
		int callid,
redmine authored
1176
# endif
redmine authored
1177 1178 1179 1180
		enum privileged_action action,
		void **ret_p
	)
{
redmine authored
1181 1182
	int rc = 0;

redmine authored
1183
# ifdef READWRITE_SIGNALLING
redmine authored
1184
	char buf[1] = {0};
redmine authored
1185 1186
# endif
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1187 1188 1189
	clock_t start_ticks;

	int isadjusting;
redmine authored
1190
# endif
redmine authored
1191
# ifdef HL_LOCKS
redmine authored
1192
	debug(10, "(%u, %p): %i", action, ret_p, hl_lock_p->enabled);
redmine authored
1193
# else
redmine authored
1194
	debug(10, "(%u, %p)",     action, ret_p);
redmine authored
1195
# endif
redmine authored
1196

redmine authored
1197 1198 1199
	pthread_mutex_lock(pthread_mutex_action_entrance_p);
# ifndef READWRITE_SIGNALLING
	debug(10, "Waiting the privileged thread/process to get prepared for signal");
redmine authored
1200
#  ifdef HL_LOCKS
redmine authored
1201
	if (hl_lock_p->enabled) {
redmine authored
1202
		while (!hl_isanswered(HLLOCK_HANDLER))
redmine authored
1203 1204
			if (!helper_isalive_cache) {
				debug(1, "The privileged thread/process is dead (#0). Ignoring the command.");
redmine authored
1205 1206 1207
				rc = ENOENT;
				goto privileged_action_end;
			}
redmine authored
1208
	} else {
redmine authored
1209
#  endif
redmine authored
1210 1211 1212
		critical_on(!helper_isalive_cache);
		pthread_mutex_lock(pthread_mutex_privileged_p);
		pthread_mutex_unlock(pthread_mutex_privileged_p);
redmine authored
1213
#  ifdef HL_LOCKS
redmine authored
1214
	}
redmine authored
1215
#  endif
redmine authored
1216
# endif
redmine authored
1217 1218
	if (!helper_isalive_cache) {
		debug(1, "The privileged thread/process is dead (#1). Ignoring the command.");
redmine authored
1219 1220
		rc = ENOENT;
		goto privileged_action_end;
redmine authored
1221 1222
	}

redmine authored
1223 1224
	cmd_p->action = action;
	debug(10, "Sending information (action == %i) to the privileged thread/process", action);
redmine authored
1225
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1226
	cmd_p->hl_lock_tries = hl_lock_p->tries[callid];
redmine authored
1227

redmine authored
1228 1229
	if ((isadjusting = hl_lock_p->enabled)) {
		isadjusting = hl_lock_p->tries[callid];
redmine authored
1230
		if (isadjusting) {
redmine authored
1231
			isadjusting = ((double)fabs(hl_lock_p->tries_step[callid]-1) > (double)HL_LOCK_AUTO_K_FINISH);
redmine authored
1232
			if (isadjusting) {
redmine authored
1233 1234
				isadjusting = !((++hl_lock_p->count[callid]) << (sizeof(hl_lock_p->count[callid])*CHAR_BIT - HL_LOCK_AUTO_INTERVAL));
				debug(11, "isadjusting == %u; hl_lock_p->tries_step[%i] == %lf; hl_lock_p->count[%i] == %lu", isadjusting, callid, hl_lock_p->tries_step[callid], callid, hl_lock_p->count[callid]);
redmine authored
1235 1236 1237 1238 1239
				if (isadjusting)
					start_ticks = clock();
			}
		}
	}
redmine authored
1240

redmine authored
1241 1242
# endif
# ifdef HL_LOCKS
redmine authored
1243 1244 1245
	if (action == PA_DIE)
		hl_lock_p->enabled = 0;

redmine authored
1246
	if (!hl_lock_p->enabled || !hl_signal(HLLOCK_HANDLER)) {
redmine authored
1247
# endif
redmine authored
1248
		critical_on(!helper_isalive_cache);
redmine authored
1249
# ifdef READWRITE_SIGNALLING
redmine authored
1250
		write_inf(priv_write_fd, buf, 1);
redmine authored
1251 1252
# else
#  ifdef HL_LOCKS
redmine authored
1253
		if (hl_lock_p->enabled) {
redmine authored
1254
			debug(10, "Waiting the privileged thread/process to get prepared for signal (by fallback)");
redmine authored
1255 1256
			critical_on (pthread_mutex_lock(pthread_mutex_privileged_p));
			critical_on (pthread_mutex_unlock(pthread_mutex_privileged_p));
redmine authored
1257
		} else
redmine authored
1258
#  endif
redmine authored
1259 1260
		critical_on (pthread_mutex_lock(pthread_mutex_action_signal_p));
		critical_on (pthread_cond_signal(pthread_cond_privileged_p));
redmine authored
1261 1262
# endif
# ifdef HL_LOCKS
redmine authored
1263
	}
redmine authored
1264
# endif
redmine authored
1265

redmine authored
1266 1267
	if (action == PA_DIE)
		goto privileged_action_end;
redmine authored
1268
	debug(10, "Waiting for the answer");
redmine authored
1269

redmine authored
1270
# ifdef HL_LOCKS
redmine authored
1271
	if (hl_lock_p->enabled) {
redmine authored
1272
		while (!hl_isanswered(HLLOCK_HANDLER))
redmine authored
1273 1274
			if (!helper_isalive_cache) {
				debug(1, "The privileged thread/process is dead (#2). Ignoring the command.");
redmine authored
1275 1276 1277
				rc = ENOENT;
				goto privileged_action_end;
			}
redmine authored
1278

redmine authored
1279
#  ifdef HL_LOCK_TRIES_AUTO
redmine authored
1280 1281
		if (isadjusting) {
			unsigned long delay = (long)clock() - (long)start_ticks;
redmine authored
1282
			long diff  = delay - hl_lock_p->delay[callid];
redmine authored
1283

redmine authored
1284
			debug(13, "diff == %li; hl_lock_p->delay[%i] == %lu; delay == %lu; delay*HL_LOCK_AUTO_THREADHOLD == %lu", diff, callid, hl_lock_p->delay[callid], delay, delay*HL_LOCK_AUTO_THREADHOLD)
redmine authored
1285

redmine authored
1286
			if (diff && ((unsigned long)labs(diff) > (unsigned long)delay*HL_LOCK_AUTO_THREADHOLD)) {
redmine authored
1287

redmine authored
1288
				if (diff > 0)
redmine authored
1289
					hl_lock_p->tries_step[callid] = 1/((hl_lock_p->tries_step[callid]-1)/HL_LOCK_AUTO_DECELERATION+1);
redmine authored
1290

redmine authored
1291
				hl_lock_p->delay[callid]  = delay;
redmine authored
1292

redmine authored
1293
				debug(12, "diff == %li; hl_lock_p->tries_step[%i] == %lf; hl_lock_p->delay[%i] == %lu", diff, callid, hl_lock_p->tries_step[callid], callid, hl_lock_p->delay[callid]);
redmine authored
1294
			}
redmine authored
1295
			hl_lock_p->tries[callid] *= hl_lock_p->tries_step[callid];
redmine authored
1296

redmine authored
1297 1298
			if (hl_lock_p->tries[callid] > HL_LOCK_AUTO_LIMIT_HIGH)
				hl_lock_p->tries[callid] = HL_LOCK_AUTO_LIMIT_HIGH;
redmine authored
1299

redmine authored
1300
			debug(14, "hl_lock_p->tries[%i] == %lu", callid, hl_lock_p->tries[callid]);
redmine authored
1301
		}
redmine authored
1302
#  endif
redmine authored
1303
	} else {
redmine authored
1304
# endif
redmine authored
1305
		critical_on(!helper_isalive_cache);
redmine authored
1306
# ifdef READWRITE_SIGNALLING
redmine authored
1307
		read_inf(nonp_read_fd, buf, 1);
redmine authored
1308
# else
redmine authored
1309
		critical_on (pthread_cond_wait(pthread_cond_action_p, pthread_mutex_action_signal_p));
redmine authored
1310 1311
# endif
# ifdef HL_LOCKS
redmine authored
1312
	}
redmine authored
1313
# endif
redmine authored
1314

redmine authored
1315
	if (ret_p != NULL)
redmine authored
1316 1317
		*ret_p = (void *)cmd_ret_p->ret;
	errno = cmd_ret_p->_errno;
redmine authored
1318

redmine authored
1319
privileged_action_end:
redmine authored
1320
	debug(10, "Unlocking pthread_mutex_action_*");
redmine authored
1321 1322
# ifndef READWRITE_SIGNALLING
#  ifdef HL_LOCKS
redmine authored
1323
	if (!hl_lock_p->enabled)
redmine authored
1324
#  endif
redmine authored
1325
		pthread_mutex_unlock(pthread_mutex_action_signal_p);
redmine authored
1326 1327
# endif

redmine authored
1328
	pthread_mutex_unlock(pthread_mutex_action_entrance_p);
redmine authored
1329

redmine authored
1330
	return rc;
redmine authored
1331 1332
}

redmine authored
1333
FTS *__privileged_fts_open_procsplit(
redmine authored
1334
		char *const *path_argv,
redmine authored
1335 1336
		int options,
		int (*compar)(const FTSENT **, const FTSENT **)
redmine authored
1337
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1338
		, int callid
redmine authored
1339
# endif
redmine authored
1340 1341
	)
{
redmine authored
1342
	void *ret = NULL;
redmine authored
1343
	int i;
redmine authored
1344

redmine authored
1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
	i = 0;
	while (path_argv[i] != NULL) {
		cmd_p->arg.fts_open.path_argv[i] = (void *)cmd_p->arg.fts_open._path_argv[i];
		debug(25, "path_argv[%i] == <%s> (%p) -> %p", i, path_argv[i], path_argv[i], cmd_p->arg.fts_open.path_argv[i]);
		strncpy(cmd_p->arg.fts_open.path_argv[i], path_argv[i], sizeof(cmd_p->arg.fts_open._path_argv[i]));
		i++;
		critical_on(i >= MAXARGUMENTS);
	}
	cmd_p->arg.fts_open.path_argv[i]	= NULL;
	cmd_p->arg.fts_open.options		= options;
	cmd_p->arg.fts_open.compar		= compar;
redmine authored
1356

redmine authored
1357
	privileged_action(
redmine authored
1358
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1359
			callid,
redmine authored
1360
# endif
redmine authored
1361 1362 1363
			PA_FTS_OPEN,
			&ret
		);
redmine authored
1364 1365 1366 1367

	return ret;
}

redmine authored
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393
FTS *__privileged_fts_open_threadsplit(
		char *const *path_argv,
		int options,
		int (*compar)(const FTSENT **, const FTSENT **)
# ifdef HL_LOCK_TRIES_AUTO
		, int callid
# endif
	)
{
	void *ret = NULL;

	cmd_p->arg.fts_open.path_argv_p		= path_argv;
	cmd_p->arg.fts_open.options		= options;
	cmd_p->arg.fts_open.compar		= compar;

	privileged_action(
# ifdef HL_LOCK_TRIES_AUTO
			callid,
# endif
			PA_FTS_OPEN,
			&ret
		);

	return ret;
}

redmine authored
1394 1395
FTSENT *__privileged_fts_read(
		FTS *ftsp
redmine authored
1396
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1397
		, int callid
redmine authored
1398
# endif
redmine authored
1399
	)
redmine authored
1400
{
redmine authored
1401
	void *ret = NULL;
redmine authored
1402
	cmd_p->arg.void_v = ftsp;
redmine authored
1403
	privileged_action(
redmine authored
1404
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1405
			callid,
redmine authored
1406
# endif
redmine authored
1407 1408 1409
			PA_FTS_READ,
			&ret
		);
redmine authored
1410 1411 1412
	return ret;
}

redmine authored
1413 1414
int __privileged_fts_close(
		FTS *ftsp
redmine authored
1415
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1416
		, int callid
redmine authored
1417
# endif
redmine authored
1418
	)
redmine authored
1419
{
redmine authored
1420
	void *ret = (void *)(long)-1;
redmine authored
1421
	cmd_p->arg.void_v = ftsp;
redmine authored
1422
	privileged_action(
redmine authored
1423
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1424
			callid,
redmine authored
1425
# endif
redmine authored
1426 1427 1428
			PA_FTS_CLOSE,
			&ret
		);
redmine authored
1429 1430 1431
	return (long)ret;
}

redmine authored
1432
int __privileged_inotify_init() {
redmine authored
1433
	void *ret = (void *)(long)-1;
redmine authored
1434
	privileged_action(
redmine authored
1435
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1436
			PC_DEFAULT,
redmine authored
1437
# endif
redmine authored
1438 1439 1440
			PA_INOTIFY_INIT,
			&ret
		);
redmine authored
1441 1442 1443
	return (long)ret;
}

redmine authored
1444
int __privileged_inotify_init1(int flags) {
redmine authored
1445
	void *ret = (void *)(long)-1;
redmine authored
1446
	cmd_p->arg.uint32_v = flags;
redmine authored
1447
	privileged_action(
redmine authored
1448
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1449
			PC_DEFAULT,
redmine authored
1450
# endif
redmine authored
1451 1452 1453
			PA_INOTIFY_INIT1,
			&ret
		);
redmine authored
1454 1455 1456
	return (long)ret;
}

redmine authored
1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484
int __privileged_inotify_add_watch_threadsplit(
		int fd,
		const char *pathname,
		uint32_t mask
# ifdef HL_LOCK_TRIES_AUTO
		, int callid
# endif
	)
{
	debug(25, "(%i, <%s>, o%o, ?)", fd, pathname, mask);
	void *ret = (void *)(long)-1;

	cmd_p->arg.inotify_add_watch.pathname_p	= pathname;
	cmd_p->arg.inotify_add_watch.fd		= fd;
	cmd_p->arg.inotify_add_watch.mask	= mask;

	privileged_action(
# ifdef HL_LOCK_TRIES_AUTO
			callid,
# endif
			PA_INOTIFY_ADD_WATCH,
			&ret
		);

	return (long)ret;
}

int __privileged_inotify_add_watch_procsplit(
redmine authored
1485 1486 1487
		int fd,
		const char *pathname,
		uint32_t mask
redmine authored
1488
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1489
		, int callid
redmine authored
1490
# endif
redmine authored
1491 1492
	)
{
redmine authored
1493
	debug(25, "(%i, <%s>, o%o, ?)", fd, pathname, mask);
redmine authored
1494
	void *ret = (void *)(long)-1;
redmine authored
1495

redmine authored
1496 1497 1498
	strncpy((void *)cmd_p->arg.inotify_add_watch.pathname, pathname, sizeof(cmd_p->arg.inotify_add_watch.pathname));
	cmd_p->arg.inotify_add_watch.fd		= fd;
	cmd_p->arg.inotify_add_watch.mask	= mask;
redmine authored
1499

redmine authored
1500
	privileged_action(
redmine authored
1501
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1502
			callid,
redmine authored
1503
# endif
redmine authored
1504 1505 1506
			PA_INOTIFY_ADD_WATCH,
			&ret
		);
redmine authored
1507 1508 1509 1510

	return (long)ret;
}

redmine authored
1511
int __privileged_inotify_rm_watch(
redmine authored
1512 1513 1514 1515
		int fd,
		int wd
	)
{
redmine authored
1516
	void *ret = (void *)(long)-1;
redmine authored
1517

redmine authored
1518 1519
	cmd_p->arg.inotify_rm_watch.fd	= fd;
	cmd_p->arg.inotify_rm_watch.wd	= wd;
redmine authored
1520

redmine authored
1521
	privileged_action(
redmine authored
1522
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1523
			PC_DEFAULT,
redmine authored
1524
# endif
redmine authored
1525 1526 1527
			PA_INOTIFY_RM_WATCH,
			&ret
		);
redmine authored
1528 1529 1530 1531

	return (long)ret;
}

redmine authored
1532
# ifdef CGROUP_SUPPORT
redmine authored
1533
int __privileged_clsync_cgroup_deinit(ctx_t *ctx_p)
redmine authored
1534 1535 1536
{
	void *ret = (void *)(long)-1;

redmine authored
1537
	cmd_p->arg.ctx_p = ctx_p;
redmine authored
1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550

	privileged_action(
#  ifdef HL_LOCK_TRIES_AUTO
			PC_DEFAULT,
#  endif
			PA_CLSYNC_CGROUP_DEINIT,
			&ret
		);

	return (long)ret;
}
# endif

redmine authored
1551
int __privileged_fork_setuid_execvp_procsplit(
redmine authored
1552 1553 1554
		const char *file,
		char *const argv[]
	)
redmine authored
1555
{
redmine authored
1556
	int i;
redmine authored
1557
	void *ret = (void *)(long)-1;
redmine authored
1558

redmine authored
1559 1560 1561 1562 1563 1564 1565 1566 1567 1568
	strncpy((void *)cmd_p->arg.fork_execvp.file, file, sizeof(cmd_p->arg.fork_execvp.file));

	i=0;
	while (argv[i] != NULL) {
		cmd_p->arg.fork_execvp.argv[i] = (void *)cmd_p->arg.fork_execvp._argv[i];
		strncpy(cmd_p->arg.fork_execvp.argv[i], argv[i], sizeof(cmd_p->arg.fork_execvp._argv[i]));
		i++;
		critical_on(i >= MAXARGUMENTS);
	}
	cmd_p->arg.fork_execvp.argv[i] = NULL;
redmine authored
1569

redmine authored
1570
	privileged_action(
redmine authored
1571
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1572
			PC_DEFAULT,
redmine authored
1573
# endif
redmine authored
1574 1575 1576
			PA_FORK_EXECVP,
			&ret
		);
redmine authored
1577 1578 1579 1580

	return (long)ret;
}

redmine authored
1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601
int __privileged_fork_setuid_execvp_threadsplit(
		const char *file,
		char *const argv[]
	)
{
	void *ret = (void *)(long)-1;

	cmd_p->arg.fork_execvp.file_p = file;
	cmd_p->arg.fork_execvp.argv_p = argv;

	privileged_action(
# ifdef HL_LOCK_TRIES_AUTO
			PC_DEFAULT,
# endif
			PA_FORK_EXECVP,
			&ret
		);

	return (long)ret;
}

redmine authored
1602
int __privileged_kill_child_wrapper(pid_t pid, int signal, char ignoreerrors)
redmine authored
1603
{
redmine authored
1604
	void *ret = (void *)(long)-1;
redmine authored
1605

redmine authored
1606 1607 1608
	cmd_p->arg.kill_child.pid          = pid;
	cmd_p->arg.kill_child.signal       = signal;
	cmd_p->arg.kill_child.ignoreerrors = ignoreerrors;
redmine authored
1609

redmine authored
1610
	privileged_action(
redmine authored
1611
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1612
			PC_DEFAULT,
redmine authored
1613
# endif
redmine authored
1614 1615
			PA_KILL_CHILD,
			&ret);
redmine authored
1616 1617 1618 1619

	return (long)ret;
}

redmine authored
1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639
pid_t __privileged_waitpid(pid_t pid, int *status, int options)
{
	void *ret = (void *)(long)-1;

	cmd_p->arg.waitpid.pid     = pid;
	cmd_p->arg.waitpid.options = options;

	privileged_action(
# ifdef HL_LOCK_TRIES_AUTO
			PC_DEFAULT,
# endif
			PA_WAITPID,
			&ret);

	if (status != NULL)
		*status = cmd_p->arg.waitpid.status;

	return (long)ret;
}

redmine authored
1640 1641
#endif

redmine authored
1642 1643 1644
uid_t __privileged_fork_execvp_uid;
gid_t __privileged_fork_execvp_gid;
int __privileged_fork_execvp(const char *file, char *const argv[])
redmine authored
1645 1646 1647 1648 1649 1650 1651
{
	debug(4, "");
	pid_t pid = fork();
	switch (pid) {
		case -1: 
			error("Cannot fork().");
			return -1;
redmine authored
1652 1653 1654 1655 1656 1657 1658 1659 1660 1661
		case  0: {
			int rc;
			(void)rc;	// anti-warning on ./configure --enable-debug=no

			rc = setgid(__privileged_fork_execvp_gid);
			debug(4, "setgid(%u) == %i", __privileged_fork_execvp_gid, rc);

			rc = setuid(__privileged_fork_execvp_uid);
			debug(4, "setuid(%u) == %i", __privileged_fork_execvp_uid, rc);

redmine authored
1662 1663 1664
			errno = 0;
			execvp(file, argv);
			exit(errno);
redmine authored
1665
		}
redmine authored
1666 1667 1668 1669 1670
	}

	return pid;
}

redmine authored
1671
#ifdef CAPABILITIES_SUPPORT
redmine authored
1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738
#define pthread_mutex_init_smart(mutex_p) _pthread_mutex_init_smart(ctx_p->flags[SPLITTING]==SM_PROCESS, mutex_p)
static inline int _pthread_mutex_init_smart(int isshared, pthread_mutex_t **mutex_p) {
	int rc;
	pthread_mutex_t *mutex, initial = PTHREAD_MUTEX_INITIALIZER;
	if (isshared)
		return pthread_mutex_init_shared(mutex_p);

	mutex = xmalloc(sizeof(*mutex));
	memcpy(mutex, &initial, sizeof(*mutex));

	rc = pthread_mutex_init(mutex, NULL);
	if (rc)
		return rc;

	*mutex_p = mutex;

	return rc;
}

#define pthread_mutex_destroy_smart(mutex_p) _pthread_mutex_destroy_smart(ctx_p->flags[SPLITTING]==SM_PROCESS, mutex_p)
static inline int _pthread_mutex_destroy_smart(int isshared, pthread_mutex_t *mutex_p) {
	int rc;
	if (isshared)
		return pthread_mutex_destroy_shared(mutex_p);

	rc = pthread_mutex_destroy(mutex_p);
	if (rc)
		return rc;

	free(mutex_p);

	return 0;
}

#define pthread_cond_init_smart(cond_p) _pthread_cond_init_smart(ctx_p->flags[SPLITTING]==SM_PROCESS, cond_p)
static inline int _pthread_cond_init_smart(int isshared, pthread_cond_t **cond_p) {
	int rc;
	pthread_cond_t *cond, initial = PTHREAD_COND_INITIALIZER;
	if (isshared)
		return pthread_cond_init_shared(cond_p);

	cond = xmalloc(sizeof(*cond));
	memcpy(cond, &initial, sizeof(*cond));

	rc = pthread_cond_init(cond, NULL);
	if (rc)
		return rc;

	*cond_p = cond;

	return rc;
}

#define pthread_cond_destroy_smart(cond_p) _pthread_cond_destroy_smart(ctx_p->flags[SPLITTING]==SM_PROCESS, cond_p)
static inline int _pthread_cond_destroy_smart(int isshared, pthread_cond_t *cond_p) {
	int rc;
	if (isshared)
		return pthread_cond_destroy_shared(cond_p);

	rc = pthread_cond_destroy(cond_p);
	if (rc)
		return rc;

	free(cond_p);

	return 0;
}
redmine authored
1739
#endif
redmine authored
1740

redmine authored
1741 1742
int privileged_init(ctx_t *ctx_p)
{
redmine authored
1743
#ifdef READWRITE_SIGNALLING
redmine authored
1744
	int pipefds[2];