Blame view

privileged.c 50.9 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
# include <sys/prctl.h>			// prctl()
# include <linux/filter.h>		// struct sock_filter
# include <linux/seccomp.h>		// SECCOMP_RET_*

redmine authored
67 68 69 70 71 72 73 74 75
# ifndef __NR_shmdt
#  ifdef __i386__
#   warning [security] Caution! __NR_shmdt is not defined. Setting it to -222.
#   define __NR_shmdt -222
#  endif
# endif


# define syscall_nr (offsetof(struct seccomp_data, nr))
redmine authored
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

/* 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
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
# define FILTER_TABLE_NONPRIV						\
	SECCOMP_ALLOW_ACCUM_SYSCALL(futex),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(inotify_init1),			\
	SECCOMP_ALLOW_ACCUM_SYSCALL(alarm),				\
	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(rt_sigreturn),			\
	SECCOMP_ALLOW_ACCUM_SYSCALL(brk),				\
	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
112
	SECCOMP_ALLOW_ACCUM_SYSCALL(shmdt),				\
redmine authored
113
	SECCOMP_ALLOW_ACCUM_SYSCALL(clone),		/* for --threading */		\
redmine authored
114 115 116
	SECCOMP_ALLOW_ACCUM_SYSCALL(set_robust_list),	/* for --threading? */		\
	SECCOMP_ALLOW_ACCUM_SYSCALL(madvise),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(exit),				\
redmine authored
117
	SECCOMP_ALLOW_ACCUM_SYSCALL(clock_gettime),			\
redmine authored
118

redmine authored
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
# ifdef __i386__
#  define FILTER_TABLE_ARCHDEPENDED			/* unused */	\
	SECCOMP_ALLOW_ACCUM_SYSCALL(fstat64),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(lstat64),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(stat64),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(time),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(mmap2),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(gettimeofday),			\
	SECCOMP_ALLOW_ACCUM_SYSCALL(_newselect),			\

# else
#  define FILTER_TABLE_ARCHDEPENDED					\
	SECCOMP_ALLOW_ACCUM_SYSCALL(fstat),		/* unused */	\
	SECCOMP_ALLOW_ACCUM_SYSCALL(lstat),				\
	SECCOMP_ALLOW_ACCUM_SYSCALL(stat),		/* unused */	\
	SECCOMP_ALLOW_ACCUM_SYSCALL(mmap),				\

# endif
redmine authored
137

redmine authored
138 139 140 141 142

/* Syscalls allowed to non-privileged thread */
static struct sock_filter filter_table[] = {
	SECCOMP_COPY_SYSCALL_TO_ACCUM,
	FILTER_TABLE_NONPRIV
redmine authored
143
	FILTER_TABLE_ARCHDEPENDED
redmine authored
144 145
	SECCOMP_DENY,
};
redmine authored
146 147 148
static struct sock_filter filter_w_mprotect_table[] = {
	SECCOMP_COPY_SYSCALL_TO_ACCUM,
	FILTER_TABLE_NONPRIV
redmine authored
149
	FILTER_TABLE_ARCHDEPENDED
redmine authored
150 151 152
	SECCOMP_ALLOW_ACCUM_SYSCALL(mprotect),
	SECCOMP_DENY,
};
redmine authored
153

redmine authored
154 155 156
int nonprivileged_seccomp_init(ctx_t *ctx_p) {
	struct sock_fprog *filter_p;
	struct sock_fprog filter = {
redmine authored
157 158 159
		.len = (unsigned short)(sizeof(filter_table)/sizeof(filter_table[0])),
		.filter = filter_table,
	};
redmine authored
160 161 162 163 164
	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
165 166
	debug(5, "enabling the seccomp");

redmine authored
167
	filter_p = (ctx_p->flags[PERMIT_MPROTECT] ? &filter_w_mprotect : &filter);
redmine authored
168 169

	SAFE (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0),			return -1);
redmine authored
170
	SAFE (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, filter_p),	return -1);
redmine authored
171 172 173 174 175

	return 0;
}
#endif

redmine authored
176
int (*privileged_fork_execvp)(const char *file, char *const argv[]);
redmine authored
177
int (*privileged_kill_child)(pid_t pid, int sig, char ignoreerrors);
redmine authored
178 179

#ifdef CAPABILITIES_SUPPORT
redmine authored
180
pid_t		helper_pid = 0;
redmine authored
181 182 183 184 185 186 187 188
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
189

redmine authored
190
# ifdef READWRITE_SIGNALLING
redmine authored
191 192 193 194
int priv_read_fd;
int priv_write_fd;
int nonp_read_fd;
int nonp_write_fd;
redmine authored
195
# endif
redmine authored
196

redmine authored
197 198 199
enum privileged_action {
	PA_UNKNOWN = 0,

redmine authored
200 201
	PA_SETUP,

redmine authored
202 203 204 205 206 207 208 209 210 211 212 213 214
	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
215
	PA_KILL_CHILD,
redmine authored
216 217

	PA_CLSYNC_CGROUP_DEINIT,
redmine authored
218 219

	PA_WAITPID,
redmine authored
220 221
};

redmine authored
222
struct pa_fts_open_arg {
redmine authored
223 224 225
	char		_path_argv[MAXARGUMENTS+1][PATH_MAX];
	char		*path_argv[MAXARGUMENTS+1];
	char *const	*path_argv_p;
redmine authored
226 227 228
	int options;
	int (*compar)(const FTSENT **, const FTSENT **);
};
redmine authored
229

redmine authored
230 231
struct pa_inotify_add_watch_arg {
	int fd;
redmine authored
232 233
	char		 pathname[PATH_MAX];
	const char	*pathname_p;
redmine authored
234 235
	uint32_t mask;
};
redmine authored
236

redmine authored
237 238 239 240 241 242
struct pa_inotify_rm_watch_arg {
	int fd;
	int wd;
};

struct pa_fork_execvp_arg {
redmine authored
243 244 245 246 247
	char		 file[PATH_MAX];
	const char	*file_p;
	char		_argv[MAXARGUMENTS+1][BUFSIZ];
	char		*argv[MAXARGUMENTS+1];
	char *const	*argv_p;
redmine authored
248 249 250 251 252
};

