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