1
2
3
4
5
6
7
8
9
10 package gssapi
11
12
13 import "C"
14 import (
15 "fmt"
16 "net"
17 "strconv"
18 "strings"
19 "sync"
20 "unsafe"
21 )
22
23
24 func New(target, username, password string, passwordSet bool, props map[string]string) (*SaslClient, error) {
25 initOnce.Do(initSSPI)
26 if initError != nil {
27 return nil, initError
28 }
29
30 var err error
31 serviceName := "mongodb"
32 serviceRealm := ""
33 canonicalizeHostName := false
34 var serviceHostSet bool
35
36 for key, value := range props {
37 switch strings.ToUpper(key) {
38 case "CANONICALIZE_HOST_NAME":
39 canonicalizeHostName, err = strconv.ParseBool(value)
40 if err != nil {
41 return nil, fmt.Errorf("%s must be a boolean (true, false, 0, 1) but got '%s'", key, value)
42 }
43
44 case "SERVICE_REALM":
45 serviceRealm = value
46 case "SERVICE_NAME":
47 serviceName = value
48 case "SERVICE_HOST":
49 serviceHostSet = true
50 target = value
51 }
52 }
53
54 if canonicalizeHostName {
55
56 if serviceHostSet {
57 return nil, fmt.Errorf("CANONICALIZE_HOST_NAME and SERVICE_HOST canonot both be specified")
58 }
59
60 names, err := net.LookupAddr(target)
61 if err != nil || len(names) == 0 {
62 return nil, fmt.Errorf("unable to canonicalize hostname: %s", err)
63 }
64 target = names[0]
65 if target[len(target)-1] == '.' {
66 target = target[:len(target)-1]
67 }
68 }
69
70 servicePrincipalName := fmt.Sprintf("%s/%s", serviceName, target)
71 if serviceRealm != "" {
72 servicePrincipalName += "@" + serviceRealm
73 }
74
75 return &SaslClient{
76 servicePrincipalName: servicePrincipalName,
77 username: username,
78 password: password,
79 passwordSet: passwordSet,
80 }, nil
81 }
82
83 type SaslClient struct {
84 servicePrincipalName string
85 username string
86 password string
87 passwordSet bool
88
89
90 state C.sspi_client_state
91 contextComplete bool
92 done bool
93 }
94
95 func (sc *SaslClient) Close() {
96 C.sspi_client_destroy(&sc.state)
97 }
98
99 func (sc *SaslClient) Start() (string, []byte, error) {
100 const mechName = "GSSAPI"
101
102 var cusername *C.char
103 var cpassword *C.char
104 if sc.username != "" {
105 cusername = C.CString(sc.username)
106 defer C.free(unsafe.Pointer(cusername))
107 if sc.passwordSet {
108 cpassword = C.CString(sc.password)
109 defer C.free(unsafe.Pointer(cpassword))
110 }
111 }
112 status := C.sspi_client_init(&sc.state, cusername, cpassword)
113
114 if status != C.SSPI_OK {
115 return mechName, nil, sc.getError("unable to initialize client")
116 }
117
118 payload, err := sc.Next(nil)
119
120 return mechName, payload, err
121 }
122
123 func (sc *SaslClient) Next(challenge []byte) ([]byte, error) {
124
125 var outBuf C.PVOID
126 var outBufLen C.ULONG
127
128 if sc.contextComplete {
129 if sc.username == "" {
130 var cusername *C.char
131 status := C.sspi_client_username(&sc.state, &cusername)
132 if status != C.SSPI_OK {
133 return nil, sc.getError("unable to acquire username")
134 }
135 defer C.free(unsafe.Pointer(cusername))
136 sc.username = C.GoString((*C.char)(unsafe.Pointer(cusername)))
137 }
138
139 bytes := append([]byte{1, 0, 0, 0}, []byte(sc.username)...)
140 buf := (C.PVOID)(unsafe.Pointer(&bytes[0]))
141 bufLen := C.ULONG(len(bytes))
142 status := C.sspi_client_wrap_msg(&sc.state, buf, bufLen, &outBuf, &outBufLen)
143 if status != C.SSPI_OK {
144 return nil, sc.getError("unable to wrap authz")
145 }
146
147 sc.done = true
148 } else {
149 var buf C.PVOID
150 var bufLen C.ULONG
151 if len(challenge) > 0 {
152 buf = (C.PVOID)(unsafe.Pointer(&challenge[0]))
153 bufLen = C.ULONG(len(challenge))
154 }
155 cservicePrincipalName := C.CString(sc.servicePrincipalName)
156 defer C.free(unsafe.Pointer(cservicePrincipalName))
157
158 status := C.sspi_client_negotiate(&sc.state, cservicePrincipalName, buf, bufLen, &outBuf, &outBufLen)
159 switch status {
160 case C.SSPI_OK:
161 sc.contextComplete = true
162 case C.SSPI_CONTINUE:
163 default:
164 return nil, sc.getError("unable to negotiate with server")
165 }
166 }
167
168 if outBuf != C.PVOID(nil) {
169 defer C.free(unsafe.Pointer(outBuf))
170 }
171
172 return C.GoBytes(unsafe.Pointer(outBuf), C.int(outBufLen)), nil
173 }
174
175 func (sc *SaslClient) Completed() bool {
176 return sc.done
177 }
178
179 func (sc *SaslClient) getError(prefix string) error {
180 return getError(prefix, sc.state.status)
181 }
182
183 var initOnce sync.Once
184 var initError error
185
186 func initSSPI() {
187 rc := C.sspi_init()
188 if rc != 0 {
189 initError = fmt.Errorf("error initializing sspi: %v", rc)
190 }
191 }
192
193 func getError(prefix string, status C.SECURITY_STATUS) error {
194 var s string
195 switch status {
196 case C.SEC_E_ALGORITHM_MISMATCH:
197 s = "The client and server cannot communicate because they do not possess a common algorithm."
198 case C.SEC_E_BAD_BINDINGS:
199 s = "The SSPI channel bindings supplied by the client are incorrect."
200 case C.SEC_E_BAD_PKGID:
201 s = "The requested package identifier does not exist."
202 case C.SEC_E_BUFFER_TOO_SMALL:
203 s = "The buffers supplied to the function are not large enough to contain the information."
204 case C.SEC_E_CANNOT_INSTALL:
205 s = "The security package cannot initialize successfully and should not be installed."
206 case C.SEC_E_CANNOT_PACK:
207 s = "The package is unable to pack the context."
208 case C.SEC_E_CERT_EXPIRED:
209 s = "The received certificate has expired."
210 case C.SEC_E_CERT_UNKNOWN:
211 s = "An unknown error occurred while processing the certificate."
212 case C.SEC_E_CERT_WRONG_USAGE:
213 s = "The certificate is not valid for the requested usage."
214 case C.SEC_E_CONTEXT_EXPIRED:
215 s = "The application is referencing a context that has already been closed. A properly written application should not receive this error."
216 case C.SEC_E_CROSSREALM_DELEGATION_FAILURE:
217 s = "The server attempted to make a Kerberos-constrained delegation request for a target outside the server's realm."
218 case C.SEC_E_CRYPTO_SYSTEM_INVALID:
219 s = "The cryptographic system or checksum function is not valid because a required function is unavailable."
220 case C.SEC_E_DECRYPT_FAILURE:
221 s = "The specified data could not be decrypted."
222 case C.SEC_E_DELEGATION_REQUIRED:
223 s = "The requested operation cannot be completed. The computer must be trusted for delegation"
224 case C.SEC_E_DOWNGRADE_DETECTED:
225 s = "The system detected a possible attempt to compromise security. Verify that the server that authenticated you can be contacted."
226 case C.SEC_E_ENCRYPT_FAILURE:
227 s = "The specified data could not be encrypted."
228 case C.SEC_E_ILLEGAL_MESSAGE:
229 s = "The message received was unexpected or badly formatted."
230 case C.SEC_E_INCOMPLETE_CREDENTIALS:
231 s = "The credentials supplied were not complete and could not be verified. The context could not be initialized."
232 case C.SEC_E_INCOMPLETE_MESSAGE:
233 s = "The message supplied was incomplete. The signature was not verified."
234 case C.SEC_E_INSUFFICIENT_MEMORY:
235 s = "Not enough memory is available to complete the request."
236 case C.SEC_E_INTERNAL_ERROR:
237 s = "An error occurred that did not map to an SSPI error code."
238 case C.SEC_E_INVALID_HANDLE:
239 s = "The handle passed to the function is not valid."
240 case C.SEC_E_INVALID_TOKEN:
241 s = "The token passed to the function is not valid."
242 case C.SEC_E_ISSUING_CA_UNTRUSTED:
243 s = "An untrusted certification authority (CA) was detected while processing the smart card certificate used for authentication."
244 case C.SEC_E_ISSUING_CA_UNTRUSTED_KDC:
245 s = "An untrusted CA was detected while processing the domain controller certificate used for authentication. The system event log contains additional information."
246 case C.SEC_E_KDC_CERT_EXPIRED:
247 s = "The domain controller certificate used for smart card logon has expired."
248 case C.SEC_E_KDC_CERT_REVOKED:
249 s = "The domain controller certificate used for smart card logon has been revoked."
250 case C.SEC_E_KDC_INVALID_REQUEST:
251 s = "A request that is not valid was sent to the KDC."
252 case C.SEC_E_KDC_UNABLE_TO_REFER:
253 s = "The KDC was unable to generate a referral for the service requested."
254 case C.SEC_E_KDC_UNKNOWN_ETYPE:
255 s = "The requested encryption type is not supported by the KDC."
256 case C.SEC_E_LOGON_DENIED:
257 s = "The logon has been denied"
258 case C.SEC_E_MAX_REFERRALS_EXCEEDED:
259 s = "The number of maximum ticket referrals has been exceeded."
260 case C.SEC_E_MESSAGE_ALTERED:
261 s = "The message supplied for verification has been altered."
262 case C.SEC_E_MULTIPLE_ACCOUNTS:
263 s = "The received certificate was mapped to multiple accounts."
264 case C.SEC_E_MUST_BE_KDC:
265 s = "The local computer must be a Kerberos domain controller (KDC)"
266 case C.SEC_E_NO_AUTHENTICATING_AUTHORITY:
267 s = "No authority could be contacted for authentication."
268 case C.SEC_E_NO_CREDENTIALS:
269 s = "No credentials are available."
270 case C.SEC_E_NO_IMPERSONATION:
271 s = "No impersonation is allowed for this context."
272 case C.SEC_E_NO_IP_ADDRESSES:
273 s = "Unable to accomplish the requested task because the local computer does not have any IP addresses."
274 case C.SEC_E_NO_KERB_KEY:
275 s = "No Kerberos key was found."
276 case C.SEC_E_NO_PA_DATA:
277 s = "Policy administrator (PA) data is needed to determine the encryption type"
278 case C.SEC_E_NO_S4U_PROT_SUPPORT:
279 s = "The Kerberos subsystem encountered an error. A service for user protocol request was made against a domain controller which does not support service for a user."
280 case C.SEC_E_NO_TGT_REPLY:
281 s = "The client is trying to negotiate a context and the server requires a user-to-user connection"
282 case C.SEC_E_NOT_OWNER:
283 s = "The caller of the function does not own the credentials."
284 case C.SEC_E_OK:
285 s = "The operation completed successfully."
286 case C.SEC_E_OUT_OF_SEQUENCE:
287 s = "The message supplied for verification is out of sequence."
288 case C.SEC_E_PKINIT_CLIENT_FAILURE:
289 s = "The smart card certificate used for authentication is not trusted."
290 case C.SEC_E_PKINIT_NAME_MISMATCH:
291 s = "The client certificate does not contain a valid UPN or does not match the client name in the logon request."
292 case C.SEC_E_QOP_NOT_SUPPORTED:
293 s = "The quality of protection attribute is not supported by this package."
294 case C.SEC_E_REVOCATION_OFFLINE_C:
295 s = "The revocation status of the smart card certificate used for authentication could not be determined."
296 case C.SEC_E_REVOCATION_OFFLINE_KDC:
297 s = "The revocation status of the domain controller certificate used for smart card authentication could not be determined. The system event log contains additional information."
298 case C.SEC_E_SECPKG_NOT_FOUND:
299 s = "The security package was not recognized."
300 case C.SEC_E_SECURITY_QOS_FAILED:
301 s = "The security context could not be established due to a failure in the requested quality of service (for example"
302 case C.SEC_E_SHUTDOWN_IN_PROGRESS:
303 s = "A system shutdown is in progress."
304 case C.SEC_E_SMARTCARD_CERT_EXPIRED:
305 s = "The smart card certificate used for authentication has expired."
306 case C.SEC_E_SMARTCARD_CERT_REVOKED:
307 s = "The smart card certificate used for authentication has been revoked. Additional information may exist in the event log."
308 case C.SEC_E_SMARTCARD_LOGON_REQUIRED:
309 s = "Smart card logon is required and was not used."
310 case C.SEC_E_STRONG_CRYPTO_NOT_SUPPORTED:
311 s = "The other end of the security negotiation requires strong cryptography"
312 case C.SEC_E_TARGET_UNKNOWN:
313 s = "The target was not recognized."
314 case C.SEC_E_TIME_SKEW:
315 s = "The clocks on the client and server computers do not match."
316 case C.SEC_E_TOO_MANY_PRINCIPALS:
317 s = "The KDC reply contained more than one principal name."
318 case C.SEC_E_UNFINISHED_CONTEXT_DELETED:
319 s = "A security context was deleted before the context was completed. This is considered a logon failure."
320 case C.SEC_E_UNKNOWN_CREDENTIALS:
321 s = "The credentials provided were not recognized."
322 case C.SEC_E_UNSUPPORTED_FUNCTION:
323 s = "The requested function is not supported."
324 case C.SEC_E_UNSUPPORTED_PREAUTH:
325 s = "An unsupported preauthentication mechanism was presented to the Kerberos package."
326 case C.SEC_E_UNTRUSTED_ROOT:
327 s = "The certificate chain was issued by an authority that is not trusted."
328 case C.SEC_E_WRONG_CREDENTIAL_HANDLE:
329 s = "The supplied credential handle does not match the credential associated with the security context."
330 case C.SEC_E_WRONG_PRINCIPAL:
331 s = "The target principal name is incorrect."
332 case C.SEC_I_COMPLETE_AND_CONTINUE:
333 s = "The function completed successfully"
334 case C.SEC_I_COMPLETE_NEEDED:
335 s = "The function completed successfully"
336 case C.SEC_I_CONTEXT_EXPIRED:
337 s = "The message sender has finished using the connection and has initiated a shutdown. For information about initiating or recognizing a shutdown"
338 case C.SEC_I_CONTINUE_NEEDED:
339 s = "The function completed successfully"
340 case C.SEC_I_INCOMPLETE_CREDENTIALS:
341 s = "The credentials supplied were not complete and could not be verified. Additional information can be returned from the context."
342 case C.SEC_I_LOCAL_LOGON:
343 s = "The logon was completed"
344 case C.SEC_I_NO_LSA_CONTEXT:
345 s = "There is no LSA mode context associated with this context."
346 case C.SEC_I_RENEGOTIATE:
347 s = "The context data must be renegotiated with the peer."
348 default:
349 return fmt.Errorf("%s: 0x%x", prefix, uint32(status))
350 }
351
352 return fmt.Errorf("%s: %s(0x%x)", prefix, s, uint32(status))
353 }
354
View as plain text