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