aboutsummaryrefslogtreecommitdiff
path: root/src/security_util.cpp
blob: ab5b8c48cba201e56a2ee4a9df5763823550f888 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//SPDX-License-Identifier: GPL-3.0

#include <libqalculate/Function.h>
#include <libqalculate/Number.h>
#include <libqalculate/QalculateDateTime.h>
#include <libqalculate/Variable.h>
#include <security_util.h>
#include <sys/time.h>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <string>

#ifdef SETUID
#include <grp.h>
#include <cap-ng.h>
#endif

#ifdef SECCOMP
#include <seccomp.h>
#endif

#ifdef LIBQALCULATE_DEFANG
static void destroy_if_exists(ExpressionItem *item) {
	if (item)
		item->destroy();
}

void do_defang_calculator(Calculator &calc) {
	destroy_if_exists(calc.getActiveFunction("command")); // rce
	destroy_if_exists(calc.getActiveFunction("plot")); // wouldn't work, possible rce
	destroy_if_exists(calc.getActiveVariable("uptime")); // information leakage
	destroy_if_exists(calc.getActiveVariable("export")); // lfi
	destroy_if_exists(calc.getActiveVariable("load")); // lfi
}
#else
void do_defang_calculator(Calculator&) {}
#endif

void do_setuid() {
#ifdef SETUID
	if (setgroups(0, {})) {
		perror("couldn't remove groups");
		abort();
	}

	if (setresgid(SETUID_GID, SETUID_GID, SETUID_GID)) {
		perror("couldn't set gid");
		abort();
	}

	if (setresuid(SETUID_UID, SETUID_UID, SETUID_UID)) {
		perror("couldn't set uid");
		abort();
	}

	capng_clear(CAPNG_SELECT_BOTH);
	if (capng_update(CAPNG_DROP, static_cast<capng_type_t>(CAPNG_EFFECTIVE | CAPNG_PERMITTED), CAP_SETGID)) {
		perror("couldn't drop caps: can't select capabilities to drop\n");
		abort();
	}
	int err = capng_apply(CAPNG_SELECT_BOTH);
	if (err) {
		printf("couldn't drop caps: %d\n", err);
		abort();
	}
#endif
}

#ifdef SECCOMP
static int now_year;
static int now_month;
static int now_day;
static suseconds_t now_usec;
static std::time_t now_sec;

// "now", "today", "yesterday", etc. all depend on the openat syscall. I've opted to instead get the time before
// seccomping and then return it by replacing these two functions. This does seem dirty and requires an extra linker
// flag when doing a static link, but it does work.
void QalculateDateTime::setToCurrentDate() {
	parsed_string.clear();
	set(now_year, now_month, now_day);
}

void QalculateDateTime::setToCurrentTime() {
	parsed_string.clear();
	Number nr(now_usec, 0, -6);
	nr += now_sec;
	set(nr);
}

void do_seccomp() {
	struct std::tm tmdate;
	std::time_t rawtime;
	std::time(&rawtime);
	tmdate = *localtime(&rawtime);
	now_year = tmdate.tm_year + 1900;
	now_month = tmdate.tm_mon + 1;
	now_day = tmdate.tm_mday;

	struct timeval tv;
	gettimeofday(&tv, NULL);
	now_usec = tv.tv_usec;
	now_sec = tv.tv_sec;

	setenv("QALCULATE_USER_DIR", "/", 1);
	// Despite having global definitions compiled in, libqalculate will still attempt to load local definitions in some
	// cirsumstances (e.g. when using a dataset function such as Planets). This results in a call to util.cc:buildPath(),
	// which will in turn result to a nasty getpwuid() and getuid() if QALCULATE_USER_DIR, XDG_DATA_HOME are unset. These
	// calls in turn require openat and getcwd syscalls respectively, which can be avoided by simply setting either of
	// these environment variables to a bogus value.

	scmp_filter_ctx ctx;
#ifdef ENABLE_DEBUG
	ctx = seccomp_init(SCMP_ACT_LOG);
#else
	ctx = seccomp_init(SCMP_ACT_KILL_PROCESS);
#endif
	/*   0 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
	/*   1 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
	/*   3 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
	/*   9 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
	/*  10 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
	/*  11 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
	/*  12 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
	/*  13 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction), 0);
	/*  14 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 0);
	/*  24 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sched_yield), 0);
	/*  28 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(madvise), 0);
	/*  56 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clone), 0); // required by docker
	/*  60 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
	/* 202 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
	/* 230 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clock_nanosleep), 0);
	/* 231 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
	/* 262 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(newfstatat), 0);
	/* 273 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(set_robust_list), 0);
	/* 334 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rseq), 0);
	/* 435 */seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clone3), 0);

	int err = seccomp_load(ctx);
	if (err) {
		printf("couldn't seccomp: %d\n", err);
		abort();
	}
}
#else
void do_seccomp() {}
#endif