1// Copyright 2020 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//go:build darwin
6
7// Package macOS provides cgo-less wrappers for Core Foundation and
8// Security.framework, similarly to how package syscall provides access to
9// libSystem.dylib.
10package macOS
11
12import (
13	"errors"
14	"internal/abi"
15	"reflect"
16	"runtime"
17	"time"
18	"unsafe"
19)
20
21// Core Foundation linker flags for the external linker. See Issue 42459.
22//go:cgo_ldflag "-framework"
23//go:cgo_ldflag "CoreFoundation"
24
25// CFRef is an opaque reference to a Core Foundation object. It is a pointer,
26// but to memory not owned by Go, so not an unsafe.Pointer.
27type CFRef uintptr
28
29// CFDataToSlice returns a copy of the contents of data as a bytes slice.
30func CFDataToSlice(data CFRef) []byte {
31	length := CFDataGetLength(data)
32	ptr := CFDataGetBytePtr(data)
33	src := (*[1 << 20]byte)(unsafe.Pointer(ptr))[:length:length]
34	out := make([]byte, length)
35	copy(out, src)
36	return out
37}
38
39// CFStringToString returns a Go string representation of the passed
40// in CFString.
41func CFStringToString(ref CFRef) string {
42	data := CFStringCreateExternalRepresentation(ref)
43	b := CFDataToSlice(data)
44	CFRelease(data)
45	return string(b)
46}
47
48// TimeToCFDateRef converts a time.Time into an apple CFDateRef
49func TimeToCFDateRef(t time.Time) CFRef {
50	secs := t.Sub(time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)).Seconds()
51	ref := CFDateCreate(int(secs))
52	return ref
53}
54
55type CFString CFRef
56
57const kCFAllocatorDefault = 0
58const kCFStringEncodingUTF8 = 0x08000100
59
60//go:cgo_import_dynamic x509_CFDataCreate CFDataCreate "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
61
62func BytesToCFData(b []byte) CFRef {
63	p := unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&b)).Data)
64	ret := syscall(abi.FuncPCABI0(x509_CFDataCreate_trampoline), kCFAllocatorDefault, uintptr(p), uintptr(len(b)), 0, 0, 0)
65	runtime.KeepAlive(p)
66	return CFRef(ret)
67}
68func x509_CFDataCreate_trampoline()
69
70//go:cgo_import_dynamic x509_CFStringCreateWithBytes CFStringCreateWithBytes "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
71
72// StringToCFString returns a copy of the UTF-8 contents of s as a new CFString.
73func StringToCFString(s string) CFString {
74	p := unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&s)).Data)
75	ret := syscall(abi.FuncPCABI0(x509_CFStringCreateWithBytes_trampoline), kCFAllocatorDefault, uintptr(p),
76		uintptr(len(s)), uintptr(kCFStringEncodingUTF8), 0 /* isExternalRepresentation */, 0)
77	runtime.KeepAlive(p)
78	return CFString(ret)
79}
80func x509_CFStringCreateWithBytes_trampoline()
81
82//go:cgo_import_dynamic x509_CFDictionaryGetValueIfPresent CFDictionaryGetValueIfPresent "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
83
84func CFDictionaryGetValueIfPresent(dict CFRef, key CFString) (value CFRef, ok bool) {
85	ret := syscall(abi.FuncPCABI0(x509_CFDictionaryGetValueIfPresent_trampoline), uintptr(dict), uintptr(key),
86		uintptr(unsafe.Pointer(&value)), 0, 0, 0)
87	if ret == 0 {
88		return 0, false
89	}
90	return value, true
91}
92func x509_CFDictionaryGetValueIfPresent_trampoline()
93
94const kCFNumberSInt32Type = 3
95
96//go:cgo_import_dynamic x509_CFNumberGetValue CFNumberGetValue "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
97
98func CFNumberGetValue(num CFRef) (int32, error) {
99	var value int32
100	ret := syscall(abi.FuncPCABI0(x509_CFNumberGetValue_trampoline), uintptr(num), uintptr(kCFNumberSInt32Type),
101		uintptr(unsafe.Pointer(&value)), 0, 0, 0)
102	if ret == 0 {
103		return 0, errors.New("CFNumberGetValue call failed")
104	}
105	return value, nil
106}
107func x509_CFNumberGetValue_trampoline()
108
109//go:cgo_import_dynamic x509_CFDataGetLength CFDataGetLength "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
110
111func CFDataGetLength(data CFRef) int {
112	ret := syscall(abi.FuncPCABI0(x509_CFDataGetLength_trampoline), uintptr(data), 0, 0, 0, 0, 0)
113	return int(ret)
114}
115func x509_CFDataGetLength_trampoline()
116
117//go:cgo_import_dynamic x509_CFDataGetBytePtr CFDataGetBytePtr "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
118
119func CFDataGetBytePtr(data CFRef) uintptr {
120	ret := syscall(abi.FuncPCABI0(x509_CFDataGetBytePtr_trampoline), uintptr(data), 0, 0, 0, 0, 0)
121	return ret
122}
123func x509_CFDataGetBytePtr_trampoline()
124
125//go:cgo_import_dynamic x509_CFArrayGetCount CFArrayGetCount "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
126
127func CFArrayGetCount(array CFRef) int {
128	ret := syscall(abi.FuncPCABI0(x509_CFArrayGetCount_trampoline), uintptr(array), 0, 0, 0, 0, 0)
129	return int(ret)
130}
131func x509_CFArrayGetCount_trampoline()
132
133//go:cgo_import_dynamic x509_CFArrayGetValueAtIndex CFArrayGetValueAtIndex "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
134
135func CFArrayGetValueAtIndex(array CFRef, index int) CFRef {
136	ret := syscall(abi.FuncPCABI0(x509_CFArrayGetValueAtIndex_trampoline), uintptr(array), uintptr(index), 0, 0, 0, 0)
137	return CFRef(ret)
138}
139func x509_CFArrayGetValueAtIndex_trampoline()
140
141//go:cgo_import_dynamic x509_CFEqual CFEqual "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
142
143func CFEqual(a, b CFRef) bool {
144	ret := syscall(abi.FuncPCABI0(x509_CFEqual_trampoline), uintptr(a), uintptr(b), 0, 0, 0, 0)
145	return ret == 1
146}
147func x509_CFEqual_trampoline()
148
149//go:cgo_import_dynamic x509_CFRelease CFRelease "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
150
151func CFRelease(ref CFRef) {
152	syscall(abi.FuncPCABI0(x509_CFRelease_trampoline), uintptr(ref), 0, 0, 0, 0, 0)
153}
154func x509_CFRelease_trampoline()
155
156//go:cgo_import_dynamic x509_CFArrayCreateMutable CFArrayCreateMutable "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
157
158func CFArrayCreateMutable() CFRef {
159	ret := syscall(abi.FuncPCABI0(x509_CFArrayCreateMutable_trampoline), kCFAllocatorDefault, 0, 0 /* kCFTypeArrayCallBacks */, 0, 0, 0)
160	return CFRef(ret)
161}
162func x509_CFArrayCreateMutable_trampoline()
163
164//go:cgo_import_dynamic x509_CFArrayAppendValue CFArrayAppendValue "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
165
166func CFArrayAppendValue(array CFRef, val CFRef) {
167	syscall(abi.FuncPCABI0(x509_CFArrayAppendValue_trampoline), uintptr(array), uintptr(val), 0, 0, 0, 0)
168}
169func x509_CFArrayAppendValue_trampoline()
170
171//go:cgo_import_dynamic x509_CFDateCreate CFDateCreate "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
172
173func CFDateCreate(seconds int) CFRef {
174	ret := syscall(abi.FuncPCABI0(x509_CFDateCreate_trampoline), kCFAllocatorDefault, uintptr(seconds), 0, 0, 0, 0)
175	return CFRef(ret)
176}
177func x509_CFDateCreate_trampoline()
178
179//go:cgo_import_dynamic x509_CFErrorCopyDescription CFErrorCopyDescription "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
180
181func CFErrorCopyDescription(errRef CFRef) CFRef {
182	ret := syscall(abi.FuncPCABI0(x509_CFErrorCopyDescription_trampoline), uintptr(errRef), 0, 0, 0, 0, 0)
183	return CFRef(ret)
184}
185func x509_CFErrorCopyDescription_trampoline()
186
187//go:cgo_import_dynamic x509_CFStringCreateExternalRepresentation CFStringCreateExternalRepresentation "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
188
189func CFStringCreateExternalRepresentation(strRef CFRef) CFRef {
190	ret := syscall(abi.FuncPCABI0(x509_CFStringCreateExternalRepresentation_trampoline), kCFAllocatorDefault, uintptr(strRef), kCFStringEncodingUTF8, 0, 0, 0)
191	return CFRef(ret)
192}
193func x509_CFStringCreateExternalRepresentation_trampoline()
194
195// syscall is implemented in the runtime package (runtime/sys_darwin.go)
196func syscall(fn, a1, a2, a3, a4, a5, a6 uintptr) uintptr
197
198// ReleaseCFArray iterates through an array, releasing its contents, and then
199// releases the array itself. This is necessary because we cannot, easily, set the
200// CFArrayCallBacks argument when creating CFArrays.
201func ReleaseCFArray(array CFRef) {
202	for i := 0; i < CFArrayGetCount(array); i++ {
203		ref := CFArrayGetValueAtIndex(array, i)
204		CFRelease(ref)
205	}
206	CFRelease(array)
207}
208