github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/libcontainer/seccomp/seccomp_linux.go (about)

     1  // +build linux,cgo,seccomp
     2  
     3  package seccomp
     4  
     5  import (
     6  	"bufio"
     7  	"fmt"
     8  	"os"
     9  	"strings"
    10  	"syscall"
    11  
    12  	"github.com/opencontainers/runc/libcontainer/configs"
    13  	libseccomp "github.com/seccomp/libseccomp-golang"
    14  )
    15  
    16  var (
    17  	actAllow = libseccomp.ActAllow
    18  	actTrap  = libseccomp.ActTrap
    19  	actKill  = libseccomp.ActKill
    20  	actTrace = libseccomp.ActTrace.SetReturnCode(int16(syscall.EPERM))
    21  	actErrno = libseccomp.ActErrno.SetReturnCode(int16(syscall.EPERM))
    22  
    23  	// SeccompModeFilter refers to the syscall argument SECCOMP_MODE_FILTER.
    24  	SeccompModeFilter = uintptr(2)
    25  )
    26  
    27  // Filters given syscalls in a container, preventing them from being used
    28  // Started in the container init process, and carried over to all child processes
    29  // Setns calls, however, require a separate invocation, as they are not children
    30  // of the init until they join the namespace
    31  func InitSeccomp(config *configs.Seccomp) error {
    32  	if config == nil {
    33  		return fmt.Errorf("cannot initialize Seccomp - nil config passed")
    34  	}
    35  
    36  	defaultAction, err := getAction(config.DefaultAction)
    37  	if err != nil {
    38  		return fmt.Errorf("error initializing seccomp - invalid default action")
    39  	}
    40  
    41  	filter, err := libseccomp.NewFilter(defaultAction)
    42  	if err != nil {
    43  		return fmt.Errorf("error creating filter: %s", err)
    44  	}
    45  
    46  	// Add extra architectures
    47  	for _, arch := range config.Architectures {
    48  		scmpArch, err := libseccomp.GetArchFromString(arch)
    49  		if err != nil {
    50  			return err
    51  		}
    52  
    53  		if err := filter.AddArch(scmpArch); err != nil {
    54  			return err
    55  		}
    56  	}
    57  
    58  	// Unset no new privs bit
    59  	if err := filter.SetNoNewPrivsBit(false); err != nil {
    60  		return fmt.Errorf("error setting no new privileges: %s", err)
    61  	}
    62  
    63  	// Add a rule for each syscall
    64  	for _, call := range config.Syscalls {
    65  		if call == nil {
    66  			return fmt.Errorf("encountered nil syscall while initializing Seccomp")
    67  		}
    68  
    69  		if err = matchCall(filter, call); err != nil {
    70  			return err
    71  		}
    72  	}
    73  
    74  	if err = filter.Load(); err != nil {
    75  		return fmt.Errorf("error loading seccomp filter into kernel: %s", err)
    76  	}
    77  
    78  	return nil
    79  }
    80  
    81  // IsEnabled returns if the kernel has been configured to support seccomp.
    82  func IsEnabled() bool {
    83  	// Try to read from /proc/self/status for kernels > 3.8
    84  	s, err := parseStatusFile("/proc/self/status")
    85  	if err != nil {
    86  		// Check if Seccomp is supported, via CONFIG_SECCOMP.
    87  		if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_GET_SECCOMP, 0, 0); err != syscall.EINVAL {
    88  			// Make sure the kernel has CONFIG_SECCOMP_FILTER.
    89  			if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_SECCOMP, SeccompModeFilter, 0); err != syscall.EINVAL {
    90  				return true
    91  			}
    92  		}
    93  		return false
    94  	}
    95  	_, ok := s["Seccomp"]
    96  	return ok
    97  }
    98  
    99  // Convert Libcontainer Action to Libseccomp ScmpAction
   100  func getAction(act configs.Action) (libseccomp.ScmpAction, error) {
   101  	switch act {
   102  	case configs.Kill:
   103  		return actKill, nil
   104  	case configs.Errno:
   105  		return actErrno, nil
   106  	case configs.Trap:
   107  		return actTrap, nil
   108  	case configs.Allow:
   109  		return actAllow, nil
   110  	case configs.Trace:
   111  		return actTrace, nil
   112  	default:
   113  		return libseccomp.ActInvalid, fmt.Errorf("invalid action, cannot use in rule")
   114  	}
   115  }
   116  
   117  // Convert Libcontainer Operator to Libseccomp ScmpCompareOp
   118  func getOperator(op configs.Operator) (libseccomp.ScmpCompareOp, error) {
   119  	switch op {
   120  	case configs.EqualTo:
   121  		return libseccomp.CompareEqual, nil
   122  	case configs.NotEqualTo:
   123  		return libseccomp.CompareNotEqual, nil
   124  	case configs.GreaterThan:
   125  		return libseccomp.CompareGreater, nil
   126  	case configs.GreaterThanOrEqualTo:
   127  		return libseccomp.CompareGreaterEqual, nil
   128  	case configs.LessThan:
   129  		return libseccomp.CompareLess, nil
   130  	case configs.LessThanOrEqualTo:
   131  		return libseccomp.CompareLessOrEqual, nil
   132  	case configs.MaskEqualTo:
   133  		return libseccomp.CompareMaskedEqual, nil
   134  	default:
   135  		return libseccomp.CompareInvalid, fmt.Errorf("invalid operator, cannot use in rule")
   136  	}
   137  }
   138  
   139  // Convert Libcontainer Arg to Libseccomp ScmpCondition
   140  func getCondition(arg *configs.Arg) (libseccomp.ScmpCondition, error) {
   141  	cond := libseccomp.ScmpCondition{}
   142  
   143  	if arg == nil {
   144  		return cond, fmt.Errorf("cannot convert nil to syscall condition")
   145  	}
   146  
   147  	op, err := getOperator(arg.Op)
   148  	if err != nil {
   149  		return cond, err
   150  	}
   151  
   152  	return libseccomp.MakeCondition(arg.Index, op, arg.Value, arg.ValueTwo)
   153  }
   154  
   155  // Add a rule to match a single syscall
   156  func matchCall(filter *libseccomp.ScmpFilter, call *configs.Syscall) error {
   157  	if call == nil || filter == nil {
   158  		return fmt.Errorf("cannot use nil as syscall to block")
   159  	}
   160  
   161  	if len(call.Name) == 0 {
   162  		return fmt.Errorf("empty string is not a valid syscall")
   163  	}
   164  
   165  	// If we can't resolve the syscall, assume it's not supported on this kernel
   166  	// Ignore it, don't error out
   167  	callNum, err := libseccomp.GetSyscallFromName(call.Name)
   168  	if err != nil {
   169  		return nil
   170  	}
   171  
   172  	// Convert the call's action to the libseccomp equivalent
   173  	callAct, err := getAction(call.Action)
   174  	if err != nil {
   175  		return err
   176  	}
   177  
   178  	// Unconditional match - just add the rule
   179  	if len(call.Args) == 0 {
   180  		if err = filter.AddRule(callNum, callAct); err != nil {
   181  			return err
   182  		}
   183  	} else {
   184  		// Conditional match - convert the per-arg rules into library format
   185  		conditions := []libseccomp.ScmpCondition{}
   186  
   187  		for _, cond := range call.Args {
   188  			newCond, err := getCondition(cond)
   189  			if err != nil {
   190  				return err
   191  			}
   192  
   193  			conditions = append(conditions, newCond)
   194  		}
   195  
   196  		if err = filter.AddRuleConditional(callNum, callAct, conditions); err != nil {
   197  			return err
   198  		}
   199  	}
   200  
   201  	return nil
   202  }
   203  
   204  func parseStatusFile(path string) (map[string]string, error) {
   205  	f, err := os.Open(path)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	defer f.Close()
   210  
   211  	s := bufio.NewScanner(f)
   212  	status := make(map[string]string)
   213  
   214  	for s.Scan() {
   215  		if err := s.Err(); err != nil {
   216  			return nil, err
   217  		}
   218  
   219  		text := s.Text()
   220  		parts := strings.Split(text, ":")
   221  
   222  		if len(parts) <= 1 {
   223  			continue
   224  		}
   225  
   226  		status[parts[0]] = parts[1]
   227  	}
   228  	return status, nil
   229  }