struct pa_kill_child_arg {
	pid_t pid;
	int   signal;
redmine authored
253
	char  ignoreerrors;
redmine authored
254 255
};

redmine authored
256 257 258 259 260 261 262
struct pa_waitpid_arg {
	pid_t  pid;
	int    status;
	int    options;
};

struct pa_arg {
redmine authored
263 264 265 266 267
	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
268
	struct pa_waitpid_arg		 waitpid;
redmine authored
269 270 271 272 273 274
	void				*void_v;
	ctx_t				*ctx_p;
	uint32_t			 uint32_v;
};

# ifdef HL_LOCKS
redmine authored
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
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
292
struct hl_lock {
redmine authored
293 294 295 296 297
	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
298
#  ifdef HL_LOCK_TRIES_AUTO
redmine authored
299 300 301 302
	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
303

redmine authored
304
#   define				tries_cur tries[callid]
redmine authored
305
#  else
redmine authored
306 307
	volatile unsigned long		tries;
#   define				tries_cur tries
redmine authored
308
#  endif
redmine authored
309
};
redmine authored
310
# endif
redmine authored
311

redmine authored
312 313 314 315 316
struct pa_fts_read_ret {
	FTSENT		ftsent;
	char		fts_accpath[PATH_MAX];
	char		fts_path[PATH_MAX];
	char		fts_name[PATH_MAX];
redmine authored
317
};
redmine authored
318
struct pa_ret {
redmine authored
319 320
	struct stat		stat;
	struct pa_fts_read_ret	fts_read;
redmine authored
321
};
redmine authored
322
struct cmd {
redmine authored
323
	volatile struct pa_arg		 arg;
redmine authored
324 325
	volatile enum privileged_action	 action;
# ifdef HL_LOCKS
redmine authored
326
	volatile unsigned long		 hl_lock_tries;
redmine authored
327
# endif
redmine authored
328
};
redmine authored
329
struct cmd_ret {
redmine authored
330
	volatile struct pa_ret		 ret_buf;
redmine authored
331 332
	volatile void			*ret;
	volatile int			 _errno;
redmine authored
333
	volatile int			 processing_longcmd;
redmine authored
334 335 336 337 338 339
};
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
340

redmine authored
341
# ifdef HL_LOCKS
redmine authored
342 343 344
static inline void hl_lock_init(volatile struct hl_lock *hl_lock_p) {
	debug(10, "");
	hl_lock_p->enabled = 1;
redmine authored
345 346 347 348
#  ifdef HL_LOCK_TRIES_AUTO
	int i;
	i = 0;
	while (i < PC_MAX) {
redmine authored
349 350 351
		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
352 353 354
		i++;
	}
#  else
redmine authored
355
	hl_lock_p->tries	= HL_LOCK_TRIES_INITIAL;
redmine authored
356 357 358
#  endif
	return;
}
redmine authored
359
# endif
redmine authored
360

redmine authored
361 362 363 364 365
struct pa_options {
	synchandler_args_t args[SHARGS_MAX];
	char *label;
	char *exithookfile;
	char *preexithookfile;
redmine authored
366 367
	char *permitted_hookfile[MAXPERMITTEDHOOKFILES+1];
	int   permitted_hookfiles;
redmine authored
368
	int   isprocsplitting;
redmine authored
369
	int   shm_mprotect;
redmine authored
370 371
};

redmine authored
372
FTS *(*_privileged_fts_open)		(
redmine authored
373 374 375
		char * const *path_argv,
		int options,
		int (*compar)(const FTSENT **, const FTSENT **)
redmine authored
376
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
377
		, int callid
redmine authored
378
# endif
redmine authored
379 380 381 382
	);

FTSENT *(*_privileged_fts_read)		(
		FTS *ftsp
redmine authored
383
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
384
		, int callid
redmine authored
385
# endif
redmine authored
386 387 388 389
	);

int (*_privileged_fts_close)		(
		FTS *ftsp
redmine authored
390
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
391
		, int callid
redmine authored
392
# endif
redmine authored
393 394
	);

redmine authored
395 396
int (*_privileged_inotify_init)		();
int (*_privileged_inotify_init1)	(int flags);
redmine authored
397

redmine authored
398
int (*_privileged_inotify_add_watch)	(
redmine authored
399 400 401
		int fd,
		const char *pathname,
		uint32_t mask
redmine authored
402
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
403
		, int callid
redmine authored
404
# endif
redmine authored
405 406
	);

redmine authored
407
int (*_privileged_inotify_rm_watch)	(
redmine authored
408 409 410 411
		int fd,
		int wd
	);

redmine authored
412
int (*_privileged_clsync_cgroup_deinit)	(ctx_t *ctx_p);
redmine authored
413

redmine authored
414
pid_t (*_privileged_waitpid)		(pid_t pid, int *status, int options);
redmine authored
415

redmine authored
416 417 418 419 420 421 422
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
423
		error("Cannot get capabilities with capget()");
redmine authored
424 425 426 427 428 429 430 431 432 433 434 435 436 437
		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
438
int cap_drop(ctx_t *ctx_p, __u32 caps) {
Barak A. Pearlmutter authored
439
	debug(1, "Dropping all Linux capabilities but 0x%x", caps);
redmine authored
440 441 442 443 444 445

	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
446
		error_or_debug((ctx_p->flags[CAP_PRESERVE] != CAP_PRESERVE_TRY) ? -1 : 3, "Cannot get capabilities with capget()");
redmine authored
447 448
		return errno;
	}
redmine authored
449 450
	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
451

redmine authored
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
	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
467

redmine authored
468
	debug(3, "new: cap.eff == 0x%04x; cap.prm == 0x%04x; cap.inh == 0x%04x.",
redmine authored
469 470 471
		cap_dat.effective, cap_dat.permitted, cap_dat.inheritable);

	if (capset(&cap_hdr, &cap_dat) < 0) {
redmine authored
472
		error_or_debug((ctx_p->flags[CAP_PRESERVE] != CAP_PRESERVE_TRY) ? -1 : 3, "Cannot set capabilities with capset().");
redmine authored
473 474 475 476 477 478
		return errno;
	}

	return 0;
}

