2 * parse and setup OCI seccomp filter
3 * Copyright (c) 2020 Daniel Golle <daniel@makrotopia.org>
4 * seccomp example with syscall reporting
5 * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org>
7 * Kees Cook <keescook@chromium.org>
8 * Will Drewry <wad@chromium.org>
10 * Use of this source code is governed by a BSD-style license that can be
11 * found in the LICENSE file.
18 #include <libubox/utils.h>
19 #include <libubox/blobmsg.h>
20 #include <libubox/blobmsg_json.h>
23 #include "seccomp-bpf.h"
24 #include "seccomp-oci.h"
25 #include "../syscall-names.h"
26 #include "seccomp-syscalls-helpers.h"
28 static uint32_t resolve_action(char *actname
)
30 if (!strcmp(actname
, "SCMP_ACT_KILL"))
31 return SECCOMP_RET_KILL
;
32 else if (!strcmp(actname
, "SCMP_ACT_KILL_PROCESS"))
33 return SECCOMP_RET_KILLPROCESS
;
34 else if (!strcmp(actname
, "SCMP_ACT_TRAP"))
35 return SECCOMP_RET_TRAP
;
36 else if (!strcmp(actname
, "SCMP_ACT_ERRNO"))
37 return SECCOMP_RET_ERRNO
;
38 else if (!strcmp(actname
, "SCMP_ACT_ERROR"))
39 return SECCOMP_RET_ERRNO
;
40 else if (!strcmp(actname
, "SCMP_ACT_TRACE"))
41 return SECCOMP_RET_TRACE
;
42 else if (!strcmp(actname
, "SCMP_ACT_ALLOW"))
43 return SECCOMP_RET_ALLOW
;
44 else if (!strcmp(actname
, "SCMP_ACT_LOG"))
45 return SECCOMP_RET_LOGALLOW
;
47 ERROR("unknown seccomp action %s\n", actname
);
48 return SECCOMP_RET_KILL
;
52 static uint32_t resolve_architecture(char *archname
)
54 if (!strcmp(archname
, "SCMP_ARCH_X86"))
55 return AUDIT_ARCH_I386
;
56 else if (!strcmp(archname
, "SCMP_ARCH_X86_64"))
57 return AUDIT_ARCH_X86_64
;
58 else if (!strcmp(archname
, "SCMP_ARCH_X32"))
60 * return AUDIT_ARCH_X86_64;
61 * 32-bit userland on 64-bit kernel is not supported yet
64 else if (!strcmp(archname
, "SCMP_ARCH_ARM"))
65 return AUDIT_ARCH_ARM
;
66 else if (!strcmp(archname
, "SCMP_ARCH_AARCH64"))
67 return AUDIT_ARCH_AARCH64
;
68 else if (!strcmp(archname
, "SCMP_ARCH_MIPS"))
69 return AUDIT_ARCH_MIPS
;
70 else if (!strcmp(archname
, "SCMP_ARCH_MIPS64"))
71 return AUDIT_ARCH_MIPS64
;
72 else if (!strcmp(archname
, "SCMP_ARCH_MIPS64N32"))
73 return AUDIT_ARCH_MIPS64N32
;
74 else if (!strcmp(archname
, "SCMP_ARCH_MIPSEL"))
75 return AUDIT_ARCH_MIPSEL
;
76 else if (!strcmp(archname
, "SCMP_ARCH_MIPSEL64"))
77 return AUDIT_ARCH_MIPSEL64
;
78 else if (!strcmp(archname
, "SCMP_ARCH_MIPSEL64N32"))
79 return AUDIT_ARCH_MIPSEL64N32
;
80 else if (!strcmp(archname
, "SCMP_ARCH_PPC"))
81 return AUDIT_ARCH_PPC
;
82 else if (!strcmp(archname
, "SCMP_ARCH_PPC64"))
83 return AUDIT_ARCH_PPC64
;
84 else if (!strcmp(archname
, "SCMP_ARCH_PPC64LE"))
85 return AUDIT_ARCH_PPC64LE
;
86 else if (!strcmp(archname
, "SCMP_ARCH_S390"))
87 return AUDIT_ARCH_S390
;
88 else if (!strcmp(archname
, "SCMP_ARCH_S390X"))
89 return AUDIT_ARCH_S390X
;
90 else if (!strcmp(archname
, "SCMP_ARCH_PARISC"))
91 return AUDIT_ARCH_PARISC
;
92 else if (!strcmp(archname
, "SCMP_ARCH_PARISC64"))
93 return AUDIT_ARCH_PARISC64
;
95 ERROR("unknown seccomp architecture %s\n", archname
);
101 OCI_LINUX_SECCOMP_DEFAULTACTION
,
102 OCI_LINUX_SECCOMP_ARCHITECTURES
,
103 OCI_LINUX_SECCOMP_FLAGS
,
104 OCI_LINUX_SECCOMP_SYSCALLS
,
105 __OCI_LINUX_SECCOMP_MAX
,
108 static const struct blobmsg_policy oci_linux_seccomp_policy
[] = {
109 [OCI_LINUX_SECCOMP_DEFAULTACTION
] = { "defaultAction", BLOBMSG_TYPE_STRING
},
110 [OCI_LINUX_SECCOMP_ARCHITECTURES
] = { "architectures", BLOBMSG_TYPE_ARRAY
},
111 [OCI_LINUX_SECCOMP_FLAGS
] = { "flags", BLOBMSG_TYPE_ARRAY
},
112 [OCI_LINUX_SECCOMP_SYSCALLS
] = { "syscalls", BLOBMSG_TYPE_ARRAY
},
116 OCI_LINUX_SECCOMP_SYSCALLS_NAMES
,
117 OCI_LINUX_SECCOMP_SYSCALLS_ACTION
,
118 OCI_LINUX_SECCOMP_SYSCALLS_ERRNORET
,
119 OCI_LINUX_SECCOMP_SYSCALLS_ARGS
,
120 __OCI_LINUX_SECCOMP_SYSCALLS_MAX
123 static const struct blobmsg_policy oci_linux_seccomp_syscalls_policy
[] = {
124 [OCI_LINUX_SECCOMP_SYSCALLS_NAMES
] = { "names", BLOBMSG_TYPE_ARRAY
},
125 [OCI_LINUX_SECCOMP_SYSCALLS_ERRNORET
] = { "errnoRet", BLOBMSG_TYPE_INT32
},
126 [OCI_LINUX_SECCOMP_SYSCALLS_ARGS
] = { "args", BLOBMSG_TYPE_ARRAY
},
127 [OCI_LINUX_SECCOMP_SYSCALLS_ACTION
] = { "action", BLOBMSG_TYPE_STRING
},
131 OCI_LINUX_SECCOMP_SYSCALLS_ARGS_INDEX
,
132 OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUE
,
133 OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUETWO
,
134 OCI_LINUX_SECCOMP_SYSCALLS_ARGS_OP
,
135 __OCI_LINUX_SECCOMP_SYSCALLS_ARGS_MAX
138 static const struct blobmsg_policy oci_linux_seccomp_syscalls_args_policy
[] = {
139 [OCI_LINUX_SECCOMP_SYSCALLS_ARGS_INDEX
] = { "index", BLOBMSG_TYPE_INT32
},
140 [OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUE
] = { "value", BLOBMSG_TYPE_INT64
},
141 [OCI_LINUX_SECCOMP_SYSCALLS_ARGS_VALUETWO
] = { "valueTwo", BLOBMSG_TYPE_INT64
},
142 [OCI_LINUX_SECCOMP_SYSCALLS_ARGS_OP
] = { "op", BLOBMSG_TYPE_STRING
},
145 struct sock_fprog
*parseOCIlinuxseccomp(struct blob_attr
*msg
)
147 struct blob_attr
*tb
[__OCI_LINUX_SECCOMP_MAX
];
148 struct blob_attr
*tbn
[__OCI_LINUX_SECCOMP_SYSCALLS_MAX
];
149 struct blob_attr
*tba
[__OCI_LINUX_SECCOMP_SYSCALLS_ARGS_MAX
];
150 struct blob_attr
*cur
, *curn
, *curarg
;
151 int rem
, remn
, remargs
, sc
;
152 struct sock_filter
*filter
;
153 struct sock_fprog
*prog
;
155 uint32_t default_policy
= 0;
156 uint32_t seccomp_arch
;
158 blobmsg_parse(oci_linux_seccomp_policy
, __OCI_LINUX_SECCOMP_MAX
, tb
, blobmsg_data(msg
), blobmsg_len(msg
));
160 if (!tb
[OCI_LINUX_SECCOMP_DEFAULTACTION
]) {
161 ERROR("seccomp: no default action set\n");
165 default_policy
= resolve_action(blobmsg_get_string(tb
[OCI_LINUX_SECCOMP_DEFAULTACTION
]));
167 /* verify architecture while ignoring the x86_64 anomaly for now */
168 blobmsg_for_each_attr(cur
, tb
[OCI_LINUX_SECCOMP_ARCHITECTURES
], rem
) {
169 seccomp_arch
= resolve_architecture(blobmsg_get_string(cur
));
170 /* take the first useful arch for now */
175 if (ARCH_NR
!= seccomp_arch
) {
176 ERROR("seccomp architecture doesn't match system\n");
180 blobmsg_for_each_attr(cur
, tb
[OCI_LINUX_SECCOMP_SYSCALLS
], rem
) {
181 blobmsg_parse(oci_linux_seccomp_syscalls_policy
, __OCI_LINUX_SECCOMP_SYSCALLS_MAX
, tbn
, blobmsg_data(cur
), blobmsg_len(cur
));
182 blobmsg_for_each_attr(curn
, tbn
[OCI_LINUX_SECCOMP_SYSCALLS_NAMES
], remn
)
185 if (tbn
[OCI_LINUX_SECCOMP_SYSCALLS_ARGS
])
186 blobmsg_for_each_attr(curarg
, tbn
[OCI_LINUX_SECCOMP_SYSCALLS_ARGS
], remargs
)
190 prog
= malloc(sizeof(struct sock_fprog
));
194 filter
= calloc(sz
, sizeof(struct sock_filter
));
196 ERROR("failed to allocate memory for seccomp filter\n");
201 set_filter(&filter
[idx
++], BPF_LD
+ BPF_W
+ BPF_ABS
, 0, 0, arch_nr
);
202 set_filter(&filter
[idx
++], BPF_JMP
+ BPF_JEQ
+ BPF_K
, 1, 0, ARCH_NR
);
203 set_filter(&filter
[idx
++], BPF_RET
+ BPF_K
, 0, 0, SECCOMP_RET_KILL
);
206 set_filter(&filter
[idx
++], BPF_LD
+ BPF_W
+ BPF_ABS
, 0, 0, syscall_nr
);
208 blobmsg_for_each_attr(cur
, tb
[OCI_LINUX_SECCOMP_SYSCALLS
], rem
) {
210 blobmsg_parse(oci_linux_seccomp_syscalls_policy
, __OCI_LINUX_SECCOMP_SYSCALLS_MAX
, tbn
, blobmsg_data(cur
), blobmsg_len(cur
));
211 action
= resolve_action(blobmsg_get_string(tbn
[OCI_LINUX_SECCOMP_SYSCALLS_ACTION
]));
212 if (tbn
[OCI_LINUX_SECCOMP_SYSCALLS_ERRNORET
]) {
213 if (action
!= SECCOMP_RET_ERRNO
)
216 action
= SECCOMP_RET_ERROR(blobmsg_get_u32(tbn
[OCI_LINUX_SECCOMP_SYSCALLS_ERRNORET
]));
217 } else if (action
== SECCOMP_RET_ERRNO
)
218 action
= SECCOMP_RET_ERROR(EPERM
);
220 blobmsg_for_each_attr(curn
, tbn
[OCI_LINUX_SECCOMP_SYSCALLS_NAMES
], remn
) {
221 sc
= find_syscall(blobmsg_get_string(curn
));
223 ERROR("unknown syscall '%s'\n", blobmsg_get_string(curn
));
224 /* TODO: support run.oci.seccomp_fail_unknown_syscall=1 annotation */
228 /* add rule to filter */
229 set_filter(&filter
[idx
++], BPF_JMP
+ BPF_JEQ
+ BPF_K
, 0, 1, sc
);
230 set_filter(&filter
[idx
++], BPF_RET
+ BPF_K
, 0, 0, action
);
233 blobmsg_for_each_attr(curn
, tbn
[OCI_LINUX_SECCOMP_SYSCALLS_ARGS
], remn
) {
234 blobmsg_parse(oci_linux_seccomp_syscalls_args_policy
, __OCI_LINUX_SECCOMP_SYSCALLS_ARGS_MAX
, tba
, blobmsg_data(curn
), blobmsg_len(curn
));
235 /* ToDo: process args */
239 set_filter(&filter
[idx
], BPF_RET
+ BPF_K
, 0, 0, default_policy
);
241 prog
->len
= (unsigned short) idx
+ 1;
242 prog
->filter
= filter
;
254 int applyOCIlinuxseccomp(struct sock_fprog
*prog
)
256 if (prctl(PR_SET_NO_NEW_PRIVS
, 1, 0, 0, 0)) {
257 ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %m\n");
261 if (prctl(PR_SET_SECCOMP
, SECCOMP_MODE_FILTER
, prog
)) {
262 ERROR("prctl(PR_SET_SECCOMP) failed: %m\n");