1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build openbsd
6// +build 386 amd64 arm
7
8package unix
9
10import (
11	"errors"
12	"fmt"
13	"strconv"
14	"syscall"
15	"unsafe"
16)
17
18// Pledge implements the pledge syscall.
19//
20// The pledge syscall does not accept execpromises on OpenBSD releases
21// before 6.3.
22//
23// execpromises must be empty when Pledge is called on OpenBSD
24// releases predating 6.3, otherwise an error will be returned.
25//
26// For more information see pledge(2).
27func Pledge(promises, execpromises string) error {
28	maj, min, err := majmin()
29	if err != nil {
30		return err
31	}
32
33	err = pledgeAvailable(maj, min, execpromises)
34	if err != nil {
35		return err
36	}
37
38	pptr, err := syscall.BytePtrFromString(promises)
39	if err != nil {
40		return err
41	}
42
43	// This variable will hold either a nil unsafe.Pointer or
44	// an unsafe.Pointer to a string (execpromises).
45	var expr unsafe.Pointer
46
47	// If we're running on OpenBSD > 6.2, pass execpromises to the syscall.
48	if maj > 6 || (maj == 6 && min > 2) {
49		exptr, err := syscall.BytePtrFromString(execpromises)
50		if err != nil {
51			return err
52		}
53		expr = unsafe.Pointer(exptr)
54	}
55
56	_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0)
57	if e != 0 {
58		return e
59	}
60
61	return nil
62}
63
64// PledgePromises implements the pledge syscall.
65//
66// This changes the promises and leaves the execpromises untouched.
67//
68// For more information see pledge(2).
69func PledgePromises(promises string) error {
70	maj, min, err := majmin()
71	if err != nil {
72		return err
73	}
74
75	err = pledgeAvailable(maj, min, "")
76	if err != nil {
77		return err
78	}
79
80	// This variable holds the execpromises and is always nil.
81	var expr unsafe.Pointer
82
83	pptr, err := syscall.BytePtrFromString(promises)
84	if err != nil {
85		return err
86	}
87
88	_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0)
89	if e != 0 {
90		return e
91	}
92
93	return nil
94}
95
96// PledgeExecpromises implements the pledge syscall.
97//
98// This changes the execpromises and leaves the promises untouched.
99//
100// For more information see pledge(2).
101func PledgeExecpromises(execpromises string) error {
102	maj, min, err := majmin()
103	if err != nil {
104		return err
105	}
106
107	err = pledgeAvailable(maj, min, execpromises)
108	if err != nil {
109		return err
110	}
111
112	// This variable holds the promises and is always nil.
113	var pptr unsafe.Pointer
114
115	exptr, err := syscall.BytePtrFromString(execpromises)
116	if err != nil {
117		return err
118	}
119
120	_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(pptr), uintptr(unsafe.Pointer(exptr)), 0)
121	if e != 0 {
122		return e
123	}
124
125	return nil
126}
127
128// majmin returns major and minor version number for an OpenBSD system.
129func majmin() (major int, minor int, err error) {
130	var v Utsname
131	err = Uname(&v)
132	if err != nil {
133		return
134	}
135
136	major, err = strconv.Atoi(string(v.Release[0]))
137	if err != nil {
138		err = errors.New("cannot parse major version number returned by uname")
139		return
140	}
141
142	minor, err = strconv.Atoi(string(v.Release[2]))
143	if err != nil {
144		err = errors.New("cannot parse minor version number returned by uname")
145		return
146	}
147
148	return
149}
150
151// pledgeAvailable checks for availability of the pledge(2) syscall
152// based on the running OpenBSD version.
153func pledgeAvailable(maj, min int, execpromises string) error {
154	// If OpenBSD <= 5.9, pledge is not available.
155	if (maj == 5 && min != 9) || maj < 5 {
156		return fmt.Errorf("pledge syscall is not available on OpenBSD %d.%d", maj, min)
157	}
158
159	// If OpenBSD <= 6.2 and execpromises is not empty,
160	// return an error - execpromises is not available before 6.3
161	if (maj < 6 || (maj == 6 && min <= 2)) && execpromises != "" {
162		return fmt.Errorf("cannot use execpromises on OpenBSD %d.%d", maj, min)
163	}
164
165	return nil
166}
167