redmine authored
479
#endif
redmine authored
480
int __privileged_kill_child_itself(pid_t child_pid, int signal, char ignoreerrors) {
redmine authored
481 482 483 484 485
	// 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
486 487
			if (!ignoreerrors)
				error("Got error while kill(%u, %u)", child_pid, signal);
redmine authored
488 489 490
			return errno;
		}

redmine authored
491
		waitpid_timed(child_pid, NULL, SLEEP_SECONDS, 0);
redmine authored
492 493 494 495 496
	} else
		return ENOENT;

	return 0;
}
redmine authored
497
#ifdef CAPABILITIES_SUPPORT
redmine authored
498

redmine authored
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
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
542
int privileged_execvp_check_arguments(struct pa_options *opts, const char *u_file, char *const *u_argv) {
redmine authored
543 544
	int a_i;
	size_t u_argc;
redmine authored
545 546
	synchandler_args_t *args = opts->args;
	
redmine authored
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
	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
564
		debug(8, "Checking the number of arguments: %i <> %i", argc, u_argc);
redmine authored
565 566 567
		if (argc != u_argc)
			continue;

redmine authored
568 569
		critical_on (!argc);

redmine authored
570
		debug(8, "Checking the execution file: \"%s\" <> \"%s\"; isexpanded == %i", argv[0], u_file, isexpanded[0]);
redmine authored
571 572 573 574
		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
575

redmine authored
576
		debug(8, "Checking arguments");
redmine authored
577 578
		i = 1;
		while (i < argc) {
redmine authored
579 580
			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
581
				break;
redmine authored
582
			}
redmine authored
583 584 585 586 587 588 589 590 591 592
			i++;
		}

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

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

redmine authored
593 594
	if (a_i < SHARGS_MAX)
		return 0;
redmine authored
595

redmine authored
596 597 598 599
	if (u_argc == 2) {
		int i;

		if ((opts->exithookfile != NULL) || (opts->preexithookfile != NULL)) {
redmine authored
600 601 602 603 604 605 606 607 608 609
			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
610
		i = 0;
redmine authored
611 612 613 614 615 616 617
		while (i < opts->permitted_hookfiles) {
			if (!strcmp(opts->permitted_hookfile[i], u_file))
				return 0;
			i++;
		}
	}

redmine authored
618
	debug(1, "a_i == %i; SHARGS_MAX == %i; u_argc == %i", SHARGS_MAX, a_i, u_argc);
redmine authored
619 620
	critical("Arguments are wrong. This should happend only on hacking attack.");
	return EPERM;
redmine authored
621 622
}

redmine authored
623 624
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
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
	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
642
			continue;
redmine authored
643

redmine authored
644
		argv_d[0] = strdup_protect(ctx_p->handlerfpath, PROT_READ);
redmine authored
645 646 647

		i = 0;
		while (i < argc_s) {
redmine authored
648
			argv_d[i+1] = strdup_protect(argv_s[i], PROT_READ);
redmine authored
649 650 651 652 653 654 655 656 657 658 659 660 661
			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
662
	opts->label = strdup_protect(ctx_p->label, PROT_READ);
redmine authored
663
	if (ctx_p->exithookfile != NULL)
redmine authored
664
		opts->exithookfile = strdup_protect(ctx_p->exithookfile, PROT_READ);
redmine authored
665
	if (ctx_p->preexithookfile != NULL)
redmine authored
666
		opts->preexithookfile = strdup_protect(ctx_p->preexithookfile, PROT_READ);
redmine authored
667

redmine authored
668 669 670 671
	{
		int i = 0;

		while (i < ctx_p->permitted_hookfiles) {
redmine authored
672
			opts->permitted_hookfile[i] = strdup_protect(ctx_p->permitted_hookfile[i], PROT_READ);
redmine authored
673 674 675 676 677
			i++;
		}
		opts->permitted_hookfile[i] = NULL;
		opts->permitted_hookfiles   = i;
	}
redmine authored
678

redmine authored
679 680 681
	return 0;
}

redmine authored
682
int pa_unsetup(struct pa_options *opts) {
redmine authored
683 684 685
#ifdef TODO_FIX
	// segfaults: gdb --args clsync -K lxc-brother-atomic-sync -l jabber --pre-exit-hook wlxc-stop --chroot= --pivot-root=off -d9 -b0 -Ystderr

redmine authored
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
	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++;
		}
	}
redmine authored
710
#endif
redmine authored
711 712 713 714

	return 0;
}

redmine authored
715
# ifdef HL_LOCKS
redmine authored
716 717

static inline int hl_isanswered(int lockid) {
redmine authored
718
	return hl_lock_p->count_wait[lockid] == hl_lock_p->count_signal[lockid]+1;
redmine authored
719 720 721
}

static inline int hl_isready(int lockid) {
redmine authored
722
	return hl_lock_p->count_wait[lockid] == hl_lock_p->count_signal[lockid];
redmine authored
723 724 725
}

static inline void hl_setstate(int lockid, hllock_state_t stateid) {
redmine authored
726
	g_atomic_int_set(&hl_lock_p->state[lockid], stateid);
redmine authored
727 728 729
}

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

redmine authored
733
	if (*local_lock_p)
redmine authored
734 735
		return 0;

redmine authored
736 737 738 739 740
	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
741 742 743
		return 0;
	}

redmine authored
744
	if (!(hl_lock_p->state[lockid]&stateid_old_mask)) {
redmine authored
745
		g_atomic_int_dec_and_test(local_lock_p);
redmine authored
746 747 748
		return 0;
	}

redmine authored
749
	debug(50, "success");
redmine authored
750
	g_atomic_int_set(&hl_lock_p->state[lockid], stateid_new);
redmine authored
751 752
	g_atomic_int_dec_and_test(local_lock_p);
#undef local_lock
redmine authored
753 754 755
	return 1;
}

