-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
syscall: add jail support to ForkExec on FreeBSD
Introduce a new SysProcAttr member called Jail on FreeBSD. This allows supplying an existing jail's ID to which the child process is attached before calling the exec system call. Fixes golang#46259 Change-Id: Ie282e5b83429131f9a9e1e27cfcb3bcc995d1d4d
- Loading branch information
Showing
2 changed files
with
126 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// Copyright 2022 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
//go:build freebsd | ||
|
||
package syscall_test | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"internal/testenv" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"syscall" | ||
"testing" | ||
"unsafe" | ||
) | ||
|
||
const ( | ||
flagJailCreate = uintptr(0x1) | ||
) | ||
|
||
func prepareJail(t *testing.T) (int, string, func()) { | ||
t.Helper() | ||
|
||
root, err := os.MkdirTemp("", "jail_attach") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
paramPath := []byte("path\x00") | ||
conf := make([]syscall.Iovec, 4) | ||
conf[0].Base = ¶mPath[0] | ||
conf[0].SetLen(len(paramPath)) | ||
p, err := syscall.BytePtrFromString(root) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
conf[1].Base = p | ||
conf[1].SetLen(len(root) + 1) | ||
|
||
paramPersist := []byte("persist\x00") | ||
conf[2].Base = ¶mPersist[0] | ||
conf[2].SetLen(8) | ||
conf[3].Base = nil | ||
conf[3].SetLen(0) | ||
|
||
id, _, err1 := syscall.Syscall(syscall.SYS_JAIL_SET, | ||
uintptr(unsafe.Pointer(&conf[0])), uintptr(len(conf)), flagJailCreate) | ||
if err1 != 0 { | ||
t.Fatalf("jail_set: %v", err1) | ||
} | ||
|
||
return int(id), root, func() { | ||
_, _, err1 := syscall.Syscall(syscall.SYS_JAIL_REMOVE, id, 0, 0) | ||
if err1 != 0 { | ||
t.Errorf("failed to cleanup jail: %v", err) | ||
} | ||
err := os.RemoveAll(root) | ||
if err != nil { | ||
t.Errorf("failed to cleanup temporary fail root: %v", err) | ||
} | ||
} | ||
} | ||
|
||
func TestJailAttach(t *testing.T) { | ||
// Make sure we are running as root, so we have permissions to create | ||
// and remove jails. | ||
if os.Getuid() != 0 { | ||
t.Skip("kernel prohibits jail system calls in unprivileged process") | ||
} | ||
|
||
jid, root, cleanup := prepareJail(t) | ||
defer cleanup() | ||
|
||
// Since jail attach does an implicit chroot to the jail's path, | ||
// we need the binary there, and it must be statically linked. | ||
x := filepath.Join(root, "syscall.test") | ||
cmd := exec.Command(testenv.GoToolPath(t), "test", "-c", "-o", x, "syscall") | ||
cmd.Env = append(os.Environ(), "CGO_ENABLED=0") | ||
if o, err := cmd.CombinedOutput(); err != nil { | ||
t.Fatalf("Build of syscall in jail root failed, output %v, err %v", o, err) | ||
} | ||
|
||
cmd = exec.Command("/syscall.test", "-test.run=TestJailAttachHelper", "/") | ||
cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") | ||
cmd.SysProcAttr = &syscall.SysProcAttr{Jail: jid} | ||
out, err := cmd.CombinedOutput() | ||
if err != nil { | ||
t.Fatalf("Cmd failed with err %v, output: %s", err, out) | ||
} | ||
if !bytes.HasSuffix(bytes.TrimSpace(out), []byte("1")) { | ||
t.Fatalf("got: %q, want: a line that ends with 1", out) | ||
} | ||
} | ||
|
||
// TestJailAttachHelper isn't a real test. It's used as a helper process for | ||
// TestJailAttach | ||
func TestJailAttachHelper(t *testing.T) { | ||
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { | ||
return | ||
} | ||
defer os.Exit(0) | ||
|
||
jailed, err := syscall.SysctlUint32("security.jail.jailed") | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err) | ||
os.Exit(2) | ||
} | ||
|
||
fmt.Println(jailed) | ||
// TODO: should we check, whether we are in the correct jail? | ||
// This would have to be done by the parent process. | ||
} |