unix: add support for OpenBSD pledge

Pledge, the privilege-restricting syscall and mitigation mechanism,
was missing from syscall_openbsd.go. As of the latest release, it
is officially supported in "stable".

More information about the call itself, and hence its importance,
can be found at:

http://www.openbsd.org/papers/hackfest2015-pledge/mgp00001.html

Change-Id: I2fdac1968664668e7bea1175677efe6433e0125e
Reviewed-on: https://go-review.googlesource.com/21815
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/unix/openbsd_pledge.go b/unix/openbsd_pledge.go
new file mode 100644
index 0000000..db4f72e
--- /dev/null
+++ b/unix/openbsd_pledge.go
@@ -0,0 +1,38 @@
+// Copyright 2016 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.
+
+// +build openbsd
+// +build 386 amd64 arm
+
+package unix
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+const (
+	SYS_PLEDGE = 108
+)
+
+// Pledge implements the pledge syscall. For more information see pledge(2).
+func Pledge(promises string, paths []string) error {
+	promisesPtr, err := syscall.BytePtrFromString(promises)
+	if err != nil {
+		return err
+	}
+	promisesUnsafe, pathsUnsafe := unsafe.Pointer(promisesPtr), unsafe.Pointer(nil)
+	if paths != nil {
+		var pathsPtr []*byte
+		if pathsPtr, err = syscall.SlicePtrFromStrings(paths); err != nil {
+			return err
+		}
+		pathsUnsafe = unsafe.Pointer(&pathsPtr[0])
+	}
+	_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(promisesUnsafe), uintptr(pathsUnsafe), 0)
+	if e != 0 {
+		return e
+	}
+	return nil
+}
diff --git a/unix/openbsd_test.go b/unix/openbsd_test.go
new file mode 100644
index 0000000..734d765
--- /dev/null
+++ b/unix/openbsd_test.go
@@ -0,0 +1,113 @@
+// Copyright 2016 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.
+
+// +build openbsd
+
+// This, on the face of it, bizarre testing mechanism is necessary because
+// the only reliable way to gauge whether or not a pledge(2) call has succeeded
+// is that the program has been killed as a result of breaking its pledge.
+
+package unix_test
+
+import (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"testing"
+
+	"golang.org/x/sys/unix"
+)
+
+type testProc struct {
+	fn      func()       // should always exit instead of returning
+	cleanup func() error // for instance, delete coredumps from testing pledge
+	success bool         // whether zero-exit means success or failure
+}
+
+var (
+	testProcs = map[string]testProc{}
+	procName  = ""
+)
+
+const (
+	optName = "sys-unix-internal-procname"
+)
+
+func init() {
+	flag.StringVar(&procName, optName, "", "internal use only")
+}
+
+// testCmd generates a proper command that, when executed, runs the test
+// corresponding to the given key.
+func testCmd(procName string) (*exec.Cmd, error) {
+	exe, err := filepath.Abs(os.Args[0])
+	if err != nil {
+		return nil, err
+	}
+	cmd := exec.Command(exe, "-"+optName+"="+procName)
+	cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
+	return cmd, nil
+}
+
+// ExitsCorrectly is a comprehensive, one-line-of-use wrapper for testing
+// a testProc with a key.
+func ExitsCorrectly(procName string, t *testing.T) {
+	s := testProcs[procName]
+	c, err := testCmd(procName)
+	defer func() {
+		if s.cleanup() != nil {
+			t.Fatalf("Failed to run cleanup for %s", procName)
+		}
+	}()
+	if err != nil {
+		t.Fatalf("Failed to construct command for %s", procName)
+	}
+	if (c.Run() == nil) != s.success {
+		result := "succeed"
+		if !s.success {
+			result = "fail"
+		}
+		t.Fatalf("Process did not %s when it was supposed to", result)
+	}
+}
+
+func TestMain(m *testing.M) {
+	flag.Parse()
+	if procName != "" {
+		testProcs[procName].fn()
+	}
+	os.Exit(m.Run())
+}
+
+// For example, add a test for pledge.
+func init() {
+	testProcs["pledge"] = testProc{
+		func() {
+			fmt.Println(unix.Pledge("", nil))
+			os.Exit(0)
+		},
+		func() error {
+			files, err := ioutil.ReadDir(".")
+			if err != nil {
+				return err
+			}
+			for _, file := range files {
+				if filepath.Ext(file.Name()) == ".core" {
+					if err := os.Remove(file.Name()); err != nil {
+						return err
+					}
+				}
+			}
+			return nil
+		},
+		false,
+	}
+}
+
+func TestPledge(t *testing.T) {
+	ExitsCorrectly("pledge", t)
+}