github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/cmd/snap-seccomp/main.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017-2019 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package main 21 22 //#cgo CFLAGS: -D_FILE_OFFSET_BITS=64 23 //#cgo pkg-config: libseccomp 24 //#cgo LDFLAGS: 25 // 26 //#include <asm/ioctls.h> 27 //#include <ctype.h> 28 //#include <errno.h> 29 //#include <linux/can.h> 30 //#include <linux/netlink.h> 31 //#include <sched.h> 32 //#include <search.h> 33 //#include <stdbool.h> 34 //#include <stdio.h> 35 //#include <stdlib.h> 36 //#include <string.h> 37 //#include <sys/prctl.h> 38 //#include <sys/quota.h> 39 //#include <sys/resource.h> 40 //#include <sys/socket.h> 41 //#include <sys/stat.h> 42 //#include <sys/types.h> 43 //#include <sys/utsname.h> 44 //#include <sys/ptrace.h> 45 //#include <termios.h> 46 //#include <unistd.h> 47 // //The XFS interface requires a 64 bit file system interface 48 // //but we don't want to leak this anywhere else if not globally 49 // //defined. 50 //#ifndef _FILE_OFFSET_BITS 51 //#define _FILE_OFFSET_BITS 64 52 //#include <xfs/xqm.h> 53 //#undef _FILE_OFFSET_BITS 54 //#else 55 //#include <xfs/xqm.h> 56 //#endif 57 //#include <seccomp.h> 58 //#include <linux/sched.h> 59 //#include <linux/seccomp.h> 60 //#include <arpa/inet.h> 61 // 62 //#ifndef AF_IB 63 //#define AF_IB 27 64 //#define PF_IB AF_IB 65 //#endif // AF_IB 66 // 67 //#ifndef AF_MPLS 68 //#define AF_MPLS 28 69 //#define PF_MPLS AF_MPLS 70 //#endif // AF_MPLS 71 // 72 // // https://github.com/sctplab/usrsctp/blob/master/usrsctplib/usrsctp.h 73 //#ifndef AF_CONN 74 //#define AF_CONN 123 75 //#define PF_CONN AF_CONN 76 //#endif // AF_CONN 77 // 78 //#ifndef PR_CAP_AMBIENT 79 //#define PR_CAP_AMBIENT 47 80 //#define PR_CAP_AMBIENT_IS_SET 1 81 //#define PR_CAP_AMBIENT_RAISE 2 82 //#define PR_CAP_AMBIENT_LOWER 3 83 //#define PR_CAP_AMBIENT_CLEAR_ALL 4 84 //#endif // PR_CAP_AMBIENT 85 // 86 //#ifndef PR_SET_THP_DISABLE 87 //#define PR_SET_THP_DISABLE 41 88 //#endif // PR_SET_THP_DISABLE 89 //#ifndef PR_GET_THP_DISABLE 90 //#define PR_GET_THP_DISABLE 42 91 //#endif // PR_GET_THP_DISABLE 92 // 93 //#ifndef PR_MPX_ENABLE_MANAGEMENT 94 //#define PR_MPX_ENABLE_MANAGEMENT 43 95 //#endif 96 // 97 //#ifndef PR_MPX_DISABLE_MANAGEMENT 98 //#define PR_MPX_DISABLE_MANAGEMENT 44 99 //#endif 100 // 101 // //FIXME: ARCH_BAD is defined as ~0 in libseccomp internally, however 102 // // this leads to a build failure on 14.04. the important part 103 // // is that its an invalid id for libseccomp. 104 // 105 //#define ARCH_BAD 0x7FFFFFFF 106 //#ifndef SCMP_ARCH_AARCH64 107 //#define SCMP_ARCH_AARCH64 ARCH_BAD 108 //#endif 109 // 110 //#ifndef SCMP_ARCH_PPC 111 //#define SCMP_ARCH_PPC ARCH_BAD 112 //#endif 113 // 114 //#ifndef SCMP_ARCH_PPC64LE 115 //#define SCMP_ARCH_PPC64LE ARCH_BAD 116 //#endif 117 // 118 //#ifndef SCMP_ARCH_PPC64 119 //#define SCMP_ARCH_PPC64 ARCH_BAD 120 //#endif 121 // 122 //#ifndef SCMP_ARCH_S390X 123 //#define SCMP_ARCH_S390X ARCH_BAD 124 //#endif 125 // 126 //#ifndef SECCOMP_RET_LOG 127 //#define SECCOMP_RET_LOG 0x7ffc0000U 128 //#endif 129 // 130 //typedef struct seccomp_data kernel_seccomp_data; 131 // 132 //__u32 htot32(__u32 arch, __u32 val) 133 //{ 134 // if (arch & __AUDIT_ARCH_LE) 135 // return htole32(val); 136 // else 137 // return htobe32(val); 138 //} 139 // 140 //__u64 htot64(__u32 arch, __u64 val) 141 //{ 142 // if (arch & __AUDIT_ARCH_LE) 143 // return htole64(val); 144 // else 145 // return htobe64(val); 146 //} 147 // 148 // /* Define missing ptrace constants. They are available on some architectures 149 // only but the missing values are not reused on architectures that lack them. 150 // As such we can simply define the missing pair and have a simpler cross-arch 151 // code to support. */ 152 // 153 // #ifndef PTRACE_GETREGS 154 // #define PTRACE_GETREGS 12 155 // #endif 156 // #ifndef PTRACE_SETREGS 157 // #define PTRACE_SETREGS 13 158 // #endif 159 // #ifndef PTRACE_GETFPREGS 160 // #define PTRACE_GETFPREGS 14 161 // #endif 162 // #ifndef PTRACE_SETFPREGS 163 // #define PTRACE_SETFPREGS 15 164 // #endif 165 // #ifndef PTRACE_GETFPXREGS 166 // #define PTRACE_GETFPXREGS 18 167 // #endif 168 // #ifndef PTRACE_SETFPXREGS 169 // #define PTRACE_SETFPXREGS 19 170 // #endif 171 import "C" 172 173 import ( 174 "bufio" 175 "bytes" 176 "fmt" 177 "io/ioutil" 178 "os" 179 "strconv" 180 "strings" 181 "syscall" 182 183 // FIXME: we want github.com/seccomp/libseccomp-golang but that 184 // will not work with trusty because libseccomp-golang checks 185 // for the seccomp version and errors if it find one < 2.2.0 186 "github.com/mvo5/libseccomp-golang" 187 188 "github.com/snapcore/snapd/arch" 189 "github.com/snapcore/snapd/osutil" 190 ) 191 192 // libseccomp maximum per ARG_COUNT_MAX in src/arch.h 193 const ScArgsMaxlength = 6 194 195 var seccompResolver = map[string]uint64{ 196 // man 2 socket - domain and man 5 apparmor.d. AF_ and PF_ are 197 // synonymous in the kernel and can be used interchangeably in 198 // policy (ie, if use AF_UNIX, don't need a corresponding PF_UNIX 199 // rule). See include/linux/socket.h 200 "AF_UNIX": syscall.AF_UNIX, 201 "PF_UNIX": C.PF_UNIX, 202 "AF_LOCAL": syscall.AF_LOCAL, 203 "PF_LOCAL": C.PF_LOCAL, 204 "AF_INET": syscall.AF_INET, 205 "PF_INET": C.PF_INET, 206 "AF_INET6": syscall.AF_INET6, 207 "PF_INET6": C.PF_INET6, 208 "AF_IPX": syscall.AF_IPX, 209 "PF_IPX": C.PF_IPX, 210 "AF_NETLINK": syscall.AF_NETLINK, 211 "PF_NETLINK": C.PF_NETLINK, 212 "AF_X25": syscall.AF_X25, 213 "PF_X25": C.PF_X25, 214 "AF_AX25": syscall.AF_AX25, 215 "PF_AX25": C.PF_AX25, 216 "AF_ATMPVC": syscall.AF_ATMPVC, 217 "PF_ATMPVC": C.PF_ATMPVC, 218 "AF_APPLETALK": syscall.AF_APPLETALK, 219 "PF_APPLETALK": C.PF_APPLETALK, 220 "AF_PACKET": syscall.AF_PACKET, 221 "PF_PACKET": C.PF_PACKET, 222 "AF_ALG": syscall.AF_ALG, 223 "PF_ALG": C.PF_ALG, 224 "AF_BRIDGE": syscall.AF_BRIDGE, 225 "PF_BRIDGE": C.PF_BRIDGE, 226 "AF_NETROM": syscall.AF_NETROM, 227 "PF_NETROM": C.PF_NETROM, 228 "AF_ROSE": syscall.AF_ROSE, 229 "PF_ROSE": C.PF_ROSE, 230 "AF_NETBEUI": syscall.AF_NETBEUI, 231 "PF_NETBEUI": C.PF_NETBEUI, 232 "AF_SECURITY": syscall.AF_SECURITY, 233 "PF_SECURITY": C.PF_SECURITY, 234 "AF_KEY": syscall.AF_KEY, 235 "PF_KEY": C.PF_KEY, 236 "AF_ASH": syscall.AF_ASH, 237 "PF_ASH": C.PF_ASH, 238 "AF_ECONET": syscall.AF_ECONET, 239 "PF_ECONET": C.PF_ECONET, 240 "AF_SNA": syscall.AF_SNA, 241 "PF_SNA": C.PF_SNA, 242 "AF_IRDA": syscall.AF_IRDA, 243 "PF_IRDA": C.PF_IRDA, 244 "AF_PPPOX": syscall.AF_PPPOX, 245 "PF_PPPOX": C.PF_PPPOX, 246 "AF_WANPIPE": syscall.AF_WANPIPE, 247 "PF_WANPIPE": C.PF_WANPIPE, 248 "AF_BLUETOOTH": syscall.AF_BLUETOOTH, 249 "PF_BLUETOOTH": C.PF_BLUETOOTH, 250 "AF_RDS": syscall.AF_RDS, 251 "PF_RDS": C.PF_RDS, 252 "AF_LLC": syscall.AF_LLC, 253 "PF_LLC": C.PF_LLC, 254 "AF_TIPC": syscall.AF_TIPC, 255 "PF_TIPC": C.PF_TIPC, 256 "AF_IUCV": syscall.AF_IUCV, 257 "PF_IUCV": C.PF_IUCV, 258 "AF_RXRPC": syscall.AF_RXRPC, 259 "PF_RXRPC": C.PF_RXRPC, 260 "AF_ISDN": syscall.AF_ISDN, 261 "PF_ISDN": C.PF_ISDN, 262 "AF_PHONET": syscall.AF_PHONET, 263 "PF_PHONET": C.PF_PHONET, 264 "AF_IEEE802154": syscall.AF_IEEE802154, 265 "PF_IEEE802154": C.PF_IEEE802154, 266 "AF_CAIF": syscall.AF_CAIF, 267 "PF_CAIF": C.AF_CAIF, 268 "AF_NFC": C.AF_NFC, 269 "PF_NFC": C.PF_NFC, 270 "AF_VSOCK": C.AF_VSOCK, 271 "PF_VSOCK": C.PF_VSOCK, 272 // may not be defined in socket.h yet 273 "AF_IB": C.AF_IB, // 27 274 "PF_IB": C.PF_IB, 275 "AF_MPLS": C.AF_MPLS, // 28 276 "PF_MPLS": C.PF_MPLS, 277 "AF_CAN": syscall.AF_CAN, 278 "PF_CAN": C.PF_CAN, 279 "AF_CONN": C.AF_CONN, // 123 280 "PF_CONN": C.PF_CONN, 281 282 // man 2 socket - type 283 "SOCK_STREAM": syscall.SOCK_STREAM, 284 "SOCK_DGRAM": syscall.SOCK_DGRAM, 285 "SOCK_SEQPACKET": syscall.SOCK_SEQPACKET, 286 "SOCK_RAW": syscall.SOCK_RAW, 287 "SOCK_RDM": syscall.SOCK_RDM, 288 "SOCK_PACKET": syscall.SOCK_PACKET, 289 290 // man 2 prctl 291 "PR_CAP_AMBIENT": C.PR_CAP_AMBIENT, 292 "PR_CAP_AMBIENT_RAISE": C.PR_CAP_AMBIENT_RAISE, 293 "PR_CAP_AMBIENT_LOWER": C.PR_CAP_AMBIENT_LOWER, 294 "PR_CAP_AMBIENT_IS_SET": C.PR_CAP_AMBIENT_IS_SET, 295 "PR_CAP_AMBIENT_CLEAR_ALL": C.PR_CAP_AMBIENT_CLEAR_ALL, 296 "PR_CAPBSET_READ": C.PR_CAPBSET_READ, 297 "PR_CAPBSET_DROP": C.PR_CAPBSET_DROP, 298 "PR_SET_CHILD_SUBREAPER": C.PR_SET_CHILD_SUBREAPER, 299 "PR_GET_CHILD_SUBREAPER": C.PR_GET_CHILD_SUBREAPER, 300 "PR_SET_DUMPABLE": C.PR_SET_DUMPABLE, 301 "PR_GET_DUMPABLE": C.PR_GET_DUMPABLE, 302 "PR_SET_ENDIAN": C.PR_SET_ENDIAN, 303 "PR_GET_ENDIAN": C.PR_GET_ENDIAN, 304 "PR_SET_FPEMU": C.PR_SET_FPEMU, 305 "PR_GET_FPEMU": C.PR_GET_FPEMU, 306 "PR_SET_FPEXC": C.PR_SET_FPEXC, 307 "PR_GET_FPEXC": C.PR_GET_FPEXC, 308 "PR_SET_KEEPCAPS": C.PR_SET_KEEPCAPS, 309 "PR_GET_KEEPCAPS": C.PR_GET_KEEPCAPS, 310 "PR_MCE_KILL": C.PR_MCE_KILL, 311 "PR_MCE_KILL_GET": C.PR_MCE_KILL_GET, 312 "PR_SET_MM": C.PR_SET_MM, 313 "PR_SET_MM_START_CODE": C.PR_SET_MM_START_CODE, 314 "PR_SET_MM_END_CODE": C.PR_SET_MM_END_CODE, 315 "PR_SET_MM_START_DATA": C.PR_SET_MM_START_DATA, 316 "PR_SET_MM_END_DATA": C.PR_SET_MM_END_DATA, 317 "PR_SET_MM_START_STACK": C.PR_SET_MM_START_STACK, 318 "PR_SET_MM_START_BRK": C.PR_SET_MM_START_BRK, 319 "PR_SET_MM_BRK": C.PR_SET_MM_BRK, 320 "PR_SET_MM_ARG_START": C.PR_SET_MM_ARG_START, 321 "PR_SET_MM_ARG_END": C.PR_SET_MM_ARG_END, 322 "PR_SET_MM_ENV_START": C.PR_SET_MM_ENV_START, 323 "PR_SET_MM_ENV_END": C.PR_SET_MM_ENV_END, 324 "PR_SET_MM_AUXV": C.PR_SET_MM_AUXV, 325 "PR_SET_MM_EXE_FILE": C.PR_SET_MM_EXE_FILE, 326 "PR_MPX_ENABLE_MANAGEMENT": C.PR_MPX_ENABLE_MANAGEMENT, 327 "PR_MPX_DISABLE_MANAGEMENT": C.PR_MPX_DISABLE_MANAGEMENT, 328 "PR_SET_NAME": C.PR_SET_NAME, 329 "PR_GET_NAME": C.PR_GET_NAME, 330 "PR_SET_NO_NEW_PRIVS": C.PR_SET_NO_NEW_PRIVS, 331 "PR_GET_NO_NEW_PRIVS": C.PR_GET_NO_NEW_PRIVS, 332 "PR_SET_PDEATHSIG": C.PR_SET_PDEATHSIG, 333 "PR_GET_PDEATHSIG": C.PR_GET_PDEATHSIG, 334 "PR_SET_PTRACER": C.PR_SET_PTRACER, 335 "PR_SET_SECCOMP": C.PR_SET_SECCOMP, 336 "PR_GET_SECCOMP": C.PR_GET_SECCOMP, 337 "PR_SET_SECUREBITS": C.PR_SET_SECUREBITS, 338 "PR_GET_SECUREBITS": C.PR_GET_SECUREBITS, 339 "PR_SET_THP_DISABLE": C.PR_SET_THP_DISABLE, 340 "PR_TASK_PERF_EVENTS_DISABLE": C.PR_TASK_PERF_EVENTS_DISABLE, 341 "PR_TASK_PERF_EVENTS_ENABLE": C.PR_TASK_PERF_EVENTS_ENABLE, 342 "PR_GET_THP_DISABLE": C.PR_GET_THP_DISABLE, 343 "PR_GET_TID_ADDRESS": C.PR_GET_TID_ADDRESS, 344 "PR_SET_TIMERSLACK": C.PR_SET_TIMERSLACK, 345 "PR_GET_TIMERSLACK": C.PR_GET_TIMERSLACK, 346 "PR_SET_TIMING": C.PR_SET_TIMING, 347 "PR_GET_TIMING": C.PR_GET_TIMING, 348 "PR_SET_TSC": C.PR_SET_TSC, 349 "PR_GET_TSC": C.PR_GET_TSC, 350 "PR_SET_UNALIGN": C.PR_SET_UNALIGN, 351 "PR_GET_UNALIGN": C.PR_GET_UNALIGN, 352 353 // man 2 getpriority 354 "PRIO_PROCESS": syscall.PRIO_PROCESS, 355 "PRIO_PGRP": syscall.PRIO_PGRP, 356 "PRIO_USER": syscall.PRIO_USER, 357 358 // man 2 setns 359 "CLONE_NEWIPC": syscall.CLONE_NEWIPC, 360 "CLONE_NEWNET": syscall.CLONE_NEWNET, 361 "CLONE_NEWNS": syscall.CLONE_NEWNS, 362 "CLONE_NEWPID": syscall.CLONE_NEWPID, 363 "CLONE_NEWUSER": syscall.CLONE_NEWUSER, 364 "CLONE_NEWUTS": syscall.CLONE_NEWUTS, 365 366 // man 4 tty_ioctl 367 "TIOCSTI": syscall.TIOCSTI, 368 369 // man 2 quotactl (with what Linux supports) 370 "Q_SYNC": C.Q_SYNC, 371 "Q_QUOTAON": C.Q_QUOTAON, 372 "Q_QUOTAOFF": C.Q_QUOTAOFF, 373 "Q_GETFMT": C.Q_GETFMT, 374 "Q_GETINFO": C.Q_GETINFO, 375 "Q_SETINFO": C.Q_SETINFO, 376 "Q_GETQUOTA": C.Q_GETQUOTA, 377 "Q_SETQUOTA": C.Q_SETQUOTA, 378 "Q_XQUOTAON": C.Q_XQUOTAON, 379 "Q_XQUOTAOFF": C.Q_XQUOTAOFF, 380 "Q_XGETQUOTA": C.Q_XGETQUOTA, 381 "Q_XSETQLIM": C.Q_XSETQLIM, 382 "Q_XGETQSTAT": C.Q_XGETQSTAT, 383 "Q_XQUOTARM": C.Q_XQUOTARM, 384 385 // man 2 mknod 386 "S_IFREG": syscall.S_IFREG, 387 "S_IFCHR": syscall.S_IFCHR, 388 "S_IFBLK": syscall.S_IFBLK, 389 "S_IFIFO": syscall.S_IFIFO, 390 "S_IFSOCK": syscall.S_IFSOCK, 391 392 // man 7 netlink (uapi/linux/netlink.h) 393 "NETLINK_ROUTE": syscall.NETLINK_ROUTE, 394 "NETLINK_USERSOCK": syscall.NETLINK_USERSOCK, 395 "NETLINK_FIREWALL": syscall.NETLINK_FIREWALL, 396 "NETLINK_SOCK_DIAG": C.NETLINK_SOCK_DIAG, 397 "NETLINK_NFLOG": syscall.NETLINK_NFLOG, 398 "NETLINK_XFRM": syscall.NETLINK_XFRM, 399 "NETLINK_SELINUX": syscall.NETLINK_SELINUX, 400 "NETLINK_ISCSI": syscall.NETLINK_ISCSI, 401 "NETLINK_AUDIT": syscall.NETLINK_AUDIT, 402 "NETLINK_FIB_LOOKUP": syscall.NETLINK_FIB_LOOKUP, 403 "NETLINK_CONNECTOR": syscall.NETLINK_CONNECTOR, 404 "NETLINK_NETFILTER": syscall.NETLINK_NETFILTER, 405 "NETLINK_IP6_FW": syscall.NETLINK_IP6_FW, 406 "NETLINK_DNRTMSG": syscall.NETLINK_DNRTMSG, 407 "NETLINK_KOBJECT_UEVENT": syscall.NETLINK_KOBJECT_UEVENT, 408 "NETLINK_GENERIC": syscall.NETLINK_GENERIC, 409 "NETLINK_SCSITRANSPORT": syscall.NETLINK_SCSITRANSPORT, 410 "NETLINK_ECRYPTFS": syscall.NETLINK_ECRYPTFS, 411 "NETLINK_RDMA": C.NETLINK_RDMA, 412 "NETLINK_CRYPTO": C.NETLINK_CRYPTO, 413 "NETLINK_INET_DIAG": C.NETLINK_INET_DIAG, // synonymous with NETLINK_SOCK_DIAG 414 415 // man 2 ptrace 416 "PTRACE_ATTACH": C.PTRACE_ATTACH, 417 "PTRACE_DETACH": C.PTRACE_DETACH, 418 "PTRACE_GETREGS": C.PTRACE_GETREGS, 419 "PTRACE_GETFPREGS": C.PTRACE_GETFPREGS, 420 "PTRACE_GETFPXREGS": C.PTRACE_GETFPXREGS, 421 "PTRACE_GETREGSET": C.PTRACE_GETREGSET, 422 "PTRACE_PEEKDATA": C.PTRACE_PEEKDATA, 423 // <linux/ptrace.h> and <sys/ptrace.h> have different spellings for PEEKUS{,E}R 424 "PTRACE_PEEKUSR": C.PTRACE_PEEKUSER, 425 "PTRACE_PEEKUSER": C.PTRACE_PEEKUSER, 426 "PTRACE_CONT": C.PTRACE_CONT, 427 } 428 429 // DpkgArchToScmpArch takes a dpkg architecture and converts it to 430 // the seccomp.ScmpArch as used in the libseccomp-golang library 431 func DpkgArchToScmpArch(dpkgArch string) seccomp.ScmpArch { 432 switch dpkgArch { 433 case "amd64": 434 return seccomp.ArchAMD64 435 case "arm64": 436 return seccomp.ArchARM64 437 case "armhf": 438 return seccomp.ArchARM 439 case "i386": 440 return seccomp.ArchX86 441 case "powerpc": 442 return seccomp.ArchPPC 443 case "ppc64": 444 return seccomp.ArchPPC64 445 case "ppc64el": 446 return seccomp.ArchPPC64LE 447 case "s390x": 448 return seccomp.ArchS390X 449 } 450 panic(fmt.Sprintf("cannot map dpkg arch %q to a seccomp arch", dpkgArch)) 451 } 452 453 // important for unit testing 454 type SeccompData C.kernel_seccomp_data 455 456 func (sc *SeccompData) SetNr(nr seccomp.ScmpSyscall) { 457 sc.nr = C.int(C.htot32(C.__u32(sc.arch), C.__u32(nr))) 458 } 459 func (sc *SeccompData) SetArch(arch uint32) { 460 sc.arch = C.htot32(C.__u32(arch), C.__u32(arch)) 461 } 462 func (sc *SeccompData) SetArgs(args [6]uint64) { 463 for i := range args { 464 sc.args[i] = C.htot64(sc.arch, C.__u64(args[i])) 465 } 466 } 467 468 // Only support negative args for syscalls where we understand the glibc/kernel 469 // prototypes and behavior. This lists all the syscalls that support negative 470 // arguments where we want to ignore the high 32 bits (ie, we'll mask it since 471 // the arg is known to be 32 bit (uid_t/gid_t) and the kernel accepts one 472 // or both of uint32(-1) and uint64(-1) and does its own masking). 473 var syscallsWithNegArgsMaskHi32 = map[string]bool{ 474 "chown": true, 475 "chown32": true, 476 "fchown": true, 477 "fchown32": true, 478 "fchownat": true, 479 "lchown": true, 480 "lchown32": true, 481 "setgid": true, 482 "setgid32": true, 483 "setregid": true, 484 "setregid32": true, 485 "setresgid": true, 486 "setresgid32": true, 487 "setreuid": true, 488 "setreuid32": true, 489 "setresuid": true, 490 "setresuid32": true, 491 "setuid": true, 492 "setuid32": true, 493 } 494 495 // The kernel uses uint32 for all syscall arguments, but seccomp takes a 496 // uint64. For unsigned ints in our policy, just read straight into uint32 497 // since we don't need to worry about sign extending. 498 // 499 // For negative signed ints in our policy, we first read in as int32, convert 500 // to uint32 and then again uint64 to avoid sign extension woes (see 501 // https://github.com/seccomp/libseccomp/issues/69). For syscalls that take 502 // a 64bit arg that we want to express in our policy, we can add an exception 503 // for reading into a uint64. For now there are no exceptions, so don't need to 504 // do anything extra. 505 func readNumber(token string, syscallName string) (uint64, error) { 506 if value, ok := seccompResolver[token]; ok { 507 return value, nil 508 } 509 510 if value, err := strconv.ParseUint(token, 10, 32); err == nil { 511 return value, nil 512 } 513 514 // Not a positive integer, see if negative is allowed for this syscall 515 if !syscallsWithNegArgsMaskHi32[syscallName] { 516 return 0, fmt.Errorf(`negative argument not supported with "%s"`, syscallName) 517 } 518 519 // It is, so try to parse as an int32 520 value, err := strconv.ParseInt(token, 10, 32) 521 if err != nil { 522 return 0, err 523 } 524 525 // convert the int32 to uint32 then to uint64 (see above) 526 return uint64(uint32(value)), nil 527 } 528 529 func parseLine(line string, secFilter *seccomp.ScmpFilter) error { 530 // ignore comments and empty lines 531 if strings.HasPrefix(line, "#") || line == "" { 532 return nil 533 } 534 535 // regular line 536 tokens := strings.Fields(line) 537 if len(tokens[1:]) > ScArgsMaxlength { 538 return fmt.Errorf("too many arguments specified for syscall '%s' in line %q", tokens[0], line) 539 } 540 541 // fish out syscall 542 syscallName := tokens[0] 543 secSyscall, err := seccomp.GetSyscallFromName(syscallName) 544 if err != nil { 545 // FIXME: use structed error in libseccomp-golang when 546 // https://github.com/seccomp/libseccomp-golang/pull/26 547 // gets merged. For now, ignore 548 // unknown syscalls 549 return nil 550 } 551 552 var conds []seccomp.ScmpCondition 553 for pos, arg := range tokens[1:] { 554 var cmpOp seccomp.ScmpCompareOp 555 var value uint64 556 var err error 557 558 if arg == "-" { // skip arg 559 continue 560 } 561 562 if strings.HasPrefix(arg, ">=") { 563 cmpOp = seccomp.CompareGreaterEqual 564 value, err = readNumber(arg[2:], syscallName) 565 } else if strings.HasPrefix(arg, "<=") { 566 cmpOp = seccomp.CompareLessOrEqual 567 value, err = readNumber(arg[2:], syscallName) 568 } else if strings.HasPrefix(arg, "!") { 569 cmpOp = seccomp.CompareNotEqual 570 value, err = readNumber(arg[1:], syscallName) 571 } else if strings.HasPrefix(arg, "<") { 572 cmpOp = seccomp.CompareLess 573 value, err = readNumber(arg[1:], syscallName) 574 } else if strings.HasPrefix(arg, ">") { 575 cmpOp = seccomp.CompareGreater 576 value, err = readNumber(arg[1:], syscallName) 577 } else if strings.HasPrefix(arg, "|") { 578 cmpOp = seccomp.CompareMaskedEqual 579 value, err = readNumber(arg[1:], syscallName) 580 } else if strings.HasPrefix(arg, "u:") { 581 cmpOp = seccomp.CompareEqual 582 value, err = findUid(arg[2:]) 583 if err != nil { 584 return fmt.Errorf("cannot parse token %q (line %q): %v", arg, line, err) 585 } 586 } else if strings.HasPrefix(arg, "g:") { 587 cmpOp = seccomp.CompareEqual 588 value, err = findGid(arg[2:]) 589 if err != nil { 590 return fmt.Errorf("cannot parse token %q (line %q): %v", arg, line, err) 591 } 592 } else { 593 cmpOp = seccomp.CompareEqual 594 value, err = readNumber(arg, syscallName) 595 } 596 if err != nil { 597 return fmt.Errorf("cannot parse token %q (line %q)", arg, line) 598 } 599 600 // For now only support EQ with negative args. If changing 601 // this, be sure to adjust readNumber accordingly and use 602 // libseccomp carefully. 603 if syscallsWithNegArgsMaskHi32[syscallName] { 604 if cmpOp != seccomp.CompareEqual { 605 return fmt.Errorf("cannot parse token %q (line %q): unsupported comparison", arg, line) 606 } 607 } 608 609 var scmpCond seccomp.ScmpCondition 610 if cmpOp == seccomp.CompareMaskedEqual { 611 scmpCond, err = seccomp.MakeCondition(uint(pos), cmpOp, value, value) 612 } else if syscallsWithNegArgsMaskHi32[syscallName] { 613 scmpCond, err = seccomp.MakeCondition(uint(pos), seccomp.CompareMaskedEqual, 0xFFFFFFFF, value) 614 } else { 615 scmpCond, err = seccomp.MakeCondition(uint(pos), cmpOp, value) 616 } 617 if err != nil { 618 return fmt.Errorf("cannot parse line %q: %s", line, err) 619 } 620 conds = append(conds, scmpCond) 621 } 622 623 // Default to adding a precise match if possible. Otherwise 624 // let seccomp figure out the architecture specifics. 625 if err = secFilter.AddRuleConditionalExact(secSyscall, seccomp.ActAllow, conds); err != nil { 626 err = secFilter.AddRuleConditional(secSyscall, seccomp.ActAllow, conds) 627 } 628 629 return err 630 } 631 632 // used to mock in tests 633 var ( 634 archDpkgArchitecture = arch.DpkgArchitecture 635 archDpkgKernelArchitecture = arch.DpkgKernelArchitecture 636 ) 637 638 var ( 639 dpkgArchitecture = archDpkgArchitecture() 640 dpkgKernelArchitecture = archDpkgKernelArchitecture() 641 ) 642 643 // For architectures that support a compat architecture, when the 644 // kernel and userspace match, add the compat arch, otherwise add 645 // the kernel arch to support the kernel's arch (eg, 64bit kernels with 646 // 32bit userspace). 647 func addSecondaryArches(secFilter *seccomp.ScmpFilter) error { 648 // note that all architecture strings are in the dpkg 649 // architecture notation 650 var compatArch seccomp.ScmpArch 651 652 // common case: kernel and userspace have the same arch. We 653 // add a compat architecture for some architectures that 654 // support it, e.g. on amd64 kernel and userland, we add 655 // compat i386 syscalls. 656 if dpkgArchitecture == dpkgKernelArchitecture { 657 switch archDpkgArchitecture() { 658 case "amd64": 659 compatArch = seccomp.ArchX86 660 case "arm64": 661 compatArch = seccomp.ArchARM 662 case "ppc64": 663 compatArch = seccomp.ArchPPC 664 } 665 } else { 666 // less common case: kernel and userspace have different archs 667 // so add a compat architecture that matches the kernel. E.g. 668 // an amd64 kernel with i386 userland needs the amd64 secondary 669 // arch added to support specialized snaps that might 670 // conditionally call 64bit code when the kernel supports it. 671 // Note that in this case snapd requests i386 (or arch 'all') 672 // snaps. While unusual from a traditional Linux distribution 673 // perspective, certain classes of embedded devices are known 674 // to use this configuration. 675 compatArch = DpkgArchToScmpArch(archDpkgKernelArchitecture()) 676 } 677 678 if compatArch != seccomp.ArchInvalid { 679 return secFilter.AddArch(compatArch) 680 } 681 682 return nil 683 } 684 685 var errnoOnDenial int16 = C.EPERM 686 687 func preprocess(content []byte) (unrestricted, complain bool) { 688 scanner := bufio.NewScanner(bytes.NewBuffer(content)) 689 for scanner.Scan() { 690 line := strings.TrimSpace(scanner.Text()) 691 switch line { 692 case "@unrestricted": 693 unrestricted = true 694 case "@complain": 695 complain = true 696 } 697 } 698 return unrestricted, complain 699 } 700 701 // With golang-seccomp <= 0.9.0, seccomp.ActLog is not available so guess 702 // at the ActLog value by adding one to ActAllow and then verify that the 703 // string representation is what we expect for ActLog. The value and string is 704 // defined in https://github.com/seccomp/libseccomp-golang/pull/29. 705 // 706 // Ultimately, the fix for this workaround is to be able to use the GetApi() 707 // function created in the PR above. It'll tell us if the kernel, libseccomp, 708 // and libseccomp-golang all support ActLog, but GetApi() is also not available 709 // in golang-seccomp <= 0.9.0. 710 const actLog seccomp.ScmpAction = seccomp.ActAllow + 1 711 712 func actLogSupported() bool { 713 return actLog.String() == "Action: Log system call" 714 } 715 716 func complainAction() seccomp.ScmpAction { 717 // XXX: Work around some distributions not having a new enough 718 // libseccomp-golang that declares ActLog. 719 if actLogSupported() { 720 return actLog 721 } 722 723 // Because ActLog is functionally ActAllow with logging, if we don't 724 // support ActLog, fallback to ActLog. 725 return seccomp.ActAllow 726 } 727 728 func compile(content []byte, out string) error { 729 var err error 730 var secFilter *seccomp.ScmpFilter 731 732 unrestricted, complain := preprocess(content) 733 switch { 734 case unrestricted: 735 return osutil.AtomicWrite(out, bytes.NewBufferString("@unrestricted\n"), 0644, 0) 736 case complain: 737 var complainAct seccomp.ScmpAction = complainAction() 738 739 secFilter, err = seccomp.NewFilter(complainAct) 740 if err != nil { 741 if complainAct != seccomp.ActAllow { 742 // ActLog is only supported in newer versions 743 // of the kernel, libseccomp, and 744 // libseccomp-golang. Attempt to fall back to 745 // ActAllow before erroring out. 746 complainAct = seccomp.ActAllow 747 secFilter, err = seccomp.NewFilter(complainAct) 748 } 749 } 750 751 // Set unrestricted to 'true' to fallback to the pre-ActLog 752 // behavior of simply setting the allow filter without adding 753 // any rules. 754 if complainAct == seccomp.ActAllow { 755 unrestricted = true 756 } 757 default: 758 secFilter, err = seccomp.NewFilter(seccomp.ActErrno.SetReturnCode(errnoOnDenial)) 759 } 760 if err != nil { 761 return fmt.Errorf("cannot create seccomp filter: %s", err) 762 } 763 if err := addSecondaryArches(secFilter); err != nil { 764 return err 765 } 766 767 if !unrestricted { 768 scanner := bufio.NewScanner(bytes.NewBuffer(content)) 769 for scanner.Scan() { 770 if err := parseLine(scanner.Text(), secFilter); err != nil { 771 return fmt.Errorf("cannot parse line: %s", err) 772 } 773 } 774 if scanner.Err(); err != nil { 775 return err 776 } 777 } 778 779 if osutil.GetenvBool("SNAP_SECCOMP_DEBUG") { 780 secFilter.ExportPFC(os.Stdout) 781 } 782 783 // write atomically 784 fout, err := osutil.NewAtomicFile(out, 0644, 0, osutil.NoChown, osutil.NoChown) 785 if err != nil { 786 return err 787 } 788 // Cancel once Committed is a NOP 789 defer fout.Cancel() 790 791 if err := secFilter.ExportBPF(fout.File); err != nil { 792 return err 793 } 794 return fout.Commit() 795 } 796 797 // caches for uid and gid lookups 798 var uidCache = make(map[string]uint64) 799 var gidCache = make(map[string]uint64) 800 801 // findUid returns the identifier of the given UNIX user name. 802 func findUid(username string) (uint64, error) { 803 if uid, ok := uidCache[username]; ok { 804 return uid, nil 805 } 806 if !osutil.IsValidUsername(username) { 807 return 0, fmt.Errorf("%q must be a valid username", username) 808 } 809 uid, err := osutil.FindUid(username) 810 if err == nil { 811 uidCache[username] = uid 812 } 813 return uid, err 814 } 815 816 // findGid returns the identifier of the given UNIX group name. 817 func findGid(group string) (uint64, error) { 818 if gid, ok := gidCache[group]; ok { 819 return gid, nil 820 } 821 if !osutil.IsValidUsername(group) { 822 return 0, fmt.Errorf("%q must be a valid group name", group) 823 } 824 gid, err := osutil.FindGid(group) 825 if err == nil { 826 gidCache[group] = gid 827 } 828 return gid, err 829 } 830 831 func showSeccompLibraryVersion() error { 832 major, minor, micro := seccomp.GetLibraryVersion() 833 fmt.Fprintf(os.Stdout, "%d.%d.%d\n", major, minor, micro) 834 return nil 835 } 836 837 func main() { 838 var err error 839 var content []byte 840 841 if len(os.Args) < 2 { 842 fmt.Printf("%s: need a command\n", os.Args[0]) 843 os.Exit(1) 844 } 845 846 cmd := os.Args[1] 847 switch cmd { 848 case "compile": 849 if len(os.Args) < 4 { 850 fmt.Println("compile needs an input and output file") 851 os.Exit(1) 852 } 853 content, err = ioutil.ReadFile(os.Args[2]) 854 if err != nil { 855 break 856 } 857 err = compile(content, os.Args[3]) 858 case "library-version": 859 err = showSeccompLibraryVersion() 860 case "version-info": 861 err = showVersionInfo() 862 default: 863 err = fmt.Errorf("unsupported argument %q", cmd) 864 } 865 866 if err != nil { 867 fmt.Fprintf(os.Stderr, "error: %v\n", err) 868 os.Exit(1) 869 } 870 }