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