This repository has been archived by the owner on Jun 28, 2023. It is now read-only.
/
keytar_darwin.go
156 lines (131 loc) · 3.48 KB
/
keytar_darwin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package keytar
// #cgo LDFLAGS: -framework CoreFoundation -framework Security
// #include <stdlib.h>
// #include <CoreFoundation/CoreFoundation.h>
// #include <Security/Security.h>
import "C"
import (
// System imports
"unsafe"
)
const (
// nullSecKeychainRef is a NULL SecKeychainRef value.
nullSecKeychainRef C.SecKeychainRef = 0
// nullCFTypeRef is a NULL CFTypeRef value.
nullCFTypeRef C.CFTypeRef = 0
)
// keychainOSX implements the Keychain interface on OS X by using the Security
// framework to store items in the user's login keychain.
type keychainOSX struct{}
func (*keychainOSX) AddPassword(service, account, password string) error {
// Validate input
serviceValid := isValidNonNullUTF8(service)
accountValid := isValidNonNullUTF8(account)
passwordValid := isValidNonNullUTF8(password)
if !(serviceValid && accountValid && passwordValid) {
return ErrInvalidValue
}
// Convert values to C strings
serviceCStr := C.CString(service)
defer C.free(unsafe.Pointer(serviceCStr))
accountCStr := C.CString(account)
defer C.free(unsafe.Pointer(accountCStr))
passwordBlob := unsafe.Pointer(C.CString(password))
defer C.free(passwordBlob)
// Try to add the password
status := C.SecKeychainAddGenericPassword(
nullSecKeychainRef,
C.UInt32(len(service)),
serviceCStr,
C.UInt32(len(account)),
accountCStr,
C.UInt32(len(password)),
passwordBlob,
nil,
)
// Check for errors
if status != C.errSecSuccess {
return ErrUnknown
}
// All done
return nil
}
func (*keychainOSX) GetPassword(service, account string) (string, error) {
// Validate input
serviceValid := isValidNonNullUTF8(service)
accountValid := isValidNonNullUTF8(account)
if !(serviceValid && accountValid) {
return "", ErrInvalidValue
}
// Convert values to C strings
serviceCStr := C.CString(service)
defer C.free(unsafe.Pointer(serviceCStr))
accountCStr := C.CString(account)
defer C.free(unsafe.Pointer(accountCStr))
// Look for a match
var passwordData unsafe.Pointer
var passwordDataLength C.UInt32
status := C.SecKeychainFindGenericPassword(
nullCFTypeRef,
C.UInt32(len(service)),
serviceCStr,
C.UInt32(len(account)),
accountCStr,
&passwordDataLength,
&passwordData,
nil,
)
// Check for errors
if status != C.errSecSuccess {
return "", ErrNotFound
}
// Create the result
result := C.GoStringN((*C.char)(passwordData), C.int(passwordDataLength))
// Cleanup the temporary buffer
C.SecKeychainItemFreeContent(nil, passwordData)
// All done
return result, nil
}
func (*keychainOSX) DeletePassword(service, account string) error {
// Validate input
serviceValid := isValidNonNullUTF8(service)
accountValid := isValidNonNullUTF8(account)
if !(serviceValid && accountValid) {
return ErrInvalidValue
}
// Convert values to C strings
serviceCStr := C.CString(service)
defer C.free(unsafe.Pointer(serviceCStr))
accountCStr := C.CString(account)
defer C.free(unsafe.Pointer(accountCStr))
// Grab the item
var item C.SecKeychainItemRef
status := C.SecKeychainFindGenericPassword(
nullCFTypeRef,
C.UInt32(len(service)),
serviceCStr,
C.UInt32(len(account)),
accountCStr,
nil,
nil,
&item,
)
// Check for errors
if status != C.errSecSuccess {
return ErrNotFound
}
// Delete the item
status = C.SecKeychainItemDelete(item)
// Free the item
C.CFRelease(C.CFTypeRef(item))
// Check for errors
if status != C.errSecSuccess {
return ErrUnknown
}
// All done
return nil
}
func init() {
// Register the OS X keychain implementation
keychain = &keychainOSX{}
}