Compare commits
	
		
			11 Commits
		
	
	
		
			7106b00968
			...
			21735a8abe
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 21735a8abe | |||
| 34272672b1 | |||
| 7b96cd6ded | |||
| 163f15e93f | |||
| 016da20443 | |||
| 37780456a7 | |||
| efacaa40fa | |||
| ad6d0ee55f | |||
| cf791469d8 | |||
| be14421775 | |||
| 045983d7f4 | 
@ -1,50 +0,0 @@
 | 
				
			|||||||
name: Build
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
on:
 | 
					 | 
				
			||||||
  - push
 | 
					 | 
				
			||||||
  - pull_request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
jobs:
 | 
					 | 
				
			||||||
  dist:
 | 
					 | 
				
			||||||
    name: Create distribution
 | 
					 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					 | 
				
			||||||
    steps:
 | 
					 | 
				
			||||||
      - name: Checkout
 | 
					 | 
				
			||||||
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Install Nix
 | 
					 | 
				
			||||||
        uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          # explicitly enable sandbox
 | 
					 | 
				
			||||||
          install_options: --daemon
 | 
					 | 
				
			||||||
          extra_nix_config: |
 | 
					 | 
				
			||||||
            sandbox = true
 | 
					 | 
				
			||||||
            system-features = nixos-test benchmark big-parallel kvm
 | 
					 | 
				
			||||||
          enable_kvm: true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Ensure environment
 | 
					 | 
				
			||||||
        run: >-
 | 
					 | 
				
			||||||
          apt-get update && apt-get install -y sqlite3
 | 
					 | 
				
			||||||
        if: ${{ runner.os == 'Linux' }}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Restore Nix store
 | 
					 | 
				
			||||||
        uses: nix-community/cache-nix-action@v5
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          primary-key: nix-small-${{ runner.os }}-${{ hashFiles('**/*.nix') }}
 | 
					 | 
				
			||||||
          restore-prefixes-first-match: nix-small-${{ runner.os }}-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Build for test
 | 
					 | 
				
			||||||
        id: build-test
 | 
					 | 
				
			||||||
        run: >-
 | 
					 | 
				
			||||||
          export FORTIFY_REV="$(git rev-parse --short HEAD)" &&
 | 
					 | 
				
			||||||
          sed -i.old 's/version = /version = "0.0.0-'$FORTIFY_REV'"; # version = /' package.nix &&
 | 
					 | 
				
			||||||
          nix build --print-out-paths --print-build-logs .#dist &&
 | 
					 | 
				
			||||||
          mv package.nix.old package.nix &&
 | 
					 | 
				
			||||||
          echo "rev=$FORTIFY_REV" >> $GITHUB_OUTPUT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      - name: Upload test build
 | 
					 | 
				
			||||||
        uses: actions/upload-artifact@v3
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          name: "fortify-${{ steps.build-test.outputs.rev }}"
 | 
					 | 
				
			||||||
          path: result/*
 | 
					 | 
				
			||||||
          retention-days: 1
 | 
					 | 
				
			||||||
@ -5,7 +5,7 @@ on:
 | 
				
			|||||||
  - pull_request
 | 
					  - pull_request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  tests:
 | 
					  test:
 | 
				
			||||||
    name: Run NixOS test
 | 
					    name: Run NixOS test
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
@ -30,8 +30,13 @@ jobs:
 | 
				
			|||||||
      - name: Restore Nix store
 | 
					      - name: Restore Nix store
 | 
				
			||||||
        uses: nix-community/cache-nix-action@v5
 | 
					        uses: nix-community/cache-nix-action@v5
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix') }}
 | 
					          primary-key: flake-check-${{ runner.os }}-${{ hashFiles('**/*.nix') }}
 | 
				
			||||||
          restore-prefixes-first-match: nix-${{ runner.os }}-
 | 
					          restore-prefixes-first-match: flake-check-${{ runner.os }}-
 | 
				
			||||||
 | 
					          gc-max-store-size-linux: 1073741824
 | 
				
			||||||
 | 
					          purge: true
 | 
				
			||||||
 | 
					          purge-prefixes: flake-check-${{ runner.os }}-
 | 
				
			||||||
 | 
					          purge-created: 60
 | 
				
			||||||
 | 
					          purge-primary-key: never
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Run tests
 | 
					      - name: Run tests
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
@ -41,6 +46,55 @@ jobs:
 | 
				
			|||||||
      - name: Upload test output
 | 
					      - name: Upload test output
 | 
				
			||||||
        uses: actions/upload-artifact@v3
 | 
					        uses: actions/upload-artifact@v3
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          name: "result"
 | 
					          name: "nixos-vm-output"
 | 
				
			||||||
 | 
					          path: result/*
 | 
				
			||||||
 | 
					          retention-days: 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  dist:
 | 
				
			||||||
 | 
					    name: Create distribution
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - name: Checkout
 | 
				
			||||||
 | 
					        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Install Nix
 | 
				
			||||||
 | 
					        uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          # explicitly enable sandbox
 | 
				
			||||||
 | 
					          install_options: --daemon
 | 
				
			||||||
 | 
					          extra_nix_config: |
 | 
				
			||||||
 | 
					            sandbox = true
 | 
				
			||||||
 | 
					            system-features = nixos-test benchmark big-parallel kvm
 | 
				
			||||||
 | 
					          enable_kvm: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Ensure environment
 | 
				
			||||||
 | 
					        run: >-
 | 
				
			||||||
 | 
					          apt-get update && apt-get install -y sqlite3
 | 
				
			||||||
 | 
					        if: ${{ runner.os == 'Linux' }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Restore Nix store
 | 
				
			||||||
 | 
					        uses: nix-community/cache-nix-action@v5
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          primary-key: build-dist-${{ runner.os }}-${{ hashFiles('**/*.nix') }}
 | 
				
			||||||
 | 
					          restore-prefixes-first-match: build-dist-${{ runner.os }}-
 | 
				
			||||||
 | 
					          gc-max-store-size-linux: 1073741824
 | 
				
			||||||
 | 
					          purge: true
 | 
				
			||||||
 | 
					          purge-prefixes: build-dist-${{ runner.os }}-
 | 
				
			||||||
 | 
					          purge-created: 60
 | 
				
			||||||
 | 
					          purge-primary-key: never
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Build for test
 | 
				
			||||||
 | 
					        id: build-test
 | 
				
			||||||
 | 
					        run: >-
 | 
				
			||||||
 | 
					          export FORTIFY_REV="$(git rev-parse --short HEAD)" &&
 | 
				
			||||||
 | 
					          sed -i.old 's/version = /version = "0.0.0-'$FORTIFY_REV'"; # version = /' package.nix &&
 | 
				
			||||||
 | 
					          nix build --print-out-paths --print-build-logs .#dist &&
 | 
				
			||||||
 | 
					          mv package.nix.old package.nix &&
 | 
				
			||||||
 | 
					          echo "rev=$FORTIFY_REV" >> $GITHUB_OUTPUT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Upload test build
 | 
				
			||||||
 | 
					        uses: actions/upload-artifact@v3
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          name: "fortify-${{ steps.build-test.outputs.rev }}"
 | 
				
			||||||
          path: result/*
 | 
					          path: result/*
 | 
				
			||||||
          retention-days: 1
 | 
					          retention-days: 1
 | 
				
			||||||
 | 
				
			|||||||
@ -1,95 +0,0 @@
 | 
				
			|||||||
package bwrap
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/internal/fmsg"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type SyscallPolicy struct {
 | 
					 | 
				
			||||||
	DenyDevel bool `json:"deny_devel"`
 | 
					 | 
				
			||||||
	Multiarch bool `json:"multiarch"`
 | 
					 | 
				
			||||||
	Linux32   bool `json:"linux32"`
 | 
					 | 
				
			||||||
	Can       bool `json:"can"`
 | 
					 | 
				
			||||||
	Bluetooth bool `json:"bluetooth"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type seccompBuilder struct {
 | 
					 | 
				
			||||||
	config *Config
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *seccompBuilder) Len() int {
 | 
					 | 
				
			||||||
	if s == nil {
 | 
					 | 
				
			||||||
		return 0
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return 2
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *seccompBuilder) Append(args *[]string, extraFiles *[]*os.File) error {
 | 
					 | 
				
			||||||
	if s == nil {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if f, err := s.config.resolveSeccomp(); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		extraFile(args, extraFiles, positionalArgs[Seccomp], f)
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *Config) resolveSeccomp() (*os.File, error) {
 | 
					 | 
				
			||||||
	if c.Syscall == nil {
 | 
					 | 
				
			||||||
		return nil, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// resolve seccomp filter opts
 | 
					 | 
				
			||||||
	var (
 | 
					 | 
				
			||||||
		opts    syscallOpts
 | 
					 | 
				
			||||||
		optd    []string
 | 
					 | 
				
			||||||
		optCond = [...]struct {
 | 
					 | 
				
			||||||
			v bool
 | 
					 | 
				
			||||||
			o syscallOpts
 | 
					 | 
				
			||||||
			d string
 | 
					 | 
				
			||||||
		}{
 | 
					 | 
				
			||||||
			{!c.UserNS, flagDenyNS, "denyns"},
 | 
					 | 
				
			||||||
			{c.NewSession, flagDenyTTY, "denytty"},
 | 
					 | 
				
			||||||
			{c.Syscall.DenyDevel, flagDenyDevel, "denydevel"},
 | 
					 | 
				
			||||||
			{c.Syscall.Multiarch, flagMultiarch, "multiarch"},
 | 
					 | 
				
			||||||
			{c.Syscall.Linux32, flagLinux32, "linux32"},
 | 
					 | 
				
			||||||
			{c.Syscall.Can, flagCan, "can"},
 | 
					 | 
				
			||||||
			{c.Syscall.Bluetooth, flagBluetooth, "bluetooth"},
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	if CPrintln != nil {
 | 
					 | 
				
			||||||
		optd = make([]string, 1, len(optCond)+1)
 | 
					 | 
				
			||||||
		optd[0] = "common"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, opt := range optCond {
 | 
					 | 
				
			||||||
		if opt.v {
 | 
					 | 
				
			||||||
			opts |= opt.o
 | 
					 | 
				
			||||||
			if fmsg.Verbose() {
 | 
					 | 
				
			||||||
				optd = append(optd, opt.d)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if CPrintln != nil {
 | 
					 | 
				
			||||||
		CPrintln(fmt.Sprintf("seccomp flags: %s", optd))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// export seccomp filter to tmpfile
 | 
					 | 
				
			||||||
	if f, err := tmpfile(); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		return f, exportAndSeek(f, opts)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func exportAndSeek(f *os.File, opts syscallOpts) error {
 | 
					 | 
				
			||||||
	if err := exportFilter(f.Fd(), opts); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, err := f.Seek(0, io.SeekStart)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,83 +1,90 @@
 | 
				
			|||||||
package bwrap
 | 
					package bwrap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
#cgo linux pkg-config: --static libseccomp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "seccomp-export.h"
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
import "C"
 | 
					 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"runtime"
 | 
					
 | 
				
			||||||
 | 
						"git.gensokyo.uk/security/fortify/helper/seccomp"
 | 
				
			||||||
 | 
						"git.gensokyo.uk/security/fortify/internal/fmsg"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var CPrintln func(v ...any)
 | 
					type SyscallPolicy struct {
 | 
				
			||||||
 | 
						// disable fortify extensions
 | 
				
			||||||
var resErr = [...]error{
 | 
						Compat bool `json:"compat"`
 | 
				
			||||||
	0: nil,
 | 
						// deny development syscalls
 | 
				
			||||||
	1: errors.New("seccomp_init failed"),
 | 
						DenyDevel bool `json:"deny_devel"`
 | 
				
			||||||
	2: errors.New("seccomp_arch_add failed"),
 | 
						// deny multiarch/emulation syscalls
 | 
				
			||||||
	3: errors.New("seccomp_arch_add failed (multiarch)"),
 | 
						Multiarch bool `json:"multiarch"`
 | 
				
			||||||
	4: errors.New("internal libseccomp failure"),
 | 
						// allow PER_LINUX32
 | 
				
			||||||
	5: errors.New("seccomp_rule_add failed"),
 | 
						Linux32 bool `json:"linux32"`
 | 
				
			||||||
	6: errors.New("seccomp_export_bpf failed"),
 | 
						// allow AF_CAN
 | 
				
			||||||
 | 
						Can bool `json:"can"`
 | 
				
			||||||
 | 
						// allow AF_BLUETOOTH
 | 
				
			||||||
 | 
						Bluetooth bool `json:"bluetooth"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type (
 | 
					type seccompBuilder struct {
 | 
				
			||||||
	syscallOpts = C.f_syscall_opts
 | 
						config *Config
 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	flagDenyNS    syscallOpts = C.F_DENY_NS
 | 
					 | 
				
			||||||
	flagDenyTTY   syscallOpts = C.F_DENY_TTY
 | 
					 | 
				
			||||||
	flagDenyDevel syscallOpts = C.F_DENY_DEVEL
 | 
					 | 
				
			||||||
	flagMultiarch syscallOpts = C.F_MULTIARCH
 | 
					 | 
				
			||||||
	flagLinux32   syscallOpts = C.F_LINUX32
 | 
					 | 
				
			||||||
	flagCan       syscallOpts = C.F_CAN
 | 
					 | 
				
			||||||
	flagBluetooth syscallOpts = C.F_BLUETOOTH
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func tmpfile() (*os.File, error) {
 | 
					 | 
				
			||||||
	fd, err := C.f_tmpfile_fd()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return os.NewFile(uintptr(fd), "tmpfile"), err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func exportFilter(fd uintptr, opts syscallOpts) error {
 | 
					func (s *seccompBuilder) Len() int {
 | 
				
			||||||
	var (
 | 
						if s == nil {
 | 
				
			||||||
		arch      C.uint32_t = 0
 | 
							return 0
 | 
				
			||||||
		multiarch C.uint32_t = 0
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	switch runtime.GOARCH {
 | 
					 | 
				
			||||||
	case "386":
 | 
					 | 
				
			||||||
		arch = C.SCMP_ARCH_X86
 | 
					 | 
				
			||||||
	case "amd64":
 | 
					 | 
				
			||||||
		arch = C.SCMP_ARCH_X86_64
 | 
					 | 
				
			||||||
		multiarch = C.SCMP_ARCH_X86
 | 
					 | 
				
			||||||
	case "arm":
 | 
					 | 
				
			||||||
		arch = C.SCMP_ARCH_ARM
 | 
					 | 
				
			||||||
	case "arm64":
 | 
					 | 
				
			||||||
		arch = C.SCMP_ARCH_AARCH64
 | 
					 | 
				
			||||||
		multiarch = C.SCMP_ARCH_ARM
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return 2
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res, err := C.f_export_bpf(C.int(fd), arch, multiarch, opts)
 | 
					func (s *seccompBuilder) Append(args *[]string, extraFiles *[]*os.File) error {
 | 
				
			||||||
	if re := resErr[res]; re != nil {
 | 
						if s == nil {
 | 
				
			||||||
		if err == nil {
 | 
							return nil
 | 
				
			||||||
			return re
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return fmt.Errorf("%s: %v", re.Error(), err)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if f, err := s.config.resolveSeccomp(); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
}
 | 
						} else {
 | 
				
			||||||
 | 
							extraFile(args, extraFiles, positionalArgs[Seccomp], f)
 | 
				
			||||||
//export F_println
 | 
							return nil
 | 
				
			||||||
func F_println(v *C.char) {
 | 
					 | 
				
			||||||
	if CPrintln != nil {
 | 
					 | 
				
			||||||
		CPrintln(C.GoString(v))
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Config) resolveSeccomp() (*os.File, error) {
 | 
				
			||||||
 | 
						if c.Syscall == nil {
 | 
				
			||||||
 | 
							return nil, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// resolve seccomp filter opts
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							opts    seccomp.SyscallOpts
 | 
				
			||||||
 | 
							optd    []string
 | 
				
			||||||
 | 
							optCond = [...]struct {
 | 
				
			||||||
 | 
								v bool
 | 
				
			||||||
 | 
								o seccomp.SyscallOpts
 | 
				
			||||||
 | 
								d string
 | 
				
			||||||
 | 
							}{
 | 
				
			||||||
 | 
								{!c.Syscall.Compat, seccomp.FlagExt, "fortify"},
 | 
				
			||||||
 | 
								{!c.UserNS, seccomp.FlagDenyNS, "denyns"},
 | 
				
			||||||
 | 
								{c.NewSession, seccomp.FlagDenyTTY, "denytty"},
 | 
				
			||||||
 | 
								{c.Syscall.DenyDevel, seccomp.FlagDenyDevel, "denydevel"},
 | 
				
			||||||
 | 
								{c.Syscall.Multiarch, seccomp.FlagMultiarch, "multiarch"},
 | 
				
			||||||
 | 
								{c.Syscall.Linux32, seccomp.FlagLinux32, "linux32"},
 | 
				
			||||||
 | 
								{c.Syscall.Can, seccomp.FlagCan, "can"},
 | 
				
			||||||
 | 
								{c.Syscall.Bluetooth, seccomp.FlagBluetooth, "bluetooth"},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						if seccomp.CPrintln != nil {
 | 
				
			||||||
 | 
							optd = make([]string, 1, len(optCond)+1)
 | 
				
			||||||
 | 
							optd[0] = "common"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, opt := range optCond {
 | 
				
			||||||
 | 
							if opt.v {
 | 
				
			||||||
 | 
								opts |= opt.o
 | 
				
			||||||
 | 
								if fmsg.Verbose() {
 | 
				
			||||||
 | 
									optd = append(optd, opt.d)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if seccomp.CPrintln != nil {
 | 
				
			||||||
 | 
							seccomp.CPrintln(fmt.Sprintf("seccomp flags: %s", optd))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return seccomp.Export(opts)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										17
									
								
								helper/seccomp/export.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								helper/seccomp/export.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					package seccomp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Export(opts SyscallOpts) (f *os.File, err error) {
 | 
				
			||||||
 | 
						if f, err = tmpfile(); err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err = exportFilter(f.Fd(), opts); err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = f.Seek(0, io.SeekStart)
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -28,7 +28,7 @@ struct f_syscall_act {
 | 
				
			|||||||
#define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
 | 
					#define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SECCOMP_RULESET_ADD(ruleset) do {                                                                      \
 | 
					#define SECCOMP_RULESET_ADD(ruleset) do {                                                                      \
 | 
				
			||||||
  F_println("adding seccomp ruleset \"" #ruleset "\""); \
 | 
					  if (opts & F_VERBOSE) F_println("adding seccomp ruleset \"" #ruleset "\"");                                  \
 | 
				
			||||||
  for (int i = 0; i < LEN(ruleset); i++) {                                                                     \
 | 
					  for (int i = 0; i < LEN(ruleset); i++) {                                                                     \
 | 
				
			||||||
    assert(ruleset[i].m_errno == EPERM || ruleset[i].m_errno == ENOSYS);                                       \
 | 
					    assert(ruleset[i].m_errno == EPERM || ruleset[i].m_errno == ENOSYS);                                       \
 | 
				
			||||||
                                                                                                               \
 | 
					                                                                                                               \
 | 
				
			||||||
@ -89,6 +89,31 @@ int32_t f_export_bpf(int fd, uint32_t arch, uint32_t multiarch, f_syscall_opts o
 | 
				
			|||||||
    {SCMP_SYS(migrate_pages), EPERM},
 | 
					    {SCMP_SYS(migrate_pages), EPERM},
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // fortify: project-specific extensions
 | 
				
			||||||
 | 
					  struct f_syscall_act deny_common_ext[] = {
 | 
				
			||||||
 | 
					    // system calls for changing the system clock
 | 
				
			||||||
 | 
					    {SCMP_SYS(adjtimex), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(clock_adjtime), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(clock_adjtime64), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(clock_settime), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(clock_settime64), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(settimeofday), EPERM},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // loading and unloading of kernel modules
 | 
				
			||||||
 | 
					    {SCMP_SYS(delete_module), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(finit_module), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(init_module), EPERM},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // system calls for rebooting and reboot preparation
 | 
				
			||||||
 | 
					    {SCMP_SYS(kexec_file_load), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(kexec_load), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(reboot), EPERM},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // system calls for enabling/disabling swap devices
 | 
				
			||||||
 | 
					    {SCMP_SYS(swapoff), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(swapon), EPERM},
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  struct f_syscall_act deny_ns[] = {
 | 
					  struct f_syscall_act deny_ns[] = {
 | 
				
			||||||
    // Don't allow subnamespace setups:
 | 
					    // Don't allow subnamespace setups:
 | 
				
			||||||
    {SCMP_SYS(unshare), EPERM},
 | 
					    {SCMP_SYS(unshare), EPERM},
 | 
				
			||||||
@ -126,6 +151,34 @@ int32_t f_export_bpf(int fd, uint32_t arch, uint32_t multiarch, f_syscall_opts o
 | 
				
			|||||||
    {SCMP_SYS(mount_setattr), ENOSYS},
 | 
					    {SCMP_SYS(mount_setattr), ENOSYS},
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // fortify: project-specific extensions
 | 
				
			||||||
 | 
					  struct f_syscall_act deny_ns_ext[] = {
 | 
				
			||||||
 | 
					    // changing file ownership
 | 
				
			||||||
 | 
					    {SCMP_SYS(chown), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(chown32), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(fchown), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(fchown32), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(fchownat), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(lchown), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(lchown32), EPERM},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // system calls for changing user ID and group ID credentials
 | 
				
			||||||
 | 
					    {SCMP_SYS(setgid), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setgid32), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setgroups), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setgroups32), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setregid), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setregid32), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setresgid), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setresgid32), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setresuid), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setresuid32), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setreuid), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setreuid32), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setuid), EPERM},
 | 
				
			||||||
 | 
					    {SCMP_SYS(setuid32), EPERM},
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  struct f_syscall_act deny_tty[] = {
 | 
					  struct f_syscall_act deny_tty[] = {
 | 
				
			||||||
    // Don't allow faking input to the controlling tty (CVE-2017-5226)
 | 
					    // Don't allow faking input to the controlling tty (CVE-2017-5226)
 | 
				
			||||||
    {SCMP_SYS(ioctl), EPERM, &SCMP_A1(SCMP_CMP_MASKED_EQ, 0xFFFFFFFFu, (int)TIOCSTI)},
 | 
					    {SCMP_SYS(ioctl), EPERM, &SCMP_A1(SCMP_CMP_MASKED_EQ, 0xFFFFFFFFu, (int)TIOCSTI)},
 | 
				
			||||||
@ -145,6 +198,22 @@ int32_t f_export_bpf(int fd, uint32_t arch, uint32_t multiarch, f_syscall_opts o
 | 
				
			|||||||
    {SCMP_SYS(ptrace), EPERM}
 | 
					    {SCMP_SYS(ptrace), EPERM}
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  struct f_syscall_act deny_emu[] = {
 | 
				
			||||||
 | 
					    // modify_ldt is a historic source of interesting information leaks,
 | 
				
			||||||
 | 
					    // so it's disabled as a hardening measure.
 | 
				
			||||||
 | 
					    // However, it is required to run old 16-bit applications
 | 
				
			||||||
 | 
					    // as well as some Wine patches, so it's allowed in multiarch.
 | 
				
			||||||
 | 
					    {SCMP_SYS(modify_ldt), EPERM},
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // fortify: project-specific extensions
 | 
				
			||||||
 | 
					  struct f_syscall_act deny_emu_ext[] = {
 | 
				
			||||||
 | 
					    {SCMP_SYS(subpage_prot), ENOSYS},
 | 
				
			||||||
 | 
					    {SCMP_SYS(switch_endian), ENOSYS},
 | 
				
			||||||
 | 
					    {SCMP_SYS(vm86), ENOSYS},
 | 
				
			||||||
 | 
					    {SCMP_SYS(vm86old), ENOSYS},
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Blocklist all but unix, inet, inet6 and netlink
 | 
					  // Blocklist all but unix, inet, inet6 and netlink
 | 
				
			||||||
  struct
 | 
					  struct
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
@ -199,26 +268,11 @@ int32_t f_export_bpf(int fd, uint32_t arch, uint32_t multiarch, f_syscall_opts o
 | 
				
			|||||||
  if (opts & F_DENY_NS) SECCOMP_RULESET_ADD(deny_ns);
 | 
					  if (opts & F_DENY_NS) SECCOMP_RULESET_ADD(deny_ns);
 | 
				
			||||||
  if (opts & F_DENY_TTY) SECCOMP_RULESET_ADD(deny_tty);
 | 
					  if (opts & F_DENY_TTY) SECCOMP_RULESET_ADD(deny_tty);
 | 
				
			||||||
  if (opts & F_DENY_DEVEL) SECCOMP_RULESET_ADD(deny_devel);
 | 
					  if (opts & F_DENY_DEVEL) SECCOMP_RULESET_ADD(deny_devel);
 | 
				
			||||||
 | 
					  if (!allow_multiarch) SECCOMP_RULESET_ADD(deny_emu);
 | 
				
			||||||
  if (!allow_multiarch) {
 | 
					  if (opts & F_EXT) {
 | 
				
			||||||
    F_println("disabling modify_ldt");
 | 
					    SECCOMP_RULESET_ADD(deny_common_ext);
 | 
				
			||||||
 | 
					    if (opts & F_DENY_NS) SECCOMP_RULESET_ADD(deny_ns_ext);
 | 
				
			||||||
    // modify_ldt is a historic source of interesting information leaks,
 | 
					    if (!allow_multiarch) SECCOMP_RULESET_ADD(deny_emu_ext);
 | 
				
			||||||
    // so it's disabled as a hardening measure.
 | 
					 | 
				
			||||||
    // However, it is required to run old 16-bit applications
 | 
					 | 
				
			||||||
    // as well as some Wine patches, so it's allowed in multiarch.
 | 
					 | 
				
			||||||
    ret = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(modify_ldt), 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // See above for the meaning of EFAULT.
 | 
					 | 
				
			||||||
    if (ret == -EFAULT) {
 | 
					 | 
				
			||||||
      // call fmsg here?
 | 
					 | 
				
			||||||
      res = 4;
 | 
					 | 
				
			||||||
      goto out;
 | 
					 | 
				
			||||||
    } else if (ret < 0) {
 | 
					 | 
				
			||||||
      res = 5;
 | 
					 | 
				
			||||||
      errno = -ret;
 | 
					 | 
				
			||||||
      goto out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Socket filtering doesn't work on e.g. i386, so ignore failures here
 | 
					  // Socket filtering doesn't work on e.g. i386, so ignore failures here
 | 
				
			||||||
@ -8,13 +8,15 @@
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef enum {
 | 
					typedef enum {
 | 
				
			||||||
  F_DENY_NS    = 1 << 0,
 | 
					  F_VERBOSE    = 1 << 0,
 | 
				
			||||||
  F_DENY_TTY   = 1 << 1,
 | 
					  F_EXT        = 1 << 1,
 | 
				
			||||||
  F_DENY_DEVEL = 1 << 2,
 | 
					  F_DENY_NS    = 1 << 2,
 | 
				
			||||||
  F_MULTIARCH  = 1 << 3,
 | 
					  F_DENY_TTY   = 1 << 3,
 | 
				
			||||||
  F_LINUX32    = 1 << 4,
 | 
					  F_DENY_DEVEL = 1 << 4,
 | 
				
			||||||
  F_CAN        = 1 << 5,
 | 
					  F_MULTIARCH  = 1 << 5,
 | 
				
			||||||
  F_BLUETOOTH  = 1 << 6,
 | 
					  F_LINUX32    = 1 << 6,
 | 
				
			||||||
 | 
					  F_CAN        = 1 << 7,
 | 
				
			||||||
 | 
					  F_BLUETOOTH  = 1 << 8,
 | 
				
			||||||
} f_syscall_opts;
 | 
					} f_syscall_opts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern void F_println(char *v);
 | 
					extern void F_println(char *v);
 | 
				
			||||||
							
								
								
									
										89
									
								
								helper/seccomp/seccomp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								helper/seccomp/seccomp.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					package seccomp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					#cgo linux pkg-config: --static libseccomp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "seccomp-export.h"
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					import "C"
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var CPrintln func(v ...any)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var resErr = [...]error{
 | 
				
			||||||
 | 
						0: nil,
 | 
				
			||||||
 | 
						1: errors.New("seccomp_init failed"),
 | 
				
			||||||
 | 
						2: errors.New("seccomp_arch_add failed"),
 | 
				
			||||||
 | 
						3: errors.New("seccomp_arch_add failed (multiarch)"),
 | 
				
			||||||
 | 
						4: errors.New("internal libseccomp failure"),
 | 
				
			||||||
 | 
						5: errors.New("seccomp_rule_add failed"),
 | 
				
			||||||
 | 
						6: errors.New("seccomp_export_bpf failed"),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SyscallOpts = C.f_syscall_opts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						flagVerbose   SyscallOpts = C.F_VERBOSE
 | 
				
			||||||
 | 
						FlagExt       SyscallOpts = C.F_EXT
 | 
				
			||||||
 | 
						FlagDenyNS    SyscallOpts = C.F_DENY_NS
 | 
				
			||||||
 | 
						FlagDenyTTY   SyscallOpts = C.F_DENY_TTY
 | 
				
			||||||
 | 
						FlagDenyDevel SyscallOpts = C.F_DENY_DEVEL
 | 
				
			||||||
 | 
						FlagMultiarch SyscallOpts = C.F_MULTIARCH
 | 
				
			||||||
 | 
						FlagLinux32   SyscallOpts = C.F_LINUX32
 | 
				
			||||||
 | 
						FlagCan       SyscallOpts = C.F_CAN
 | 
				
			||||||
 | 
						FlagBluetooth SyscallOpts = C.F_BLUETOOTH
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func tmpfile() (*os.File, error) {
 | 
				
			||||||
 | 
						fd, err := C.f_tmpfile_fd()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return os.NewFile(uintptr(fd), "tmpfile"), err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func exportFilter(fd uintptr, opts SyscallOpts) error {
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							arch      C.uint32_t = 0
 | 
				
			||||||
 | 
							multiarch C.uint32_t = 0
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						switch runtime.GOARCH {
 | 
				
			||||||
 | 
						case "386":
 | 
				
			||||||
 | 
							arch = C.SCMP_ARCH_X86
 | 
				
			||||||
 | 
						case "amd64":
 | 
				
			||||||
 | 
							arch = C.SCMP_ARCH_X86_64
 | 
				
			||||||
 | 
							multiarch = C.SCMP_ARCH_X86
 | 
				
			||||||
 | 
						case "arm":
 | 
				
			||||||
 | 
							arch = C.SCMP_ARCH_ARM
 | 
				
			||||||
 | 
						case "arm64":
 | 
				
			||||||
 | 
							arch = C.SCMP_ARCH_AARCH64
 | 
				
			||||||
 | 
							multiarch = C.SCMP_ARCH_ARM
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// this removes repeated transitions between C and Go execution
 | 
				
			||||||
 | 
						// when producing log output via F_println and CPrintln is nil
 | 
				
			||||||
 | 
						if CPrintln != nil {
 | 
				
			||||||
 | 
							opts |= flagVerbose
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err := C.f_export_bpf(C.int(fd), arch, multiarch, opts)
 | 
				
			||||||
 | 
						if re := resErr[res]; re != nil {
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								return re
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return fmt.Errorf("%s: %v", re.Error(), err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//export F_println
 | 
				
			||||||
 | 
					func F_println(v *C.char) {
 | 
				
			||||||
 | 
						if CPrintln != nil {
 | 
				
			||||||
 | 
							CPrintln(C.GoString(v))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -8,7 +8,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/fst"
 | 
						"git.gensokyo.uk/security/fortify/fst"
 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/helper"
 | 
						"git.gensokyo.uk/security/fortify/helper"
 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/helper/bwrap"
 | 
						"git.gensokyo.uk/security/fortify/helper/seccomp"
 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/internal"
 | 
						"git.gensokyo.uk/security/fortify/internal"
 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/internal/fmsg"
 | 
						"git.gensokyo.uk/security/fortify/internal/fmsg"
 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/internal/proc"
 | 
						"git.gensokyo.uk/security/fortify/internal/proc"
 | 
				
			||||||
@ -128,7 +128,7 @@ func Main() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	helper.BubblewrapName = payload.Exec[0] // resolved bwrap path by parent
 | 
						helper.BubblewrapName = payload.Exec[0] // resolved bwrap path by parent
 | 
				
			||||||
	if fmsg.Verbose() {
 | 
						if fmsg.Verbose() {
 | 
				
			||||||
		bwrap.CPrintln = fmsg.Println
 | 
							seccomp.CPrintln = fmsg.Println
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if b, err := helper.NewBwrap(
 | 
						if b, err := helper.NewBwrap(
 | 
				
			||||||
		conf, innerInit,
 | 
							conf, innerInit,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								main.go
									
									
									
									
									
								
							@ -16,7 +16,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/dbus"
 | 
						"git.gensokyo.uk/security/fortify/dbus"
 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/fst"
 | 
						"git.gensokyo.uk/security/fortify/fst"
 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/helper/bwrap"
 | 
						"git.gensokyo.uk/security/fortify/helper/seccomp"
 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/internal"
 | 
						"git.gensokyo.uk/security/fortify/internal"
 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/internal/app"
 | 
						"git.gensokyo.uk/security/fortify/internal/app"
 | 
				
			||||||
	"git.gensokyo.uk/security/fortify/internal/fmsg"
 | 
						"git.gensokyo.uk/security/fortify/internal/fmsg"
 | 
				
			||||||
@ -310,7 +310,7 @@ func runApp(config *fst.Config) {
 | 
				
			|||||||
	ctx, cancel := context.WithCancel(context.Background())
 | 
						ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if fmsg.Verbose() {
 | 
						if fmsg.Verbose() {
 | 
				
			||||||
		bwrap.CPrintln = fmsg.Println
 | 
							seccomp.CPrintln = fmsg.Println
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// handle signals for graceful shutdown
 | 
						// handle signals for graceful shutdown
 | 
				
			||||||
 | 
				
			|||||||
@ -118,7 +118,8 @@ in
 | 
				
			|||||||
                          env
 | 
					                          env
 | 
				
			||||||
                          ;
 | 
					                          ;
 | 
				
			||||||
                        syscall = {
 | 
					                        syscall = {
 | 
				
			||||||
                          inherit (app) devel multiarch bluetooth;
 | 
					                          inherit (app) compat multiarch bluetooth;
 | 
				
			||||||
 | 
					                          deny_devel = !app.devel;
 | 
				
			||||||
                        };
 | 
					                        };
 | 
				
			||||||
                        map_real_uid = app.mapRealUid;
 | 
					                        map_real_uid = app.mapRealUid;
 | 
				
			||||||
                        no_new_session = app.tty;
 | 
					                        no_new_session = app.tty;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										26
									
								
								options.md
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								options.md
									
									
									
									
									
								
							@ -36,7 +36,7 @@ package
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*Default:*
 | 
					*Default:*
 | 
				
			||||||
` <derivation fortify-0.2.11> `
 | 
					` <derivation fortify-0.2.12> `
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -198,6 +198,30 @@ null or string
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## environment\.fortify\.apps\.\*\.compat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Whether to enable disable syscall filter extensions\.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*Type:*
 | 
				
			||||||
 | 
					boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*Default:*
 | 
				
			||||||
 | 
					` false `
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*Example:*
 | 
				
			||||||
 | 
					` true `
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## environment\.fortify\.apps\.\*\.dbus\.session
 | 
					## environment\.fortify\.apps\.\*\.dbus\.session
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -151,6 +151,7 @@ in
 | 
				
			|||||||
                default = true;
 | 
					                default = true;
 | 
				
			||||||
              };
 | 
					              };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              compat = mkEnableOption "disable syscall filter extensions";
 | 
				
			||||||
              devel = mkEnableOption "development kernel APIs";
 | 
					              devel = mkEnableOption "development kernel APIs";
 | 
				
			||||||
              multiarch = mkEnableOption "multiarch kernel support";
 | 
					              multiarch = mkEnableOption "multiarch kernel support";
 | 
				
			||||||
              bluetooth = mkEnableOption "AF_BLUETOOTH socket operations";
 | 
					              bluetooth = mkEnableOption "AF_BLUETOOTH socket operations";
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
buildGoModule rec {
 | 
					buildGoModule rec {
 | 
				
			||||||
  pname = "fortify";
 | 
					  pname = "fortify";
 | 
				
			||||||
  version = "0.2.11";
 | 
					  version = "0.2.12";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  src = builtins.path {
 | 
					  src = builtins.path {
 | 
				
			||||||
    name = "fortify-src";
 | 
					    name = "fortify-src";
 | 
				
			||||||
@ -63,7 +63,7 @@ buildGoModule rec {
 | 
				
			|||||||
    makeBinaryWrapper
 | 
					    makeBinaryWrapper
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  preConfigure = ''
 | 
					  preBuild = ''
 | 
				
			||||||
    HOME=$(mktemp -d) go generate ./...
 | 
					    HOME=$(mktemp -d) go generate ./...
 | 
				
			||||||
  '';
 | 
					  '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										9
									
								
								test.nix
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								test.nix
									
									
									
									
									
								
							@ -237,8 +237,8 @@ nixosTest {
 | 
				
			|||||||
    machine.succeed("rm -rf /tmp/src && cp -a '${self.packages.${system}.fortify.src}' /tmp/src")
 | 
					    machine.succeed("rm -rf /tmp/src && cp -a '${self.packages.${system}.fortify.src}' /tmp/src")
 | 
				
			||||||
    machine.succeed("fortify-fhs -c '(cd /tmp/src && go generate ./... && go test ./... && touch /tmp/success-gotest)' &> /tmp/gotest &")
 | 
					    machine.succeed("fortify-fhs -c '(cd /tmp/src && go generate ./... && go test ./... && touch /tmp/success-gotest)' &> /tmp/gotest &")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # To check sway's version:
 | 
					    # To check fortify's version:
 | 
				
			||||||
    print(machine.succeed("sway --version"))
 | 
					    print(machine.succeed("sudo -u alice -i fortify version"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Wait for Sway to complete startup:
 | 
					    # Wait for Sway to complete startup:
 | 
				
			||||||
    machine.wait_for_file("/run/user/1000/wayland-1")
 | 
					    machine.wait_for_file("/run/user/1000/wayland-1")
 | 
				
			||||||
@ -254,6 +254,11 @@ nixosTest {
 | 
				
			|||||||
    print(machine.succeed("sudo -u alice -i fortify -v run -a 0 touch /tmp/success-bare"))
 | 
					    print(machine.succeed("sudo -u alice -i fortify -v run -a 0 touch /tmp/success-bare"))
 | 
				
			||||||
    machine.wait_for_file("/tmp/fortify.1000/tmpdir/0/success-bare")
 | 
					    machine.wait_for_file("/tmp/fortify.1000/tmpdir/0/success-bare")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Verify silent output permissive defaults:
 | 
				
			||||||
 | 
					    output = machine.succeed("sudo -u alice -i fortify run -a 0 true &>/dev/stdout")
 | 
				
			||||||
 | 
					    if output != "":
 | 
				
			||||||
 | 
					        raise Exception(f"unexpected output\n{output}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Start fortify permissive defaults within Wayland session:
 | 
					    # Start fortify permissive defaults within Wayland session:
 | 
				
			||||||
    fortify('-v run --wayland --dbus notify-send -a "NixOS Tests" "Test notification" "Notification from within sandbox." && touch /tmp/dbus-done')
 | 
					    fortify('-v run --wayland --dbus notify-send -a "NixOS Tests" "Test notification" "Notification from within sandbox." && touch /tmp/dbus-done')
 | 
				
			||||||
    machine.wait_for_file("/tmp/dbus-done")
 | 
					    machine.wait_for_file("/tmp/dbus-done")
 | 
				
			||||||
 | 
				
			|||||||
@ -1,17 +1,7 @@
 | 
				
			|||||||
package wl
 | 
					#include "wayland-bind.h"
 | 
				
			||||||
 | 
					 | 
				
			||||||
//go:generate sh -c "wayland-scanner client-header `pkg-config --variable=datarootdir wayland-protocols`/wayland-protocols/staging/security-context/security-context-v1.xml security-context-v1-protocol.h"
 | 
					 | 
				
			||||||
//go:generate sh -c "wayland-scanner private-code `pkg-config --variable=datarootdir wayland-protocols`/wayland-protocols/staging/security-context/security-context-v1.xml security-context-v1-protocol.c"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
#cgo linux pkg-config: --static wayland-client
 | 
					 | 
				
			||||||
#cgo freebsd openbsd LDFLAGS: -lwayland-client
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdint.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
#include <sys/socket.h>
 | 
					#include <sys/socket.h>
 | 
				
			||||||
#include <sys/un.h>
 | 
					#include <sys/un.h>
 | 
				
			||||||
@ -33,7 +23,7 @@ static const struct wl_registry_listener registry_listener = {
 | 
				
			|||||||
  .global_remove = registry_handle_global_remove,
 | 
					  .global_remove = registry_handle_global_remove,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int32_t bind_wayland_fd(char *socket_path, int fd, const char *app_id, const char *instance_id, int sync_fd) {
 | 
					int32_t bind_wayland_fd(char *socket_path, int fd, const char *app_id, const char *instance_id, int sync_fd) {
 | 
				
			||||||
  int32_t res = 0; // refer to resErr for meaning
 | 
					  int32_t res = 0; // refer to resErr for meaning
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  struct wl_display *display;
 | 
					  struct wl_display *display;
 | 
				
			||||||
@ -96,17 +86,3 @@ out:
 | 
				
			|||||||
  free((void *)instance_id);
 | 
					  free((void *)instance_id);
 | 
				
			||||||
  return res;
 | 
					  return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
import "C"
 | 
					 | 
				
			||||||
import "errors"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var resErr = [...]error{
 | 
					 | 
				
			||||||
	0: nil,
 | 
					 | 
				
			||||||
	1: errors.New("wl_display_connect_to_fd() failed"),
 | 
					 | 
				
			||||||
	2: errors.New("wp_security_context_v1 not available"),
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func bindWaylandFd(socketPath string, fd uintptr, appID, instanceID string, syncFD uintptr) error {
 | 
					 | 
				
			||||||
	res := C.bind_wayland_fd(C.CString(socketPath), C.int(fd), C.CString(appID), C.CString(instanceID), C.int(syncFD))
 | 
					 | 
				
			||||||
	return resErr[int32(res)]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										3
									
								
								wl/wayland-bind.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								wl/wayland-bind.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int32_t bind_wayland_fd(char *socket_path, int fd, const char *app_id, const char *instance_id, int sync_fd);
 | 
				
			||||||
							
								
								
									
										24
									
								
								wl/wl.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								wl/wl.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					package wl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:generate sh -c "wayland-scanner client-header `pkg-config --variable=datarootdir wayland-protocols`/wayland-protocols/staging/security-context/security-context-v1.xml security-context-v1-protocol.h"
 | 
				
			||||||
 | 
					//go:generate sh -c "wayland-scanner private-code `pkg-config --variable=datarootdir wayland-protocols`/wayland-protocols/staging/security-context/security-context-v1.xml security-context-v1-protocol.c"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					#cgo linux pkg-config: --static wayland-client
 | 
				
			||||||
 | 
					#cgo freebsd openbsd LDFLAGS: -lwayland-client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "wayland-bind.h"
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					import "C"
 | 
				
			||||||
 | 
					import "errors"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var resErr = [...]error{
 | 
				
			||||||
 | 
						0: nil,
 | 
				
			||||||
 | 
						1: errors.New("wl_display_connect_to_fd() failed"),
 | 
				
			||||||
 | 
						2: errors.New("wp_security_context_v1 not available"),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func bindWaylandFd(socketPath string, fd uintptr, appID, instanceID string, syncFD uintptr) error {
 | 
				
			||||||
 | 
						res := C.bind_wayland_fd(C.CString(socketPath), C.int(fd), C.CString(appID), C.CString(instanceID), C.int(syncFD))
 | 
				
			||||||
 | 
						return resErr[int32(res)]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user