redmine authored
756 757
static inline int hl_wait(
		int lockid
redmine authored
758
#  ifdef HL_LOCK_TRIES_AUTO
redmine authored
759
		, unsigned long hl_lock_tries
redmine authored
760
#  endif
redmine authored
761
) {
redmine authored
762 763 764 765
	volatile long try = 0;

	debug(15, "");

redmine authored
766
	while (hl_lock_p->state[lockid] == HLLS_GOTSIGNAL);
redmine authored
767 768
	while (!hl_isready(lockid));
	hl_setstate(lockid, HLLS_READY);
redmine authored
769
	hl_lock_p->count_wait[lockid]++;
redmine authored
770

redmine authored
771
	while (try++ < hl_lock_tries)
redmine authored
772
		if (hl_lock_p->state[lockid] == HLLS_SIGNAL) {
redmine authored
773 774 775 776 777 778
			hl_setstate(lockid, HLLS_GOTSIGNAL);
			debug(15, "got signal");
			return 1;
		}

	while (!hl_setstate_ifstate(lockid, HLLS_FALLBACK, HLLS_READY|HLLS_SIGNAL));
redmine authored
779
	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
780 781 782 783 784
	return 0;
}

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

	if (hl_setstate_ifstate(lockid, HLLS_SIGNAL, HLLS_READY)) {
redmine authored
788 789
		while (hl_lock_p->state[lockid] != HLLS_GOTSIGNAL) {
			if (hl_lock_p->state[lockid] == HLLS_FALLBACK) {
redmine authored
790 791 792
				debug(15, "fallback");
				return 0;
			}
redmine authored
793
			debug(95, "state == %i != %i, %i", hl_lock_p->state[lockid], HLLS_GOTSIGNAL, HLLS_FALLBACK);
redmine authored
794
		}
redmine authored
795 796 797 798 799 800 801 802 803
		debug(15, "the signal is sent");
		hl_setstate(lockid, HLLS_WORKING);
		return 1;
	}

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

redmine authored
804 805 806 807 808 809
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
810
	memset((void *)hl_lock_p->tries, 0, sizeof(hl_lock_p->tries));
redmine authored
811
#   else
redmine authored
812
	hl_lock_p->tries = 0;
redmine authored
813 814
#   endif
#  endif
redmine authored
815 816
	hl_lock_p->state[lockid]	 = HLLS_FALLBACK;
	hl_lock_p->enabled 	 = 0;
redmine authored
817 818 819 820

	return;
}

redmine authored
821
# endif
redmine authored
822

redmine authored
823
static int helper_isalive_cache;
redmine authored
824
static inline int helper_isalive_proc() {
redmine authored
825 826 827 828 829 830 831 832 833 834
	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
835 836 837 838 839 840 841 842 843 844 845 846 847 848
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
849 850

int privileged_check() {
redmine authored
851 852
	if (helper_pid)
		critical_on(!helper_isalive_proc());
redmine authored
853 854 855 856
	return 0;
}

int privileged_handler(ctx_t *ctx_p)
redmine authored
857
{
redmine authored
858
# ifdef READWRITE_SIGNALLING
redmine authored
859
	char buf[1] = {0};
redmine authored
860 861
# else
	struct timespec wait_timeout = {0};
redmine authored
862
# endif
redmine authored
863

redmine authored
864
	int setup = 0;
redmine authored
865 866
	uid_t exec_uid = 65535;
	gid_t exec_gid = 65535;
redmine authored
867 868
	
	struct pa_options *opts;
redmine authored
869
	int use_args_check = 0;
redmine authored
870 871 872
	int helper_isrunning = 1;

	opts  = calloc_align(1, sizeof(*opts));
redmine authored
873 874 875 876 877 878
	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
879 880 881 882

/*	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
883 884 885 886 887
		sigaddset(&sigset, SIGALRM);
		sigaddset(&sigset, SIGHUP);
		sigaddset(&sigset, SIGQUIT);
		sigaddset(&sigset, SIGTERM);
		sigaddset(&sigset, SIGINT);
redmine authored
888 889 890 891 892
*/
# ifndef __linux__
#  error There's no automatical mechanism that guarantees handler closing on non-linux. Don't use process splitting!
# endif

redmine authored
893
# ifdef __linux__
redmine authored
894
		sigaddset(&sigset, SIGCHLD);
redmine authored
895
# endif
redmine authored
896
		critical_on(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL));
redmine authored
897 898

# ifndef __linux__
redmine authored
899
		critical_on(!parent_isalive());
redmine authored
900
# endif
redmine authored
901
	} else {
redmine authored
902
		register_blockthread();
redmine authored
903
		pthread_setname_np(pthread_self(), "clsync-helper");
redmine authored
904
	}
redmine authored
905
	cap_drop(ctx_p, ctx_p->caps);
redmine authored
906 907

	debug(2, "Syncing with the runner");
redmine authored
908
	pthread_mutex_lock(pthread_mutex_privileged_p);
redmine authored
909 910

	// Waiting for runner to get ready for signal
redmine authored
911 912
	pthread_mutex_lock(pthread_mutex_runner_p);
	pthread_mutex_unlock(pthread_mutex_runner_p);
redmine authored
913 914

	// Sending the signal that we're ready
redmine authored
915
	pthread_cond_signal(pthread_cond_runner_p);
redmine authored
916 917 918

	// The loop
	debug(2, "Running the loop");
