github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/seccomp/seccomp_rules.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package seccomp
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"golang.org/x/sys/unix"
    21  )
    22  
    23  // The offsets are based on the following struct in include/linux/seccomp.h.
    24  //
    25  //	struct seccomp_data {
    26  //		int nr;
    27  //		__u32 arch;
    28  //		__u64 instruction_pointer;
    29  //		__u64 args[6];
    30  //	};
    31  const (
    32  	seccompDataOffsetNR     = 0
    33  	seccompDataOffsetArch   = 4
    34  	seccompDataOffsetIPLow  = 8
    35  	seccompDataOffsetIPHigh = 12
    36  	seccompDataOffsetArgs   = 16
    37  )
    38  
    39  func seccompDataOffsetArgLow(i int) uint32 {
    40  	return uint32(seccompDataOffsetArgs + i*8)
    41  }
    42  
    43  func seccompDataOffsetArgHigh(i int) uint32 {
    44  	return seccompDataOffsetArgLow(i) + 4
    45  }
    46  
    47  // MatchAny is marker to indicate any value will be accepted.
    48  type MatchAny struct{}
    49  
    50  func (a MatchAny) String() (s string) {
    51  	return "*"
    52  }
    53  
    54  // EqualTo specifies a value that needs to be strictly matched.
    55  type EqualTo uintptr
    56  
    57  func (a EqualTo) String() (s string) {
    58  	return fmt.Sprintf("== %#x", uintptr(a))
    59  }
    60  
    61  // NotEqual specifies a value that is strictly not equal.
    62  type NotEqual uintptr
    63  
    64  func (a NotEqual) String() (s string) {
    65  	return fmt.Sprintf("!= %#x", uintptr(a))
    66  }
    67  
    68  // GreaterThan specifies a value that needs to be strictly smaller.
    69  type GreaterThan uintptr
    70  
    71  func (a GreaterThan) String() (s string) {
    72  	return fmt.Sprintf("> %#x", uintptr(a))
    73  }
    74  
    75  // GreaterThanOrEqual specifies a value that needs to be smaller or equal.
    76  type GreaterThanOrEqual uintptr
    77  
    78  func (a GreaterThanOrEqual) String() (s string) {
    79  	return fmt.Sprintf(">= %#x", uintptr(a))
    80  }
    81  
    82  // LessThan specifies a value that needs to be strictly greater.
    83  type LessThan uintptr
    84  
    85  func (a LessThan) String() (s string) {
    86  	return fmt.Sprintf("< %#x", uintptr(a))
    87  }
    88  
    89  // LessThanOrEqual specifies a value that needs to be greater or equal.
    90  type LessThanOrEqual uintptr
    91  
    92  func (a LessThanOrEqual) String() (s string) {
    93  	return fmt.Sprintf("<= %#x", uintptr(a))
    94  }
    95  
    96  type maskedEqual struct {
    97  	mask  uintptr
    98  	value uintptr
    99  }
   100  
   101  func (a maskedEqual) String() (s string) {
   102  	return fmt.Sprintf("& %#x == %#x", a.mask, a.value)
   103  }
   104  
   105  // MaskedEqual specifies a value that matches the input after the input is
   106  // masked (bitwise &) against the given mask. Can be used to verify that input
   107  // only includes certain approved flags.
   108  func MaskedEqual(mask, value uintptr) any {
   109  	return maskedEqual{
   110  		mask:  mask,
   111  		value: value,
   112  	}
   113  }
   114  
   115  // Rule stores the allowed syscall arguments.
   116  //
   117  // For example:
   118  //
   119  //	rule := Rule {
   120  //		EqualTo(linux.ARCH_GET_FS | linux.ARCH_SET_FS), // arg0
   121  //	}
   122  type Rule [7]any // 6 arguments + RIP
   123  
   124  // RuleIP indicates what rules in the Rule array have to be applied to
   125  // instruction pointer.
   126  const RuleIP = 6
   127  
   128  func (r Rule) String() (s string) {
   129  	if len(r) == 0 {
   130  		return
   131  	}
   132  	s += "( "
   133  	for _, arg := range r {
   134  		if arg != nil {
   135  			s += fmt.Sprintf("%v ", arg)
   136  		}
   137  	}
   138  	s += ")"
   139  	return
   140  }
   141  
   142  // SyscallRules stores a map of OR'ed argument rules indexed by the syscall number.
   143  // If the 'Rules' is empty, we treat it as any argument is allowed.
   144  //
   145  // For example:
   146  //
   147  //	rules := SyscallRules{
   148  //	       syscall.SYS_FUTEX: []Rule{
   149  //	               {
   150  //	                       MatchAny{},
   151  //	                       EqualTo(linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG),
   152  //	               }, // OR
   153  //	               {
   154  //	                       MatchAny{},
   155  //	                       EqualTo(linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG),
   156  //	               },
   157  //	       },
   158  //	       syscall.SYS_GETPID: []Rule{},
   159  //
   160  // }
   161  type SyscallRules map[uintptr][]Rule
   162  
   163  // NewSyscallRules returns a new SyscallRules.
   164  func NewSyscallRules() SyscallRules {
   165  	return make(map[uintptr][]Rule)
   166  }
   167  
   168  // AddRule adds the given rule. It will create a new entry for a new syscall, otherwise
   169  // it will append to the existing rules.
   170  func (sr SyscallRules) AddRule(sysno uintptr, r Rule) {
   171  	if cur, ok := sr[sysno]; ok {
   172  		// An empty rules means allow all. Honor it when more rules are added.
   173  		if len(cur) == 0 {
   174  			sr[sysno] = append(sr[sysno], Rule{})
   175  		}
   176  		sr[sysno] = append(sr[sysno], r)
   177  	} else {
   178  		sr[sysno] = []Rule{r}
   179  	}
   180  }
   181  
   182  // Merge merges the given SyscallRules.
   183  func (sr SyscallRules) Merge(rules SyscallRules) {
   184  	for sysno, rs := range rules {
   185  		if cur, ok := sr[sysno]; ok {
   186  			// An empty rules means allow all. Honor it when more rules are added.
   187  			if len(cur) == 0 {
   188  				sr[sysno] = append(sr[sysno], Rule{})
   189  			}
   190  			if len(rs) == 0 {
   191  				rs = []Rule{{}}
   192  			}
   193  			sr[sysno] = append(sr[sysno], rs...)
   194  		} else {
   195  			sr[sysno] = rs
   196  		}
   197  	}
   198  }
   199  
   200  // DenyNewExecMappings is a set of rules that denies creating new executable
   201  // mappings and converting existing ones.
   202  var DenyNewExecMappings = SyscallRules{
   203  	unix.SYS_MMAP: []Rule{
   204  		{
   205  			MatchAny{},
   206  			MatchAny{},
   207  			MaskedEqual(unix.PROT_EXEC, unix.PROT_EXEC),
   208  		},
   209  	},
   210  	unix.SYS_MPROTECT: []Rule{
   211  		{
   212  			MatchAny{},
   213  			MatchAny{},
   214  			MaskedEqual(unix.PROT_EXEC, unix.PROT_EXEC),
   215  		},
   216  	},
   217  }