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}