github.com/stulluk/snapd@v0.0.0-20210611110309-f6d5d5bd24b0/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  }