redmine authored
919 920 921
	while (helper_isrunning) {
		errno = 0;

redmine authored
922
		// Waiting for command
redmine authored
923
		debug(10, "Waiting for command");
redmine authored
924 925 926 927
		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
928
# ifdef HL_LOCKS
redmine authored
929 930
		debug(25, "hl_lock_p->enabled == %i", hl_lock_p->enabled);
		if (!hl_lock_p->enabled || !hl_wait(
redmine authored
931
			HLLOCK_HANDLER
redmine authored
932
#  ifdef HL_LOCK_TRIES_AUTO
redmine authored
933
			, hl_lock_p->tries[HLLOCK_HANDLER]
redmine authored
934
#  endif
redmine authored
935
		)) {
redmine authored
936 937
			if (opts->isprocsplitting)
				critical_on(!parent_isalive());
redmine authored
938 939
# endif
# ifdef READWRITE_SIGNALLING
redmine authored
940
#  warning		READWRITE_SIGNALLING can cause process hanging on clsync shutdown
redmine authored
941
			read_inf(priv_read_fd, buf, 1);
redmine authored
942
# else
redmine authored
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
			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
963 964
# endif
# ifdef HL_LOCKS
redmine authored
965
			if (hl_lock_p->enabled)
redmine authored
966
				hl_setstate(HLLOCK_HANDLER, HLLS_WORKING);
redmine authored
967
		}
redmine authored
968
# endif
redmine authored
969 970 971 972
		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
973

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

redmine authored
976
		if (!setup && cmd_p->action != PA_SETUP)
redmine authored
977 978
			critical("A try to use commands before PA_SETUP");

redmine authored
979
		switch (cmd_p->action) {
redmine authored
980
			case PA_SETUP: {
redmine authored
981
				debug(20, "PA_SETUP");
redmine authored
982 983 984
				if (setup)
					critical("Double privileged_handler setuping. It can be if somebody is trying to hack the clsync.");

redmine authored
985
				critical_on(pa_setup(opts, cmd_p->arg.ctx_p, &exec_uid, &exec_gid));
redmine authored
986
				mprotect(opts, sizeof(*opts), PROT_READ);
redmine authored
987
				use_args_check = cmd_p->arg.ctx_p->flags[CHECK_EXECVP_ARGS];
redmine authored
988
				cap_drop(ctx_p, ctx_p->caps);	// TODO: Find out why "permission denined" without this line
redmine authored
989
				setup++;
redmine authored
990
				critical_on(errno);
redmine authored
991 992 993
				break;
			}
			case PA_DIE:
redmine authored
994 995
				debug(20, "PA_DIE");
				helper_isrunning = 0;
redmine authored
996 997
				break;
			case PA_FTS_OPEN: {
redmine authored
998
				volatile struct pa_fts_open_arg *arg_p = &cmd_p->arg.fts_open;
redmine authored
999 1000 1001 1002 1003 1004 1005
				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
1006
				if (arg_p->compar != NULL)
redmine authored
1007
					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
1008

redmine authored
1009
				cmd_ret_p->ret = fts_open(path_argv_p, arg_p->options, NULL);
redmine authored
1010
				debug(21, "/PA_FTS_OPEN => %p", cmd_ret_p->ret);
redmine authored
1011 1012
				break;
			}
redmine authored
1013 1014 1015 1016
			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
1017 1018 1019 1020 1021 1022
					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
1023 1024 1025
					break;
				}

redmine authored
1026 1027
				{	// Is the process splitting?
					struct pa_fts_read_ret *ret_buf = (void *)&cmd_ret_p->ret_buf.fts_read;
redmine authored
1028
					memcpy(&ret_buf->ftsent, ret, sizeof(ret_buf->ftsent));
redmine authored
1029
					cmd_ret_p->ret = &ret_buf->ftsent;
redmine authored
1030 1031 1032 1033 1034 1035
					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
1036
				break;
redmine authored
1037
			}
redmine authored
1038
			case PA_FTS_CLOSE:
redmine authored
1039
				debug(20, "PA_FTS_CLOSE");
redmine authored
1040
				cmd_ret_p->ret = (void *)(long)fts_close(cmd_p->arg.void_v);
redmine authored
1041 1042
				break;
			case PA_INOTIFY_INIT:
redmine authored
1043
				debug(20, "PA_INOTIFY_INIT");
redmine authored
1044
				cmd_ret_p->ret = (void *)(long)inotify_init();
redmine authored
1045 1046
				break;
			case PA_INOTIFY_INIT1:
redmine authored
1047
				debug(20, "PA_INOTIFY_INIT1");
redmine authored
1048
				cmd_ret_p->ret = (void *)(long)inotify_init1(cmd_p->arg.uint32_v);
redmine authored
1049 1050
				break;
			case PA_INOTIFY_ADD_WATCH: {
redmine authored
1051
				struct pa_inotify_add_watch_arg *arg_p = (void *)&cmd_p->arg.inotify_add_watch;
redmine authored
1052 1053 1054
				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
1055 1056 1057
				break;
			}
			case PA_INOTIFY_RM_WATCH: {
redmine authored
1058 1059
				debug(20, "PA_INOTIFY_RM_WATCH");
				struct pa_inotify_rm_watch_arg *arg_p = (void *)&cmd_p->arg.inotify_rm_watch;
redmine authored
1060
				cmd_ret_p->ret = (void *)(long)inotify_rm_watch(arg_p->fd, arg_p->wd);
redmine authored
1061 1062 1063
				break;
			}
			case PA_FORK_EXECVP: {
redmine authored
1064
				struct pa_fork_execvp_arg *arg_p = (void *)&cmd_p->arg.fork_execvp;
redmine authored
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
				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
1076 1077
				debug(20, "PA_FORK_EXECVP (\"%s\", argv)", file);

redmine authored
1078
				if (use_args_check)
redmine authored
1079 1080
					privileged_execvp_check_arguments(opts, file, argv);

redmine authored
1081 1082 1083 1084 1085
				pid_t pid = fork();
				switch (pid) {
					case -1: 
						error("Cannot fork().");
						break;
redmine authored
1086 1087 1088
					case  0: {
						int rc;
						(void)rc;	// anti-warning on ./configure --enable-debug=no
redmine authored
1089 1090 1091
#ifdef ANTIPARANOID
						if (ctx_p->privileged_gid != exec_gid)
#endif
redmine authored
1092 1093 1094 1095
						{
							rc = setgid(exec_gid);
							debug(4, "setgid(%u) == %i", exec_gid, rc);
						}
redmine authored
1096 1097 1098 1099

#ifdef ANTIPARANOID
						if (ctx_p->privileged_uid != exec_uid)
#endif
redmine authored
1100 1101 1102 1103
						{
							rc = setuid(exec_uid);
							debug(4, "setuid(%u) == %i", exec_uid, rc);
						}
redmine authored
1104

redmine authored
1105 1106
						debug(3, "execvp(\"%s\", argv)", file);
						exit(execvp(file, argv));
redmine authored
1107
					}
redmine authored
1108
				}
redmine authored
1109
				cmd_ret_p->ret = (void *)(long)pid;
redmine authored
1110
				debug(21, "/PA_FORK_EXECVP");
redmine authored
1111 1112
				break;
			}
redmine authored
1113
			case PA_KILL_CHILD: {
redmine authored
1114 1115
				debug(20, "PA_KILL_CHILD");
				struct pa_kill_child_arg *arg_p = (void *)&cmd_p->arg.kill_child;
redmine authored
1116
				cmd_ret_p->ret = (void *)(long)__privileged_kill_child_itself(arg_p->pid, arg_p->signal, arg_p->ignoreerrors);
redmine authored
1117 1118
				break;
			}
redmine authored
1119 1120
# ifdef CGROUP_SUPPORT
			case PA_CLSYNC_CGROUP_DEINIT: {
redmine authored
1121
				debug(20, "PA_CLSYNC_CGROUP_DEINIT");
redmine authored
1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
				/*
				 * 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
1138
						exit(clsync_cgroup_deinit(cmd_p->arg.void_v));
redmine authored
1139 1140 1141 1142 1143 1144 1145 1146 1147
				}

				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
1148 1149
							cmd_ret_p->_errno = errno;
							cmd_ret_p->ret    = (void *)(long)errno;
redmine authored
1150 1151
					}
				}
redmine authored
1152

redmine authored
1153 1154 1155 1156
				// Return
				int exitcode = WEXITSTATUS(status);
				debug(3, "execution completed with exitcode %i", exitcode);

redmine authored
1157 1158
				cmd_ret_p->_errno = exitcode;
				cmd_ret_p->ret    = (void *)(long)exitcode;
redmine authored
1159

redmine authored
1160 1161 1162
				break;
			}
# endif
redmine authored
1163 1164 1165
			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);
redmine authored
1166 1167 1168
				cmd_ret_p->processing_longcmd = 1;
				cmd_ret_p->ret                = (void *)(long)waitpid(arg_p->pid, &arg_p->status, arg_p->options);
				cmd_ret_p->processing_longcmd = 0;
redmine authored
1169 1170
				break;
			}
redmine authored
1171 1172 1173 1174
			default:
				critical("Unknown command type \"%u\". It's a buffer overflow (which means a security problem) or just an internal error.");
		}

redmine authored
1175 1176
		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
1177
# ifdef HL_LOCKS
redmine authored
1178
		if (!hl_lock_p->enabled) {
redmine authored
1179 1180 1181
# endif
# ifndef __linux__
			critical_on(!parent_isalive());
redmine authored
1182 1183
# endif
# ifdef READWRITE_SIGNALLING
redmine authored
1184
			write_inf(nonp_write_fd, buf, 1);
redmine authored
1185
# else
redmine authored
1186 1187 1188
			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
1189 1190
# endif
# ifdef HL_LOCKS
redmine authored
1191
		}
redmine authored
1192
# endif
redmine authored
1193 1194
	}

redmine authored
1195
	pa_unsetup(opts);
redmine authored
1196
# ifdef HL_LOCKS
redmine authored
1197
	hl_shutdown(HLLOCK_HANDLER);
redmine authored
1198
# endif
redmine authored
1199
	pthread_mutex_unlock(pthread_mutex_privileged_p);
redmine authored
1200 1201 1202 1203
	debug(2, "Finished");
	return 0;
}

redmine authored
1204
static inline int privileged_action(
redmine authored
1205
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1206
		int callid,
redmine authored
1207
# endif
redmine authored
1208 1209 1210 1211
		enum privileged_action action,
		void **ret_p
	)
{
redmine authored
1212 1213
	int rc = 0;

redmine authored
1214
# ifdef READWRITE_SIGNALLING
redmine authored
1215
	char buf[1] = {0};
redmine authored
1216 1217
# endif
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1218 1219 1220
	clock_t start_ticks;

	int isadjusting;
redmine authored
1221
# endif
redmine authored
1222
# ifdef HL_LOCKS
redmine authored
1223
	debug(10, "(%u, %p): %i", action, ret_p, hl_lock_p->enabled);
redmine authored
1224
# else
redmine authored
1225
	debug(10, "(%u, %p)",     action, ret_p);
redmine authored
1226
# endif
redmine authored
1227

redmine authored
1228 1229 1230
	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
1231
#  ifdef HL_LOCKS
redmine authored
1232
	if (hl_lock_p->enabled) {
redmine authored
1233 1234 1235
		long long counter = 0;

		while (!hl_isanswered(HLLOCK_HANDLER)) {
redmine authored
1236 1237
			if (!helper_isalive_cache) {
				debug(1, "The privileged thread/process is dead (#0). Ignoring the command.");
redmine authored
1238 1239 1240
				rc = ENOENT;
				goto privileged_action_end;
			}
redmine authored
1241
			if (cmd_ret_p->processing_longcmd && ++counter > HL_LOCK_NONPRIV_TRIES)
redmine authored
1242 1243
				sleep(SLEEP_SECONDS);
		}
redmine authored
1244
	} else {
redmine authored
1245
#  endif
redmine authored
1246 1247 1248
		critical_on(!helper_isalive_cache);
		pthread_mutex_lock(pthread_mutex_privileged_p);
		pthread_mutex_unlock(pthread_mutex_privileged_p);
redmine authored
1249
#  ifdef HL_LOCKS
redmine authored
1250
	}
redmine authored
1251
#  endif
redmine authored
1252
# endif
redmine authored
1253 1254
	if (!helper_isalive_cache) {
		debug(1, "The privileged thread/process is dead (#1). Ignoring the command.");
redmine authored
1255 1256
		rc = ENOENT;
		goto privileged_action_end;
redmine authored
1257 1258
	}

redmine authored
1259 1260
	cmd_p->action = action;
	debug(10, "Sending information (action == %i) to the privileged thread/process", action);
redmine authored
1261
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1262
	cmd_p->hl_lock_tries = hl_lock_p->tries[callid];
redmine authored
1263

redmine authored
1264 1265
	if ((isadjusting = hl_lock_p->enabled)) {
		isadjusting = hl_lock_p->tries[callid];
redmine authored
1266
		if (isadjusting) {
redmine authored
1267
			isadjusting = ((double)fabs(hl_lock_p->tries_step[callid]-1) > (double)HL_LOCK_AUTO_K_FINISH);
redmine authored
1268
			if (isadjusting) {
redmine authored
1269 1270
				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
1271 1272 1273 1274 1275
				if (isadjusting)
					start_ticks = clock();
			}
		}
	}
redmine authored
1276

redmine authored
1277 1278
# endif
# ifdef HL_LOCKS
redmine authored
1279 1280 1281
	if (action == PA_DIE)
		hl_lock_p->enabled = 0;

redmine authored
1282
	if (!hl_lock_p->enabled || !hl_signal(HLLOCK_HANDLER)) {
redmine authored
1283
# endif
redmine authored
1284
		critical_on(!helper_isalive_cache);
redmine authored
1285
# ifdef READWRITE_SIGNALLING
redmine authored
1286
		write_inf(priv_write_fd, buf, 1);
redmine authored
1287 1288
# else
#  ifdef HL_LOCKS
redmine authored
1289
		if (hl_lock_p->enabled) {
redmine authored
1290
			debug(10, "Waiting the privileged thread/process to get prepared for signal (by fallback)");
redmine authored
1291 1292
			critical_on (pthread_mutex_lock(pthread_mutex_privileged_p));
			critical_on (pthread_mutex_unlock(pthread_mutex_privileged_p));
redmine authored
1293
		} else
redmine authored
1294
#  endif
redmine authored
1295 1296
		critical_on (pthread_mutex_lock(pthread_mutex_action_signal_p));
		critical_on (pthread_cond_signal(pthread_cond_privileged_p));
redmine authored
1297 1298
# endif
# ifdef HL_LOCKS
redmine authored
1299
	}
redmine authored
1300
# endif
redmine authored
1301

redmine authored
1302 1303
	if (action == PA_DIE)
		goto privileged_action_end;
redmine authored
1304
	debug(10, "Waiting for the answer");
redmine authored
1305

redmine authored
1306
# ifdef HL_LOCKS
redmine authored
1307
	if (hl_lock_p->enabled) {
redmine authored
1308 1309 1310
		long long counter = 0;

		while (!hl_isanswered(HLLOCK_HANDLER)) {
redmine authored
1311 1312
			if (!helper_isalive_cache) {
				debug(1, "The privileged thread/process is dead (#2). Ignoring the command.");
redmine authored
1313 1314 1315
				rc = ENOENT;
				goto privileged_action_end;
			}
redmine authored
1316
			if (cmd_ret_p->processing_longcmd && ++counter > HL_LOCK_NONPRIV_TRIES)
redmine authored
1317 1318
				sleep(SLEEP_SECONDS);
		}
redmine authored
1319

redmine authored
1320
#  ifdef HL_LOCK_TRIES_AUTO
redmine authored
1321 1322
		if (isadjusting) {
			unsigned long delay = (long)clock() - (long)start_ticks;
redmine authored
1323
			long diff  = delay - hl_lock_p->delay[callid];
redmine authored
1324

redmine authored
1325
			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
1326

redmine authored
1327
			if (diff && ((unsigned long)labs(diff) > (unsigned long)delay*HL_LOCK_AUTO_THREADHOLD)) {
redmine authored
1328

redmine authored
1329
				if (diff > 0)
redmine authored
1330
					hl_lock_p->tries_step[callid] = 1/((hl_lock_p->tries_step[callid]-1)/HL_LOCK_AUTO_DECELERATION+1);
redmine authored
1331

redmine authored
1332
				hl_lock_p->delay[callid]  = delay;
redmine authored
1333

redmine authored
1334
				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
1335
			}
redmine authored
1336
			hl_lock_p->tries[callid] *= hl_lock_p->tries_step[callid];
redmine authored
1337

redmine authored
1338 1339
			if (hl_lock_p->tries[callid] > HL_LOCK_AUTO_LIMIT_HIGH)
				hl_lock_p->tries[callid] = HL_LOCK_AUTO_LIMIT_HIGH;
redmine authored
1340

redmine authored
1341
			debug(14, "hl_lock_p->tries[%i] == %lu", callid, hl_lock_p->tries[callid]);
redmine authored
1342
		}
redmine authored
1343
#  endif
redmine authored
1344
	} else {
redmine authored
1345
# endif
redmine authored
1346
		critical_on(!helper_isalive_cache);
redmine authored
1347
# ifdef READWRITE_SIGNALLING
redmine authored
1348
		read_inf(nonp_read_fd, buf, 1);
redmine authored
1349
# else
redmine authored
1350
		critical_on (pthread_cond_wait(pthread_cond_action_p, pthread_mutex_action_signal_p));
redmine authored
1351 1352
# endif
# ifdef HL_LOCKS
redmine authored
1353
	}
redmine authored
1354
# endif
redmine authored
1355

redmine authored
1356
	if (ret_p != NULL)
redmine authored
1357 1358
		*ret_p = (void *)cmd_ret_p->ret;
	errno = cmd_ret_p->_errno;
redmine authored
1359

redmine authored
1360
privileged_action_end:
redmine authored
1361
	debug(10, "Unlocking pthread_mutex_action_*");
redmine authored
1362 1363
# ifndef READWRITE_SIGNALLING
#  ifdef HL_LOCKS
redmine authored
1364
	if (!hl_lock_p->enabled)
redmine authored
1365
#  endif
redmine authored
1366
		pthread_mutex_unlock(pthread_mutex_action_signal_p);
redmine authored
1367 1368
# endif

redmine authored
1369
	pthread_mutex_unlock(pthread_mutex_action_entrance_p);
redmine authored
1370

redmine authored
1371
	return rc;
redmine authored
1372 1373
}

redmine authored
1374
FTS *__privileged_fts_open_procsplit(
redmine authored
1375
		char *const *path_argv,
redmine authored
1376 1377
		int options,
		int (*compar)(const FTSENT **, const FTSENT **)
redmine authored
1378
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1379
		, int callid
redmine authored
1380
# endif
redmine authored
1381 1382
	)
{
redmine authored
1383
	void *ret = NULL;
redmine authored
1384
	int i;
redmine authored
1385

redmine authored
1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396
	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
1397

redmine authored
1398
	privileged_action(
redmine authored
1399
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1400
			callid,
redmine authored
1401
# endif
redmine authored
1402 1403 1404
			PA_FTS_OPEN,
			&ret
		);
redmine authored
1405 1406 1407 1408

	return ret;
}

redmine authored
1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434
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
1435 1436
FTSENT *__privileged_fts_read(
		FTS *ftsp
redmine authored
1437
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1438
		, int callid
redmine authored
1439
# endif
redmine authored
1440
	)
redmine authored
1441
{
redmine authored
1442
	void *ret = NULL;
redmine authored
1443
	cmd_p->arg.void_v = ftsp;
redmine authored
1444
	privileged_action(
redmine authored
1445
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1446
			callid,
redmine authored
1447
# endif
redmine authored
1448 1449 1450
			PA_FTS_READ,
			&ret
		);
redmine authored
1451 1452 1453
	return ret;
}

redmine authored
1454 1455
int __privileged_fts_close(
		FTS *ftsp
redmine authored
1456
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1457
		, int callid
redmine authored
1458
# endif
redmine authored
1459
	)
redmine authored
1460
{
redmine authored
1461
	void *ret = (void *)(long)-1;
redmine authored
1462
	cmd_p->arg.void_v = ftsp;
redmine authored
1463
	privileged_action(
redmine authored
1464
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1465
			callid,
redmine authored
1466
# endif
redmine authored
1467 1468 1469
			PA_FTS_CLOSE,
			&ret
		);
redmine authored
1470 1471 1472
	return (long)ret;
}

redmine authored
1473
int __privileged_inotify_init() {
redmine authored
1474
	void *ret = (void *)(long)-1;
redmine authored
1475
	privileged_action(
redmine authored
1476
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1477
			PC_DEFAULT,
redmine authored
1478
# endif
redmine authored
1479 1480 1481
			PA_INOTIFY_INIT,
			&ret
		);
redmine authored
1482 1483 1484
	return (long)ret;
}

redmine authored
1485
int __privileged_inotify_init1(int flags) {
redmine authored
1486
	void *ret = (void *)(long)-1;
redmine authored
1487
	cmd_p->arg.uint32_v = flags;
redmine authored
1488
	privileged_action(
redmine authored
1489
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1490
			PC_DEFAULT,
redmine authored
1491
# endif
redmine authored
1492 1493 1494
			PA_INOTIFY_INIT1,
			&ret
		);
redmine authored
1495 1496 1497
	return (long)ret;
}

redmine authored
1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525
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
1526 1527 1528
		int fd,
		const char *pathname,
		uint32_t mask
redmine authored
1529
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1530
		, int callid
redmine authored
1531
# endif
redmine authored
1532 1533
	)
{
redmine authored
1534
	debug(25, "(%i, <%s>, o%o, ?)", fd, pathname, mask);
redmine authored
1535
	void *ret = (void *)(long)-1;
redmine authored
1536

redmine authored
1537 1538 1539
	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
1540

redmine authored
1541
	privileged_action(
redmine authored
1542
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1543
			callid,
redmine authored
1544
# endif
redmine authored
1545 1546 1547
			PA_INOTIFY_ADD_WATCH,
			&ret
		);
redmine authored
1548 1549 1550 1551

	return (long)ret;
}

redmine authored
1552
int __privileged_inotify_rm_watch(
redmine authored
1553 1554 1555 1556
		int fd,
		int wd
	)
{
redmine authored
1557
	void *ret = (void *)(long)-1;
redmine authored
1558

redmine authored
1559 1560
	cmd_p->arg.inotify_rm_watch.fd	= fd;
	cmd_p->arg.inotify_rm_watch.wd	= wd;
redmine authored
1561

redmine authored
1562
	privileged_action(
redmine authored
1563
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1564
			PC_DEFAULT,
redmine authored
1565
# endif
redmine authored
1566 1567 1568
			PA_INOTIFY_RM_WATCH,
			&ret
		);
redmine authored
1569 1570 1571 1572

	return (long)ret;
}

redmine authored
1573
# ifdef CGROUP_SUPPORT
redmine authored
1574
int __privileged_clsync_cgroup_deinit(ctx_t *ctx_p)
redmine authored
1575 1576 1577
{
	void *ret = (void *)(long)-1;

redmine authored
1578
	cmd_p->arg.ctx_p = ctx_p;
redmine authored
1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591

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

	return (long)ret;
}
# endif

redmine authored
1592
int __privileged_fork_setuid_execvp_procsplit(
redmine authored
1593 1594 1595
		const char *file,
		char *const argv[]
	)
redmine authored
1596
{
redmine authored
1597
	int i;
redmine authored
1598
	void *ret = (void *)(long)-1;
redmine authored
1599

redmine authored
1600 1601 1602 1603 1604 1605 1606 1607 1608 1609
	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
1610

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

	return (long)ret;
}

redmine authored
1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642
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
1643
int __privileged_kill_child_wrapper(pid_t pid, int signal, char ignoreerrors)
redmine authored
1644
{
redmine authored
1645
	void *ret = (void *)(long)-1;
redmine authored
1646

redmine authored
1647 1648 1649
	cmd_p->arg.kill_child.pid          = pid;
	cmd_p->arg.kill_child.signal       = signal;
	cmd_p->arg.kill_child.ignoreerrors = ignoreerrors;
redmine authored
1650

redmine authored
1651
	privileged_action(
redmine authored
1652
# ifdef HL_LOCK_TRIES_AUTO
redmine authored
1653
			PC_DEFAULT,
redmine authored
1654
# endif
redmine authored
1655 1656
			PA_KILL_CHILD,
			&ret);
redmine authored
1657 1658 1659 1660

	return (long)ret;
}

redmine authored
1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680
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
1681 1682
#endif

redmine authored
1683 1684 1685
uid_t __privileged_fork_execvp_uid;
gid_t __privileged_fork_execvp_gid;
int __privileged_fork_execvp(const char *file, char *const argv[])
redmine authored
1686 1687 1688 1689 1690 1691 1692
{
	debug(4, "");
	pid_t pid = fork();
	switch (pid) {
		case -1: 
			error("Cannot fork().");
			return -1;
redmine authored
1693 1694 1695 1696 1697 1698 1699 1700 1701 1702
		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
1703 1704 1705
			errno = 0;
			execvp(file, argv);
			exit(errno);
redmine authored