1 #include "config.h"
2 #include <seccomp.h>
3 #include <termios.h>
4 #include <errno.h>
5 #include <stdlib.h>
6 #include <sys/ioctl.h>
7 #include <signal.h>
8 #include <string.h>
9 #include "memcached.h"
10 
11 static char *kill_msg;
12 // Make sure to preserve the ??? position in the string, or correct the offsets
13 // in the syssig handler.
14 #define KILL_MSG_STR "Seccomp policy failure. Caught syscall ???. This is " \
15     "either an exploit attempt, or your system is not supported yet.\n"
16 
handle_syssig(int signum,siginfo_t * si,void * thread_context)17 static void handle_syssig(int signum, siginfo_t *si, void *thread_context) {
18 #if defined(si_syscall)
19     int syscall_no = si->si_syscall;
20 #else
21     // If system has no support for si_syscal, the information may not be
22     // precise.
23     int syscall_no = si->si_value.sival_int;
24 #endif
25 
26     // Replace the characters in the kill message with the syscall number. We
27     // can't safely printf (even write is not really valid, but we're crashing
28     // anyway).
29 
30     kill_msg[39] = (syscall_no / 100) % 10 + '0';
31     kill_msg[40] = (syscall_no / 10) % 10 + '0';
32     kill_msg[41] = syscall_no % 10 + '0';
33     if (write(2, kill_msg, strlen(kill_msg)) == -1) {
34         // An error occurred, but we can't do anything about it here. This check
35         // is mostly to avoid the "ignoring return value of 'write'" error on
36         // distributions with broken gcc (no "ignore via cast to void" support).
37     }
38 
39     // We can't use the nice exit() version because it causes at_exit handlers
40     // to be looked up and run. We can't take any locks while handling the
41     // signal, so _exit() is the only thing to do safely.
42     _exit(EXIT_FAILURE);
43 }
44 
45 static const struct sigaction act = {
46     .sa_sigaction = handle_syssig,
47     .sa_flags = SA_SIGINFO,
48 };
49 
setup_privilege_violations_handler(void)50 void setup_privilege_violations_handler(void) {
51     kill_msg = malloc(strlen(KILL_MSG_STR)+1);
52     strncpy(kill_msg, KILL_MSG_STR, strlen(KILL_MSG_STR)+1);
53 
54     sigaction(SIGSYS, &act, NULL);
55 }
56 
57 // If anything crosses the policy, kill the process.
58 #define DENY_ACTION SCMP_ACT_TRAP
59 
drop_privileges(void)60 void drop_privileges(void) {
61     scmp_filter_ctx ctx = seccomp_init(DENY_ACTION);
62     if (ctx == NULL) {
63         return;
64     }
65 
66     int rc = 0;
67     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sigreturn), 0);
68     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0);
69     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 0);
70     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction), 0);
71     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
72     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_wait), 0);
73     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_pwait), 0);
74     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 0);
75     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), 0);
76     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept), 0);
77     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
78     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(writev), 0);
79     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
80     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
81     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
82     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
83     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(shmctl), 0);
84     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
85     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
86     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
87     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, SCMP_A1(SCMP_CMP_EQ, TIOCGWINSZ));
88     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, SCMP_A1(SCMP_CMP_EQ, TCGETS));
89     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(msync), 0);
90     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(umask), 0);
91 
92 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
93     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clock_gettime), 0);
94 #endif
95     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(gettimeofday), 0);
96 
97 #ifdef MEMCACHED_DEBUG
98     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
99     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0);
100     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
101     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(readv), 0);
102     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0);
103     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
104 
105     if (settings.relaxed_privileges) {
106         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0);
107         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mkdir), 0);
108         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(access), 0);
109     }
110 #endif
111 
112     if (rc != 0) {
113         goto fail;
114     }
115 
116     rc = seccomp_load(ctx);
117     if (rc < 0) {
118         goto fail;
119     }
120 
121     seccomp_release(ctx);
122     return;
123 
124 fail:
125     seccomp_release(ctx);
126     fprintf(stderr, "Failed to set a seccomp profile on the main thread\n");
127     exit(EXIT_FAILURE);
128 }
129 
drop_worker_privileges(void)130 void drop_worker_privileges(void) {
131     scmp_filter_ctx ctx = seccomp_init(DENY_ACTION);
132     if (ctx == NULL) {
133         return;
134     }
135 
136     int rc = 0;
137     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sigreturn), 0);
138     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0);
139     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 0);
140     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction), 0);
141     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
142     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_wait), 0);
143     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_pwait), 0);
144     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 0);
145     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(poll), 0);
146     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
147     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(readv), 0);
148     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
149     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpeername), 0);
150     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
151     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendmsg), 0);
152     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getrusage), 0);
153     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
154     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mremap), 0);
155     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
156     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(recvfrom), 0);
157     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
158     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, SCMP_A1(SCMP_CMP_EQ, TIOCGWINSZ));
159     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(msync), 0);
160 
161     // for spawning the LRU crawler
162     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clone), 0);
163     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(set_robust_list), 0);
164     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(madvise), 0);
165     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
166 
167     // stat
168     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockname), 0);
169     rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
170 
171     if (settings.shutdown_command) {
172         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(tgkill), 0);
173         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(tkill), 0);
174         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
175         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
176         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
177         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(gettid), 0);
178     }
179 
180     if (settings.relaxed_privileges) {
181         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0);
182         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mkdir), 0);
183         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(access), 0);
184         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
185         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0);
186         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0);
187         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
188         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(writev), 0);
189         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 0);
190     } else {
191         // stdout
192         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_A0(SCMP_CMP_EQ, 1));
193         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(writev), 1, SCMP_A0(SCMP_CMP_EQ, 1));
194         // stderr
195         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_A0(SCMP_CMP_EQ, 2));
196         rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(writev), 1, SCMP_A0(SCMP_CMP_EQ, 2));
197     }
198 
199     if (rc != 0) {
200         goto fail;
201     }
202 
203     rc = seccomp_load(ctx);
204     if (rc < 0) {
205         goto fail;
206     }
207 
208     seccomp_release(ctx);
209     return;
210 
211 fail:
212     seccomp_release(ctx);
213     fprintf(stderr, "Failed to set a seccomp profile on a worker thread\n");
214     exit(EXIT_FAILURE);
215 }
216