]>
Commit | Line | Data |
---|---|---|
cc754834 | 1 | /* |
71f63681 | 2 | * "$Id: sspi.c 11760 2014-03-28 12:58:24Z msweet $" |
cc754834 | 3 | * |
71f63681 | 4 | * Windows SSPI SSL implementation for CUPS. |
321d8d57 | 5 | * |
71f63681 | 6 | * Copyright 2010-2014 by Apple Inc. |
cc754834 | 7 | * |
71f63681 MS |
8 | * These coded instructions, statements, and computer programs are the |
9 | * property of Apple Inc. and are protected by Federal copyright | |
10 | * law. Distribution and use rights are outlined in the file "LICENSE.txt" | |
11 | * which should have been included with this file. If this file is | |
12 | * file is missing or damaged, see the license at "http://www.cups.org/". | |
cc754834 MS |
13 | */ |
14 | ||
15 | /* | |
16 | * Include necessary headers... | |
17 | */ | |
18 | ||
19 | #include "sspi-private.h" | |
20 | #include "debug-private.h" | |
321d8d57 | 21 | |
cc754834 MS |
22 | |
23 | /* required to link this library for certificate functions */ | |
24 | #pragma comment(lib, "Crypt32.lib") | |
25 | #pragma comment(lib, "Secur32.lib") | |
26 | #pragma comment(lib, "Ws2_32.lib") | |
27 | ||
28 | ||
29 | #if !defined(SECURITY_FLAG_IGNORE_UNKNOWN_CA) | |
30 | # define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100 /* Untrusted root */ | |
31 | #endif | |
32 | ||
33 | #if !defined(SECURITY_FLAG_IGNORE_CERT_DATE_INVALID) | |
34 | # define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */ | |
35 | #endif | |
36 | ||
37 | static DWORD sspi_verify_certificate(PCCERT_CONTEXT serverCert, | |
38 | const CHAR *serverName, | |
39 | DWORD dwCertFlags); | |
40 | ||
41 | ||
42 | /* | |
43 | * 'sspi_alloc()' - Allocate SSPI ssl object | |
44 | */ | |
45 | _sspi_struct_t* /* O - New SSPI/SSL object */ | |
46 | _sspiAlloc(void) | |
47 | { | |
48 | _sspi_struct_t *conn = calloc(sizeof(_sspi_struct_t), 1); | |
49 | ||
50 | if (conn) | |
51 | conn->sock = INVALID_SOCKET; | |
52 | ||
53 | return (conn); | |
54 | } | |
55 | ||
56 | ||
57 | /* | |
58 | * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store | |
59 | * If one cannot be found, one is created. | |
60 | */ | |
61 | BOOL /* O - 1 on success, 0 on failure */ | |
62 | _sspiGetCredentials(_sspi_struct_t *conn, | |
63 | /* I - Client connection */ | |
64 | const LPWSTR container, | |
65 | /* I - Cert container name */ | |
66 | const TCHAR *cn, /* I - Common name of certificate */ | |
67 | BOOL isServer) | |
321d8d57 | 68 | /* I - Is caller a server? */ |
cc754834 MS |
69 | { |
70 | HCERTSTORE store = NULL; /* Certificate store */ | |
71 | PCCERT_CONTEXT storedContext = NULL; | |
321d8d57 | 72 | /* Context created from the store */ |
cc754834 | 73 | PCCERT_CONTEXT createdContext = NULL; |
321d8d57 | 74 | /* Context created by us */ |
cc754834 MS |
75 | DWORD dwSize = 0; /* 32 bit size */ |
76 | PBYTE p = NULL; /* Temporary storage */ | |
77 | HCRYPTPROV hProv = (HCRYPTPROV) NULL; | |
78 | /* Handle to a CSP */ | |
79 | CERT_NAME_BLOB sib; /* Arbitrary array of bytes */ | |
80 | SCHANNEL_CRED SchannelCred; /* Schannel credential data */ | |
81 | TimeStamp tsExpiry; /* Time stamp */ | |
82 | SECURITY_STATUS Status; /* Status */ | |
83 | HCRYPTKEY hKey = (HCRYPTKEY) NULL; | |
84 | /* Handle to crypto key */ | |
85 | CRYPT_KEY_PROV_INFO kpi; /* Key container info */ | |
86 | SYSTEMTIME et; /* System time */ | |
87 | CERT_EXTENSIONS exts; /* Array of cert extensions */ | |
88 | CRYPT_KEY_PROV_INFO ckp; /* Handle to crypto key */ | |
89 | BOOL ok = TRUE; /* Return value */ | |
90 | ||
71f63681 MS |
91 | |
92 | DEBUG_printf(("_sspiGetCredentials(conn=%p, container=%p, cn=\"%s\", isServer=%d)", conn, container, cn, isServer)); | |
93 | ||
cc754834 MS |
94 | if (!conn) |
95 | return (FALSE); | |
96 | if (!cn) | |
97 | return (FALSE); | |
98 | ||
99 | if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W, | |
100 | PROV_RSA_FULL, | |
101 | CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET)) | |
102 | { | |
103 | if (GetLastError() == NTE_EXISTS) | |
104 | { | |
105 | if (!CryptAcquireContextW(&hProv, (LPWSTR) container, MS_DEF_PROV_W, | |
106 | PROV_RSA_FULL, CRYPT_MACHINE_KEYSET)) | |
107 | { | |
108 | DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n", | |
109 | GetLastError())); | |
110 | ok = FALSE; | |
111 | goto cleanup; | |
112 | } | |
113 | } | |
114 | } | |
115 | ||
116 | store = CertOpenStore(CERT_STORE_PROV_SYSTEM, | |
117 | X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, | |
118 | hProv, | |
119 | CERT_SYSTEM_STORE_LOCAL_MACHINE | | |
120 | CERT_STORE_NO_CRYPT_RELEASE_FLAG | | |
121 | CERT_STORE_OPEN_EXISTING_FLAG, | |
122 | L"MY"); | |
123 | ||
124 | if (!store) | |
125 | { | |
126 | DEBUG_printf(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n", | |
127 | GetLastError())); | |
128 | ok = FALSE; | |
129 | goto cleanup; | |
130 | } | |
131 | ||
132 | dwSize = 0; | |
321d8d57 | 133 | |
cc754834 MS |
134 | if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR, |
135 | NULL, NULL, &dwSize, NULL)) | |
136 | { | |
137 | DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n", | |
138 | GetLastError())); | |
139 | ok = FALSE; | |
140 | goto cleanup; | |
141 | } | |
142 | ||
143 | p = (PBYTE) malloc(dwSize); | |
144 | ||
145 | if (!p) | |
146 | { | |
147 | DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize)); | |
148 | ok = FALSE; | |
149 | goto cleanup; | |
150 | } | |
151 | ||
152 | if (!CertStrToName(X509_ASN_ENCODING, cn, CERT_OID_NAME_STR, NULL, | |
153 | p, &dwSize, NULL)) | |
154 | { | |
155 | DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x", | |
156 | GetLastError())); | |
157 | ok = FALSE; | |
158 | goto cleanup; | |
159 | } | |
160 | ||
161 | sib.cbData = dwSize; | |
162 | sib.pbData = p; | |
163 | ||
164 | storedContext = CertFindCertificateInStore(store, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, | |
165 | 0, CERT_FIND_SUBJECT_NAME, &sib, NULL); | |
321d8d57 | 166 | |
cc754834 MS |
167 | if (!storedContext) |
168 | { | |
321d8d57 | 169 | /* |
cc754834 MS |
170 | * If we couldn't find the context, then we'll |
171 | * create a new one | |
172 | */ | |
173 | if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey)) | |
174 | { | |
175 | DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x", | |
176 | GetLastError())); | |
177 | ok = FALSE; | |
178 | goto cleanup; | |
179 | } | |
180 | ||
181 | ZeroMemory(&kpi, sizeof(kpi)); | |
182 | kpi.pwszContainerName = (LPWSTR) container; | |
183 | kpi.pwszProvName = MS_DEF_PROV_W; | |
184 | kpi.dwProvType = PROV_RSA_FULL; | |
185 | kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID; | |
186 | kpi.dwKeySpec = AT_KEYEXCHANGE; | |
187 | ||
188 | GetSystemTime(&et); | |
189 | et.wYear += 10; | |
190 | ||
191 | ZeroMemory(&exts, sizeof(exts)); | |
192 | ||
193 | createdContext = CertCreateSelfSignCertificate(hProv, &sib, 0, &kpi, NULL, NULL, | |
194 | &et, &exts); | |
321d8d57 | 195 | |
cc754834 MS |
196 | if (!createdContext) |
197 | { | |
198 | DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x", | |
199 | GetLastError())); | |
200 | ok = FALSE; | |
201 | goto cleanup; | |
202 | } | |
203 | ||
204 | if (!CertAddCertificateContextToStore(store, createdContext, | |
205 | CERT_STORE_ADD_REPLACE_EXISTING, | |
206 | &storedContext)) | |
207 | { | |
208 | DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x", | |
209 | GetLastError())); | |
210 | ok = FALSE; | |
211 | goto cleanup; | |
212 | } | |
213 | ||
214 | ZeroMemory(&ckp, sizeof(ckp)); | |
215 | ckp.pwszContainerName = (LPWSTR) container; | |
216 | ckp.pwszProvName = MS_DEF_PROV_W; | |
217 | ckp.dwProvType = PROV_RSA_FULL; | |
218 | ckp.dwFlags = CRYPT_MACHINE_KEYSET; | |
219 | ckp.dwKeySpec = AT_KEYEXCHANGE; | |
220 | ||
221 | if (!CertSetCertificateContextProperty(storedContext, | |
222 | CERT_KEY_PROV_INFO_PROP_ID, | |
223 | 0, &ckp)) | |
224 | { | |
225 | DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x", | |
226 | GetLastError())); | |
227 | ok = FALSE; | |
228 | goto cleanup; | |
229 | } | |
230 | } | |
231 | ||
232 | ZeroMemory(&SchannelCred, sizeof(SchannelCred)); | |
233 | ||
234 | SchannelCred.dwVersion = SCHANNEL_CRED_VERSION; | |
235 | SchannelCred.cCreds = 1; | |
236 | SchannelCred.paCred = &storedContext; | |
237 | ||
238 | /* | |
239 | * SSPI doesn't seem to like it if grbitEnabledProtocols | |
240 | * is set for a client | |
241 | */ | |
242 | if (isServer) | |
243 | SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3TLS1; | |
244 | ||
245 | /* | |
246 | * Create an SSPI credential. | |
247 | */ | |
321d8d57 | 248 | Status = AcquireCredentialsHandle(NULL, UNISP_NAME, |
cc754834 MS |
249 | isServer ? SECPKG_CRED_INBOUND:SECPKG_CRED_OUTBOUND, |
250 | NULL, &SchannelCred, NULL, NULL, &conn->creds, | |
251 | &tsExpiry); | |
252 | if (Status != SEC_E_OK) | |
253 | { | |
254 | DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status)); | |
255 | ok = FALSE; | |
256 | goto cleanup; | |
257 | } | |
258 | ||
259 | cleanup: | |
260 | ||
261 | /* | |
262 | * Cleanup | |
263 | */ | |
264 | if (hKey) | |
265 | CryptDestroyKey(hKey); | |
266 | ||
267 | if (createdContext) | |
268 | CertFreeCertificateContext(createdContext); | |
269 | ||
270 | if (storedContext) | |
271 | CertFreeCertificateContext(storedContext); | |
272 | ||
273 | if (p) | |
274 | free(p); | |
275 | ||
276 | if (store) | |
277 | CertCloseStore(store, 0); | |
278 | ||
279 | if (hProv) | |
280 | CryptReleaseContext(hProv, 0); | |
281 | ||
282 | return (ok); | |
283 | } | |
284 | ||
285 | ||
286 | /* | |
287 | * '_sspiConnect()' - Make an SSL connection. This function | |
288 | * assumes a TCP/IP connection has already | |
289 | * been successfully made | |
290 | */ | |
291 | BOOL /* O - 1 on success, 0 on failure */ | |
292 | _sspiConnect(_sspi_struct_t *conn, /* I - Client connection */ | |
321d8d57 | 293 | const CHAR *hostname) /* I - Server hostname */ |
cc754834 MS |
294 | { |
295 | PCCERT_CONTEXT serverCert; /* Server certificate */ | |
296 | DWORD dwSSPIFlags; /* SSL connection attributes we want */ | |
297 | DWORD dwSSPIOutFlags; /* SSL connection attributes we got */ | |
298 | TimeStamp tsExpiry; /* Time stamp */ | |
299 | SECURITY_STATUS scRet; /* Status */ | |
300 | DWORD cbData; /* Data count */ | |
301 | SecBufferDesc inBuffer; /* Array of SecBuffer structs */ | |
302 | SecBuffer inBuffers[2]; /* Security package buffer */ | |
303 | SecBufferDesc outBuffer; /* Array of SecBuffer structs */ | |
304 | SecBuffer outBuffers[1]; /* Security package buffer */ | |
305 | BOOL ok = TRUE; /* Return value */ | |
306 | ||
307 | serverCert = NULL; | |
308 | ||
309 | dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | | |
310 | ISC_REQ_REPLAY_DETECT | | |
311 | ISC_REQ_CONFIDENTIALITY | | |
312 | ISC_RET_EXTENDED_ERROR | | |
313 | ISC_REQ_ALLOCATE_MEMORY | | |
314 | ISC_REQ_STREAM; | |
315 | ||
316 | /* | |
317 | * Initiate a ClientHello message and generate a token. | |
318 | */ | |
319 | outBuffers[0].pvBuffer = NULL; | |
320 | outBuffers[0].BufferType = SECBUFFER_TOKEN; | |
321 | outBuffers[0].cbBuffer = 0; | |
322 | ||
323 | outBuffer.cBuffers = 1; | |
324 | outBuffer.pBuffers = outBuffers; | |
325 | outBuffer.ulVersion = SECBUFFER_VERSION; | |
326 | ||
327 | scRet = InitializeSecurityContext(&conn->creds, NULL, TEXT(""), dwSSPIFlags, | |
328 | 0, SECURITY_NATIVE_DREP, NULL, 0, &conn->context, | |
329 | &outBuffer, &dwSSPIOutFlags, &tsExpiry); | |
330 | ||
331 | if (scRet != SEC_I_CONTINUE_NEEDED) | |
332 | { | |
333 | DEBUG_printf(("_sspiConnect: InitializeSecurityContext(1) failed: %x", scRet)); | |
334 | ok = FALSE; | |
335 | goto cleanup; | |
336 | } | |
337 | ||
338 | /* | |
339 | * Send response to server if there is one. | |
340 | */ | |
341 | if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer) | |
342 | { | |
343 | cbData = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0); | |
344 | ||
345 | if ((cbData == SOCKET_ERROR) || !cbData) | |
346 | { | |
347 | DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError())); | |
348 | FreeContextBuffer(outBuffers[0].pvBuffer); | |
349 | DeleteSecurityContext(&conn->context); | |
350 | ok = FALSE; | |
351 | goto cleanup; | |
352 | } | |
353 | ||
354 | DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData)); | |
355 | ||
356 | /* | |
357 | * Free output buffer. | |
358 | */ | |
359 | FreeContextBuffer(outBuffers[0].pvBuffer); | |
360 | outBuffers[0].pvBuffer = NULL; | |
361 | } | |
362 | ||
363 | dwSSPIFlags = ISC_REQ_MANUAL_CRED_VALIDATION | | |
364 | ISC_REQ_SEQUENCE_DETECT | | |
365 | ISC_REQ_REPLAY_DETECT | | |
366 | ISC_REQ_CONFIDENTIALITY | | |
367 | ISC_RET_EXTENDED_ERROR | | |
368 | ISC_REQ_ALLOCATE_MEMORY | | |
369 | ISC_REQ_STREAM; | |
370 | ||
371 | conn->decryptBufferUsed = 0; | |
372 | ||
321d8d57 | 373 | /* |
cc754834 MS |
374 | * Loop until the handshake is finished or an error occurs. |
375 | */ | |
376 | scRet = SEC_I_CONTINUE_NEEDED; | |
377 | ||
378 | while(scRet == SEC_I_CONTINUE_NEEDED || | |
379 | scRet == SEC_E_INCOMPLETE_MESSAGE || | |
321d8d57 | 380 | scRet == SEC_I_INCOMPLETE_CREDENTIALS) |
cc754834 MS |
381 | { |
382 | if ((conn->decryptBufferUsed == 0) || (scRet == SEC_E_INCOMPLETE_MESSAGE)) | |
383 | { | |
321d8d57 | 384 | if (conn->decryptBufferLength <= conn->decryptBufferUsed) |
cc754834 MS |
385 | { |
386 | conn->decryptBufferLength += 4096; | |
387 | conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer, conn->decryptBufferLength); | |
388 | ||
389 | if (!conn->decryptBuffer) | |
390 | { | |
391 | DEBUG_printf(("_sspiConnect: unable to allocate %d byte decrypt buffer", | |
392 | conn->decryptBufferLength)); | |
393 | SetLastError(E_OUTOFMEMORY); | |
394 | ok = FALSE; | |
395 | goto cleanup; | |
396 | } | |
397 | } | |
321d8d57 MS |
398 | |
399 | cbData = recv(conn->sock, conn->decryptBuffer + conn->decryptBufferUsed, | |
cc754834 | 400 | (int) (conn->decryptBufferLength - conn->decryptBufferUsed), 0); |
321d8d57 | 401 | |
cc754834 MS |
402 | if (cbData == SOCKET_ERROR) |
403 | { | |
404 | DEBUG_printf(("_sspiConnect: recv failed: %d", WSAGetLastError())); | |
405 | ok = FALSE; | |
406 | goto cleanup; | |
407 | } | |
408 | else if (cbData == 0) | |
409 | { | |
410 | DEBUG_printf(("_sspiConnect: server unexpectedly disconnected")); | |
411 | ok = FALSE; | |
412 | goto cleanup; | |
413 | } | |
414 | ||
415 | DEBUG_printf(("_sspiConnect: %d bytes of handshake data received", | |
416 | cbData)); | |
417 | ||
418 | conn->decryptBufferUsed += cbData; | |
419 | } | |
420 | ||
421 | /* | |
422 | * Set up the input buffers. Buffer 0 is used to pass in data | |
423 | * received from the server. Schannel will consume some or all | |
424 | * of this. Leftover data (if any) will be placed in buffer 1 and | |
425 | * given a buffer type of SECBUFFER_EXTRA. | |
426 | */ | |
427 | inBuffers[0].pvBuffer = conn->decryptBuffer; | |
428 | inBuffers[0].cbBuffer = (unsigned long) conn->decryptBufferUsed; | |
429 | inBuffers[0].BufferType = SECBUFFER_TOKEN; | |
430 | ||
431 | inBuffers[1].pvBuffer = NULL; | |
432 | inBuffers[1].cbBuffer = 0; | |
433 | inBuffers[1].BufferType = SECBUFFER_EMPTY; | |
434 | ||
435 | inBuffer.cBuffers = 2; | |
436 | inBuffer.pBuffers = inBuffers; | |
437 | inBuffer.ulVersion = SECBUFFER_VERSION; | |
438 | ||
439 | /* | |
440 | * Set up the output buffers. These are initialized to NULL | |
441 | * so as to make it less likely we'll attempt to free random | |
442 | * garbage later. | |
443 | */ | |
444 | outBuffers[0].pvBuffer = NULL; | |
445 | outBuffers[0].BufferType= SECBUFFER_TOKEN; | |
446 | outBuffers[0].cbBuffer = 0; | |
447 | ||
448 | outBuffer.cBuffers = 1; | |
449 | outBuffer.pBuffers = outBuffers; | |
450 | outBuffer.ulVersion = SECBUFFER_VERSION; | |
451 | ||
452 | /* | |
453 | * Call InitializeSecurityContext. | |
454 | */ | |
455 | scRet = InitializeSecurityContext(&conn->creds, &conn->context, NULL, dwSSPIFlags, | |
456 | 0, SECURITY_NATIVE_DREP, &inBuffer, 0, NULL, | |
457 | &outBuffer, &dwSSPIOutFlags, &tsExpiry); | |
458 | ||
459 | /* | |
321d8d57 | 460 | * If InitializeSecurityContext was successful (or if the error was |
cc754834 MS |
461 | * one of the special extended ones), send the contends of the output |
462 | * buffer to the server. | |
463 | */ | |
464 | if (scRet == SEC_E_OK || | |
465 | scRet == SEC_I_CONTINUE_NEEDED || | |
466 | FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)) | |
467 | { | |
468 | if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer) | |
469 | { | |
470 | cbData = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0); | |
321d8d57 | 471 | |
cc754834 MS |
472 | if ((cbData == SOCKET_ERROR) || !cbData) |
473 | { | |
474 | DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError())); | |
475 | FreeContextBuffer(outBuffers[0].pvBuffer); | |
476 | DeleteSecurityContext(&conn->context); | |
477 | ok = FALSE; | |
478 | goto cleanup; | |
479 | } | |
480 | ||
481 | DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData)); | |
482 | ||
483 | /* | |
484 | * Free output buffer. | |
485 | */ | |
486 | FreeContextBuffer(outBuffers[0].pvBuffer); | |
487 | outBuffers[0].pvBuffer = NULL; | |
488 | } | |
489 | } | |
490 | ||
491 | /* | |
492 | * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, | |
493 | * then we need to read more data from the server and try again. | |
494 | */ | |
495 | if (scRet == SEC_E_INCOMPLETE_MESSAGE) | |
496 | continue; | |
497 | ||
498 | /* | |
321d8d57 | 499 | * If InitializeSecurityContext returned SEC_E_OK, then the |
cc754834 MS |
500 | * handshake completed successfully. |
501 | */ | |
502 | if (scRet == SEC_E_OK) | |
503 | { | |
504 | /* | |
505 | * If the "extra" buffer contains data, this is encrypted application | |
506 | * protocol layer stuff. It needs to be saved. The application layer | |
507 | * will later decrypt it with DecryptMessage. | |
508 | */ | |
509 | DEBUG_printf(("_sspiConnect: Handshake was successful")); | |
510 | ||
511 | if (inBuffers[1].BufferType == SECBUFFER_EXTRA) | |
512 | { | |
513 | if (conn->decryptBufferLength < inBuffers[1].cbBuffer) | |
514 | { | |
515 | conn->decryptBuffer = realloc(conn->decryptBuffer, inBuffers[1].cbBuffer); | |
516 | ||
517 | if (!conn->decryptBuffer) | |
518 | { | |
519 | DEBUG_printf(("_sspiConnect: unable to allocate %d bytes for decrypt buffer", | |
520 | inBuffers[1].cbBuffer)); | |
521 | SetLastError(E_OUTOFMEMORY); | |
522 | ok = FALSE; | |
523 | goto cleanup; | |
524 | } | |
525 | } | |
526 | ||
527 | memmove(conn->decryptBuffer, | |
528 | conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer), | |
529 | inBuffers[1].cbBuffer); | |
530 | ||
531 | conn->decryptBufferUsed = inBuffers[1].cbBuffer; | |
532 | ||
533 | DEBUG_printf(("_sspiConnect: %d bytes of app data was bundled with handshake data", | |
534 | conn->decryptBufferUsed)); | |
535 | } | |
536 | else | |
537 | conn->decryptBufferUsed = 0; | |
538 | ||
539 | /* | |
540 | * Bail out to quit | |
541 | */ | |
542 | break; | |
543 | } | |
544 | ||
545 | /* | |
546 | * Check for fatal error. | |
547 | */ | |
548 | if (FAILED(scRet)) | |
549 | { | |
550 | DEBUG_printf(("_sspiConnect: InitializeSecurityContext(2) failed: %x", scRet)); | |
551 | ok = FALSE; | |
552 | break; | |
553 | } | |
554 | ||
555 | /* | |
556 | * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS, | |
321d8d57 | 557 | * then the server just requested client authentication. |
cc754834 MS |
558 | */ |
559 | if (scRet == SEC_I_INCOMPLETE_CREDENTIALS) | |
560 | { | |
561 | /* | |
562 | * Unimplemented | |
563 | */ | |
564 | DEBUG_printf(("_sspiConnect: server requested client credentials")); | |
565 | ok = FALSE; | |
566 | break; | |
567 | } | |
568 | ||
569 | /* | |
570 | * Copy any leftover data from the "extra" buffer, and go around | |
571 | * again. | |
572 | */ | |
573 | if (inBuffers[1].BufferType == SECBUFFER_EXTRA) | |
574 | { | |
575 | memmove(conn->decryptBuffer, | |
576 | conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer), | |
577 | inBuffers[1].cbBuffer); | |
578 | ||
579 | conn->decryptBufferUsed = inBuffers[1].cbBuffer; | |
580 | } | |
581 | else | |
582 | { | |
583 | conn->decryptBufferUsed = 0; | |
584 | } | |
585 | } | |
586 | ||
587 | if (ok) | |
588 | { | |
589 | conn->contextInitialized = TRUE; | |
590 | ||
591 | /* | |
592 | * Get the server cert | |
593 | */ | |
594 | scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (VOID*) &serverCert ); | |
595 | ||
596 | if (scRet != SEC_E_OK) | |
597 | { | |
598 | DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet)); | |
599 | ok = FALSE; | |
600 | goto cleanup; | |
601 | } | |
602 | ||
603 | scRet = sspi_verify_certificate(serverCert, hostname, conn->certFlags); | |
604 | ||
605 | if (scRet != SEC_E_OK) | |
606 | { | |
607 | DEBUG_printf(("_sspiConnect: sspi_verify_certificate failed: %x", scRet)); | |
608 | ok = FALSE; | |
609 | goto cleanup; | |
610 | } | |
321d8d57 | 611 | |
cc754834 MS |
612 | /* |
613 | * Find out how big the header/trailer will be: | |
614 | */ | |
615 | scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes); | |
616 | ||
617 | if (scRet != SEC_E_OK) | |
618 | { | |
619 | DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet)); | |
620 | ok = FALSE; | |
621 | } | |
622 | } | |
623 | ||
624 | cleanup: | |
625 | ||
626 | if (serverCert) | |
627 | CertFreeCertificateContext(serverCert); | |
628 | ||
629 | return (ok); | |
630 | } | |
631 | ||
632 | ||
633 | /* | |
634 | * '_sspiAccept()' - Accept an SSL/TLS connection | |
635 | */ | |
636 | BOOL /* O - 1 on success, 0 on failure */ | |
637 | _sspiAccept(_sspi_struct_t *conn) /* I - Client connection */ | |
638 | { | |
639 | DWORD dwSSPIFlags; /* SSL connection attributes we want */ | |
640 | DWORD dwSSPIOutFlags; /* SSL connection attributes we got */ | |
641 | TimeStamp tsExpiry; /* Time stamp */ | |
642 | SECURITY_STATUS scRet; /* SSPI Status */ | |
643 | SecBufferDesc inBuffer; /* Array of SecBuffer structs */ | |
644 | SecBuffer inBuffers[2]; /* Security package buffer */ | |
645 | SecBufferDesc outBuffer; /* Array of SecBuffer structs */ | |
646 | SecBuffer outBuffers[1]; /* Security package buffer */ | |
647 | DWORD num = 0; /* 32 bit status value */ | |
648 | BOOL fInitContext = TRUE; | |
649 | /* Has the context been init'd? */ | |
650 | BOOL ok = TRUE; /* Return value */ | |
651 | ||
652 | if (!conn) | |
653 | return (FALSE); | |
654 | ||
655 | dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT | | |
656 | ASC_REQ_REPLAY_DETECT | | |
657 | ASC_REQ_CONFIDENTIALITY | | |
658 | ASC_REQ_EXTENDED_ERROR | | |
659 | ASC_REQ_ALLOCATE_MEMORY | | |
660 | ASC_REQ_STREAM; | |
661 | ||
662 | conn->decryptBufferUsed = 0; | |
321d8d57 | 663 | |
cc754834 MS |
664 | /* |
665 | * Set OutBuffer for AcceptSecurityContext call | |
666 | */ | |
667 | outBuffer.cBuffers = 1; | |
668 | outBuffer.pBuffers = outBuffers; | |
669 | outBuffer.ulVersion = SECBUFFER_VERSION; | |
670 | ||
671 | scRet = SEC_I_CONTINUE_NEEDED; | |
672 | ||
673 | while (scRet == SEC_I_CONTINUE_NEEDED || | |
674 | scRet == SEC_E_INCOMPLETE_MESSAGE || | |
321d8d57 | 675 | scRet == SEC_I_INCOMPLETE_CREDENTIALS) |
cc754834 MS |
676 | { |
677 | if ((conn->decryptBufferUsed == 0) || (scRet == SEC_E_INCOMPLETE_MESSAGE)) | |
678 | { | |
321d8d57 | 679 | if (conn->decryptBufferLength <= conn->decryptBufferUsed) |
cc754834 MS |
680 | { |
681 | conn->decryptBufferLength += 4096; | |
682 | conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer, | |
683 | conn->decryptBufferLength); | |
684 | ||
685 | if (!conn->decryptBuffer) | |
686 | { | |
687 | DEBUG_printf(("_sspiAccept: unable to allocate %d byte decrypt buffer", | |
688 | conn->decryptBufferLength)); | |
689 | ok = FALSE; | |
690 | goto cleanup; | |
691 | } | |
692 | } | |
693 | ||
694 | for (;;) | |
695 | { | |
696 | num = recv(conn->sock, | |
697 | conn->decryptBuffer + conn->decryptBufferUsed, | |
698 | (int)(conn->decryptBufferLength - conn->decryptBufferUsed), | |
699 | 0); | |
700 | ||
701 | if ((num == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK)) | |
702 | Sleep(1); | |
703 | else | |
704 | break; | |
705 | } | |
706 | ||
707 | if (num == SOCKET_ERROR) | |
708 | { | |
709 | DEBUG_printf(("_sspiAccept: recv failed: %d", WSAGetLastError())); | |
710 | ok = FALSE; | |
711 | goto cleanup; | |
712 | } | |
713 | else if (num == 0) | |
714 | { | |
715 | DEBUG_printf(("_sspiAccept: client disconnected")); | |
716 | ok = FALSE; | |
717 | goto cleanup; | |
718 | } | |
719 | ||
720 | DEBUG_printf(("_sspiAccept: received %d (handshake) bytes from client", | |
721 | num)); | |
722 | conn->decryptBufferUsed += num; | |
723 | } | |
724 | ||
725 | /* | |
726 | * InBuffers[1] is for getting extra data that | |
727 | * SSPI/SCHANNEL doesn't proccess on this | |
728 | * run around the loop. | |
729 | */ | |
730 | inBuffers[0].pvBuffer = conn->decryptBuffer; | |
731 | inBuffers[0].cbBuffer = (unsigned long) conn->decryptBufferUsed; | |
732 | inBuffers[0].BufferType = SECBUFFER_TOKEN; | |
733 | ||
734 | inBuffers[1].pvBuffer = NULL; | |
735 | inBuffers[1].cbBuffer = 0; | |
736 | inBuffers[1].BufferType = SECBUFFER_EMPTY; | |
737 | ||
738 | inBuffer.cBuffers = 2; | |
739 | inBuffer.pBuffers = inBuffers; | |
740 | inBuffer.ulVersion = SECBUFFER_VERSION; | |
741 | ||
742 | /* | |
743 | * Initialize these so if we fail, pvBuffer contains NULL, | |
744 | * so we don't try to free random garbage at the quit | |
745 | */ | |
746 | outBuffers[0].pvBuffer = NULL; | |
747 | outBuffers[0].BufferType = SECBUFFER_TOKEN; | |
748 | outBuffers[0].cbBuffer = 0; | |
749 | ||
750 | scRet = AcceptSecurityContext(&conn->creds, (fInitContext?NULL:&conn->context), | |
751 | &inBuffer, dwSSPIFlags, SECURITY_NATIVE_DREP, | |
752 | (fInitContext?&conn->context:NULL), &outBuffer, | |
753 | &dwSSPIOutFlags, &tsExpiry); | |
754 | ||
755 | fInitContext = FALSE; | |
756 | ||
757 | if (scRet == SEC_E_OK || | |
758 | scRet == SEC_I_CONTINUE_NEEDED || | |
759 | (FAILED(scRet) && ((dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) != 0))) | |
760 | { | |
761 | if (outBuffers[0].cbBuffer && outBuffers[0].pvBuffer) | |
762 | { | |
763 | /* | |
764 | * Send response to server if there is one | |
765 | */ | |
766 | num = send(conn->sock, outBuffers[0].pvBuffer, outBuffers[0].cbBuffer, 0); | |
767 | ||
768 | if ((num == SOCKET_ERROR) || (num == 0)) | |
769 | { | |
770 | DEBUG_printf(("_sspiAccept: handshake send failed: %d", WSAGetLastError())); | |
771 | ok = FALSE; | |
772 | goto cleanup; | |
773 | } | |
774 | ||
775 | DEBUG_printf(("_sspiAccept: send %d handshake bytes to client", | |
776 | outBuffers[0].cbBuffer)); | |
777 | ||
778 | FreeContextBuffer(outBuffers[0].pvBuffer); | |
779 | outBuffers[0].pvBuffer = NULL; | |
780 | } | |
781 | } | |
782 | ||
783 | if (scRet == SEC_E_OK) | |
784 | { | |
785 | /* | |
321d8d57 | 786 | * If there's extra data then save it for |
cc754834 MS |
787 | * next time we go to decrypt |
788 | */ | |
789 | if (inBuffers[1].BufferType == SECBUFFER_EXTRA) | |
790 | { | |
791 | memcpy(conn->decryptBuffer, | |
792 | (LPBYTE) (conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer)), | |
793 | inBuffers[1].cbBuffer); | |
794 | conn->decryptBufferUsed = inBuffers[1].cbBuffer; | |
795 | } | |
796 | else | |
797 | { | |
798 | conn->decryptBufferUsed = 0; | |
799 | } | |
800 | ||
801 | ok = TRUE; | |
802 | break; | |
803 | } | |
804 | else if (FAILED(scRet) && (scRet != SEC_E_INCOMPLETE_MESSAGE)) | |
805 | { | |
806 | DEBUG_printf(("_sspiAccept: AcceptSecurityContext failed: %x", scRet)); | |
807 | ok = FALSE; | |
808 | break; | |
809 | } | |
810 | ||
811 | if (scRet != SEC_E_INCOMPLETE_MESSAGE && | |
812 | scRet != SEC_I_INCOMPLETE_CREDENTIALS) | |
813 | { | |
814 | if (inBuffers[1].BufferType == SECBUFFER_EXTRA) | |
815 | { | |
816 | memcpy(conn->decryptBuffer, | |
817 | (LPBYTE) (conn->decryptBuffer + (conn->decryptBufferUsed - inBuffers[1].cbBuffer)), | |
818 | inBuffers[1].cbBuffer); | |
819 | conn->decryptBufferUsed = inBuffers[1].cbBuffer; | |
820 | } | |
821 | else | |
822 | { | |
823 | conn->decryptBufferUsed = 0; | |
824 | } | |
825 | } | |
826 | } | |
827 | ||
828 | if (ok) | |
829 | { | |
830 | conn->contextInitialized = TRUE; | |
831 | ||
832 | /* | |
833 | * Find out how big the header will be: | |
834 | */ | |
835 | scRet = QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->streamSizes); | |
836 | ||
837 | if (scRet != SEC_E_OK) | |
838 | { | |
839 | DEBUG_printf(("_sspiAccept: QueryContextAttributes failed: %x", scRet)); | |
840 | ok = FALSE; | |
841 | } | |
842 | } | |
843 | ||
844 | cleanup: | |
845 | ||
846 | return (ok); | |
847 | } | |
848 | ||
849 | ||
850 | /* | |
851 | * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs | |
852 | */ | |
853 | void | |
854 | _sspiSetAllowsAnyRoot(_sspi_struct_t *conn, | |
855 | /* I - Client connection */ | |
856 | BOOL allow) | |
857 | /* I - Allow any root */ | |
858 | { | |
859 | conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_UNKNOWN_CA : | |
860 | conn->certFlags & ~SECURITY_FLAG_IGNORE_UNKNOWN_CA; | |
861 | } | |
862 | ||
863 | ||
864 | /* | |
865 | * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs | |
866 | */ | |
867 | void | |
868 | _sspiSetAllowsExpiredCerts(_sspi_struct_t *conn, | |
869 | /* I - Client connection */ | |
870 | BOOL allow) | |
871 | /* I - Allow expired certs */ | |
872 | { | |
873 | conn->certFlags = (allow) ? conn->certFlags | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID : | |
874 | conn->certFlags & ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID; | |
875 | } | |
876 | ||
877 | ||
878 | /* | |
879 | * '_sspiWrite()' - Write a buffer to an ssl socket | |
880 | */ | |
881 | int /* O - Bytes written or SOCKET_ERROR */ | |
882 | _sspiWrite(_sspi_struct_t *conn, /* I - Client connection */ | |
883 | void *buf, /* I - Buffer */ | |
884 | size_t len) /* I - Buffer length */ | |
885 | { | |
886 | SecBufferDesc message; /* Array of SecBuffer struct */ | |
887 | SecBuffer buffers[4] = { 0 }; /* Security package buffer */ | |
888 | BYTE *buffer = NULL; /* Scratch buffer */ | |
889 | int bufferLen; /* Buffer length */ | |
890 | size_t bytesLeft; /* Bytes left to write */ | |
891 | int index = 0; /* Index into buffer */ | |
892 | int num = 0; /* Return value */ | |
893 | ||
894 | if (!conn || !buf || !len) | |
895 | { | |
896 | WSASetLastError(WSAEINVAL); | |
897 | num = SOCKET_ERROR; | |
898 | goto cleanup; | |
899 | } | |
900 | ||
901 | bufferLen = conn->streamSizes.cbMaximumMessage + | |
902 | conn->streamSizes.cbHeader + | |
903 | conn->streamSizes.cbTrailer; | |
904 | ||
905 | buffer = (BYTE*) malloc(bufferLen); | |
906 | ||
907 | if (!buffer) | |
908 | { | |
909 | DEBUG_printf(("_sspiWrite: buffer alloc of %d bytes failed", bufferLen)); | |
910 | WSASetLastError(E_OUTOFMEMORY); | |
911 | num = SOCKET_ERROR; | |
912 | goto cleanup; | |
913 | } | |
914 | ||
915 | bytesLeft = len; | |
916 | ||
917 | while (bytesLeft) | |
918 | { | |
919 | size_t chunk = min(conn->streamSizes.cbMaximumMessage, /* Size of data to write */ | |
920 | bytesLeft); | |
921 | SECURITY_STATUS scRet; /* SSPI status */ | |
321d8d57 | 922 | |
cc754834 MS |
923 | /* |
924 | * Copy user data into the buffer, starting | |
925 | * just past the header | |
926 | */ | |
927 | memcpy(buffer + conn->streamSizes.cbHeader, | |
928 | ((BYTE*) buf) + index, | |
929 | chunk); | |
930 | ||
931 | /* | |
932 | * Setup the SSPI buffers | |
933 | */ | |
934 | message.ulVersion = SECBUFFER_VERSION; | |
935 | message.cBuffers = 4; | |
936 | message.pBuffers = buffers; | |
937 | buffers[0].pvBuffer = buffer; | |
938 | buffers[0].cbBuffer = conn->streamSizes.cbHeader; | |
939 | buffers[0].BufferType = SECBUFFER_STREAM_HEADER; | |
940 | buffers[1].pvBuffer = buffer + conn->streamSizes.cbHeader; | |
941 | buffers[1].cbBuffer = (unsigned long) chunk; | |
942 | buffers[1].BufferType = SECBUFFER_DATA; | |
943 | buffers[2].pvBuffer = buffer + conn->streamSizes.cbHeader + chunk; | |
944 | buffers[2].cbBuffer = conn->streamSizes.cbTrailer; | |
945 | buffers[2].BufferType = SECBUFFER_STREAM_TRAILER; | |
946 | buffers[3].BufferType = SECBUFFER_EMPTY; | |
947 | ||
948 | /* | |
949 | * Encrypt the data | |
950 | */ | |
951 | scRet = EncryptMessage(&conn->context, 0, &message, 0); | |
952 | ||
953 | if (FAILED(scRet)) | |
954 | { | |
955 | DEBUG_printf(("_sspiWrite: EncryptMessage failed: %x", scRet)); | |
956 | WSASetLastError(WSASYSCALLFAILURE); | |
957 | num = SOCKET_ERROR; | |
958 | goto cleanup; | |
959 | } | |
960 | ||
961 | /* | |
321d8d57 | 962 | * Send the data. Remember the size of |
cc754834 MS |
963 | * the total data to send is the size |
964 | * of the header, the size of the data | |
965 | * the caller passed in and the size | |
966 | * of the trailer | |
967 | */ | |
968 | num = send(conn->sock, | |
969 | buffer, | |
970 | buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer, | |
971 | 0); | |
972 | ||
973 | if ((num == SOCKET_ERROR) || (num == 0)) | |
974 | { | |
975 | DEBUG_printf(("_sspiWrite: send failed: %ld", WSAGetLastError())); | |
976 | goto cleanup; | |
977 | } | |
321d8d57 | 978 | |
cc754834 MS |
979 | bytesLeft -= (int) chunk; |
980 | index += (int) chunk; | |
981 | } | |
982 | ||
983 | num = (int) len; | |
984 | ||
985 | cleanup: | |
986 | ||
987 | if (buffer) | |
988 | free(buffer); | |
989 | ||
990 | return (num); | |
991 | } | |
992 | ||
993 | ||
994 | /* | |
995 | * '_sspiRead()' - Read a buffer from an ssl socket | |
996 | */ | |
997 | int /* O - Bytes read or SOCKET_ERROR */ | |
998 | _sspiRead(_sspi_struct_t *conn, /* I - Client connection */ | |
999 | void *buf, /* I - Buffer */ | |
1000 | size_t len) /* I - Buffer length */ | |
1001 | { | |
1002 | SecBufferDesc message; /* Array of SecBuffer struct */ | |
1003 | SecBuffer buffers[4] = { 0 }; /* Security package buffer */ | |
1004 | int num = 0; /* Return value */ | |
1005 | ||
1006 | if (!conn) | |
1007 | { | |
1008 | WSASetLastError(WSAEINVAL); | |
1009 | num = SOCKET_ERROR; | |
1010 | goto cleanup; | |
1011 | } | |
1012 | ||
1013 | /* | |
1014 | * If there are bytes that have already been | |
1015 | * decrypted and have not yet been read, return | |
1016 | * those | |
1017 | */ | |
1018 | if (buf && (conn->readBufferUsed > 0)) | |
1019 | { | |
1020 | int bytesToCopy = (int) min(conn->readBufferUsed, len); /* Amount of bytes to copy */ | |
1021 | /* from read buffer */ | |
1022 | ||
1023 | memcpy(buf, conn->readBuffer, bytesToCopy); | |
1024 | conn->readBufferUsed -= bytesToCopy; | |
1025 | ||
1026 | if (conn->readBufferUsed > 0) | |
1027 | /* | |
1028 | * If the caller didn't request all the bytes | |
1029 | * we have in the buffer, then move the unread | |
1030 | * bytes down | |
1031 | */ | |
1032 | memmove(conn->readBuffer, | |
1033 | conn->readBuffer + bytesToCopy, | |
1034 | conn->readBufferUsed); | |
1035 | ||
1036 | num = bytesToCopy; | |
1037 | } | |
1038 | else | |
1039 | { | |
1040 | PSecBuffer pDataBuffer; /* Data buffer */ | |
1041 | PSecBuffer pExtraBuffer; /* Excess data buffer */ | |
1042 | SECURITY_STATUS scRet; /* SSPI status */ | |
1043 | int i; /* Loop control variable */ | |
1044 | ||
1045 | /* | |
1046 | * Initialize security buffer structs | |
1047 | */ | |
1048 | message.ulVersion = SECBUFFER_VERSION; | |
1049 | message.cBuffers = 4; | |
1050 | message.pBuffers = buffers; | |
1051 | ||
1052 | do | |
1053 | { | |
1054 | /* | |
321d8d57 | 1055 | * If there is not enough space in the |
cc754834 MS |
1056 | * buffer, then increase it's size |
1057 | */ | |
321d8d57 | 1058 | if (conn->decryptBufferLength <= conn->decryptBufferUsed) |
cc754834 MS |
1059 | { |
1060 | conn->decryptBufferLength += 4096; | |
321d8d57 | 1061 | conn->decryptBuffer = (BYTE*) realloc(conn->decryptBuffer, |
cc754834 MS |
1062 | conn->decryptBufferLength); |
1063 | ||
1064 | if (!conn->decryptBuffer) | |
1065 | { | |
1066 | DEBUG_printf(("_sspiRead: unable to allocate %d byte buffer", | |
1067 | conn->decryptBufferLength)); | |
1068 | WSASetLastError(E_OUTOFMEMORY); | |
1069 | num = SOCKET_ERROR; | |
1070 | goto cleanup; | |
1071 | } | |
1072 | } | |
1073 | ||
1074 | buffers[0].pvBuffer = conn->decryptBuffer; | |
1075 | buffers[0].cbBuffer = (unsigned long) conn->decryptBufferUsed; | |
1076 | buffers[0].BufferType = SECBUFFER_DATA; | |
1077 | buffers[1].BufferType = SECBUFFER_EMPTY; | |
1078 | buffers[2].BufferType = SECBUFFER_EMPTY; | |
1079 | buffers[3].BufferType = SECBUFFER_EMPTY; | |
1080 | ||
1081 | scRet = DecryptMessage(&conn->context, &message, 0, NULL); | |
1082 | ||
1083 | if (scRet == SEC_E_INCOMPLETE_MESSAGE) | |
1084 | { | |
1085 | if (buf) | |
1086 | { | |
1087 | num = recv(conn->sock, | |
1088 | conn->decryptBuffer + conn->decryptBufferUsed, | |
1089 | (int)(conn->decryptBufferLength - conn->decryptBufferUsed), | |
1090 | 0); | |
1091 | if (num == SOCKET_ERROR) | |
1092 | { | |
1093 | DEBUG_printf(("_sspiRead: recv failed: %d", WSAGetLastError())); | |
1094 | goto cleanup; | |
1095 | } | |
1096 | else if (num == 0) | |
1097 | { | |
1098 | DEBUG_printf(("_sspiRead: server disconnected")); | |
1099 | goto cleanup; | |
1100 | } | |
1101 | ||
1102 | conn->decryptBufferUsed += num; | |
1103 | } | |
1104 | else | |
1105 | { | |
1106 | num = (int) conn->readBufferUsed; | |
1107 | goto cleanup; | |
1108 | } | |
1109 | } | |
1110 | } | |
1111 | while (scRet == SEC_E_INCOMPLETE_MESSAGE); | |
1112 | ||
1113 | if (scRet == SEC_I_CONTEXT_EXPIRED) | |
1114 | { | |
1115 | DEBUG_printf(("_sspiRead: context expired")); | |
1116 | WSASetLastError(WSAECONNRESET); | |
1117 | num = SOCKET_ERROR; | |
1118 | goto cleanup; | |
1119 | } | |
1120 | else if (scRet != SEC_E_OK) | |
1121 | { | |
1122 | DEBUG_printf(("_sspiRead: DecryptMessage failed: %lx", scRet)); | |
1123 | WSASetLastError(WSASYSCALLFAILURE); | |
1124 | num = SOCKET_ERROR; | |
1125 | goto cleanup; | |
1126 | } | |
1127 | ||
1128 | /* | |
1129 | * The decryption worked. Now, locate data buffer. | |
1130 | */ | |
1131 | pDataBuffer = NULL; | |
1132 | pExtraBuffer = NULL; | |
1133 | for (i = 1; i < 4; i++) | |
1134 | { | |
1135 | if (buffers[i].BufferType == SECBUFFER_DATA) | |
1136 | pDataBuffer = &buffers[i]; | |
1137 | else if (!pExtraBuffer && (buffers[i].BufferType == SECBUFFER_EXTRA)) | |
1138 | pExtraBuffer = &buffers[i]; | |
1139 | } | |
1140 | ||
1141 | /* | |
1142 | * If a data buffer is found, then copy | |
1143 | * the decrypted bytes to the passed-in | |
1144 | * buffer | |
1145 | */ | |
1146 | if (pDataBuffer) | |
1147 | { | |
1148 | int bytesToCopy = min(pDataBuffer->cbBuffer, (int) len); | |
1149 | /* Number of bytes to copy into buf */ | |
1150 | int bytesToSave = pDataBuffer->cbBuffer - bytesToCopy; | |
1151 | /* Number of bytes to save in our read buffer */ | |
1152 | ||
1153 | if (bytesToCopy) | |
1154 | memcpy(buf, pDataBuffer->pvBuffer, bytesToCopy); | |
1155 | ||
1156 | /* | |
1157 | * If there are more decrypted bytes than can be | |
1158 | * copied to the passed in buffer, then save them | |
1159 | */ | |
1160 | if (bytesToSave) | |
1161 | { | |
1162 | if ((int)(conn->readBufferLength - conn->readBufferUsed) < bytesToSave) | |
1163 | { | |
1164 | conn->readBufferLength = conn->readBufferUsed + bytesToSave; | |
1165 | conn->readBuffer = realloc(conn->readBuffer, | |
1166 | conn->readBufferLength); | |
1167 | ||
1168 | if (!conn->readBuffer) | |
1169 | { | |
1170 | DEBUG_printf(("_sspiRead: unable to allocate %d bytes", conn->readBufferLength)); | |
1171 | WSASetLastError(E_OUTOFMEMORY); | |
1172 | num = SOCKET_ERROR; | |
1173 | goto cleanup; | |
1174 | } | |
1175 | } | |
1176 | ||
1177 | memcpy(((BYTE*) conn->readBuffer) + conn->readBufferUsed, | |
1178 | ((BYTE*) pDataBuffer->pvBuffer) + bytesToCopy, | |
1179 | bytesToSave); | |
1180 | ||
1181 | conn->readBufferUsed += bytesToSave; | |
1182 | } | |
1183 | ||
1184 | num = (buf) ? bytesToCopy : (int) conn->readBufferUsed; | |
1185 | } | |
1186 | else | |
1187 | { | |
1188 | DEBUG_printf(("_sspiRead: unable to find data buffer")); | |
1189 | WSASetLastError(WSASYSCALLFAILURE); | |
1190 | num = SOCKET_ERROR; | |
1191 | goto cleanup; | |
1192 | } | |
1193 | ||
1194 | /* | |
1195 | * If the decryption process left extra bytes, | |
1196 | * then save those back in decryptBuffer. They will | |
1197 | * be processed the next time through the loop. | |
1198 | */ | |
321d8d57 | 1199 | if (pExtraBuffer) |
cc754834 MS |
1200 | { |
1201 | memmove(conn->decryptBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer); | |
1202 | conn->decryptBufferUsed = pExtraBuffer->cbBuffer; | |
1203 | } | |
1204 | else | |
1205 | { | |
1206 | conn->decryptBufferUsed = 0; | |
1207 | } | |
1208 | } | |
1209 | ||
1210 | cleanup: | |
1211 | ||
1212 | return (num); | |
1213 | } | |
1214 | ||
1215 | ||
1216 | /* | |
1217 | * '_sspiPending()' - Returns the number of available bytes | |
1218 | */ | |
1219 | int /* O - Number of available bytes */ | |
1220 | _sspiPending(_sspi_struct_t *conn) /* I - Client connection */ | |
1221 | { | |
1222 | return (_sspiRead(conn, NULL, 0)); | |
1223 | } | |
1224 | ||
1225 | ||
1226 | /* | |
1227 | * '_sspiFree()' - Close a connection and free resources | |
1228 | */ | |
1229 | void | |
1230 | _sspiFree(_sspi_struct_t *conn) /* I - Client connection */ | |
1231 | { | |
1232 | if (!conn) | |
1233 | return; | |
1234 | ||
1235 | if (conn->contextInitialized) | |
1236 | { | |
1237 | SecBufferDesc message; /* Array of SecBuffer struct */ | |
1238 | SecBuffer buffers[1] = { 0 }; | |
1239 | /* Security package buffer */ | |
1240 | DWORD dwType; /* Type */ | |
1241 | DWORD status; /* Status */ | |
1242 | ||
321d8d57 | 1243 | /* |
cc754834 MS |
1244 | * Notify schannel that we are about to close the connection. |
1245 | */ | |
1246 | dwType = SCHANNEL_SHUTDOWN; | |
1247 | ||
1248 | buffers[0].pvBuffer = &dwType; | |
1249 | buffers[0].BufferType = SECBUFFER_TOKEN; | |
1250 | buffers[0].cbBuffer = sizeof(dwType); | |
1251 | ||
1252 | message.cBuffers = 1; | |
1253 | message.pBuffers = buffers; | |
1254 | message.ulVersion = SECBUFFER_VERSION; | |
1255 | ||
1256 | status = ApplyControlToken(&conn->context, &message); | |
1257 | ||
321d8d57 | 1258 | if (SUCCEEDED(status)) |
cc754834 MS |
1259 | { |
1260 | PBYTE pbMessage; /* Message buffer */ | |
1261 | DWORD cbMessage; /* Message buffer count */ | |
1262 | DWORD cbData; /* Data count */ | |
1263 | DWORD dwSSPIFlags; /* SSL attributes we requested */ | |
1264 | DWORD dwSSPIOutFlags; /* SSL attributes we received */ | |
1265 | TimeStamp tsExpiry; /* Time stamp */ | |
1266 | ||
1267 | dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT | | |
1268 | ASC_REQ_REPLAY_DETECT | | |
1269 | ASC_REQ_CONFIDENTIALITY | | |
1270 | ASC_REQ_EXTENDED_ERROR | | |
1271 | ASC_REQ_ALLOCATE_MEMORY | | |
1272 | ASC_REQ_STREAM; | |
1273 | ||
1274 | buffers[0].pvBuffer = NULL; | |
1275 | buffers[0].BufferType = SECBUFFER_TOKEN; | |
1276 | buffers[0].cbBuffer = 0; | |
1277 | ||
1278 | message.cBuffers = 1; | |
1279 | message.pBuffers = buffers; | |
1280 | message.ulVersion = SECBUFFER_VERSION; | |
1281 | ||
1282 | status = AcceptSecurityContext(&conn->creds, &conn->context, NULL, | |
1283 | dwSSPIFlags, SECURITY_NATIVE_DREP, NULL, | |
1284 | &message, &dwSSPIOutFlags, &tsExpiry); | |
1285 | ||
321d8d57 | 1286 | if (SUCCEEDED(status)) |
cc754834 MS |
1287 | { |
1288 | pbMessage = buffers[0].pvBuffer; | |
1289 | cbMessage = buffers[0].cbBuffer; | |
1290 | ||
1291 | /* | |
1292 | * Send the close notify message to the client. | |
1293 | */ | |
1294 | if (pbMessage && cbMessage) | |
1295 | { | |
1296 | cbData = send(conn->sock, pbMessage, cbMessage, 0); | |
1297 | if ((cbData == SOCKET_ERROR) || (cbData == 0)) | |
1298 | { | |
1299 | status = WSAGetLastError(); | |
1300 | DEBUG_printf(("_sspiFree: sending close notify failed: %d", status)); | |
1301 | } | |
1302 | else | |
1303 | { | |
1304 | FreeContextBuffer(pbMessage); | |
1305 | } | |
1306 | } | |
1307 | } | |
1308 | else | |
1309 | { | |
1310 | DEBUG_printf(("_sspiFree: AcceptSecurityContext failed: %x", status)); | |
1311 | } | |
1312 | } | |
1313 | else | |
1314 | { | |
1315 | DEBUG_printf(("_sspiFree: ApplyControlToken failed: %x", status)); | |
1316 | } | |
1317 | ||
1318 | DeleteSecurityContext(&conn->context); | |
1319 | conn->contextInitialized = FALSE; | |
1320 | } | |
1321 | ||
1322 | if (conn->decryptBuffer) | |
1323 | { | |
1324 | free(conn->decryptBuffer); | |
1325 | conn->decryptBuffer = NULL; | |
1326 | } | |
1327 | ||
1328 | if (conn->readBuffer) | |
1329 | { | |
1330 | free(conn->readBuffer); | |
1331 | conn->readBuffer = NULL; | |
1332 | } | |
1333 | ||
1334 | if (conn->sock != INVALID_SOCKET) | |
1335 | { | |
1336 | closesocket(conn->sock); | |
1337 | conn->sock = INVALID_SOCKET; | |
1338 | } | |
1339 | ||
1340 | free(conn); | |
1341 | } | |
1342 | ||
1343 | ||
1344 | /* | |
1345 | * 'sspi_verify_certificate()' - Verify a server certificate | |
1346 | */ | |
1347 | static DWORD /* 0 - Error code (0 == No error) */ | |
1348 | sspi_verify_certificate(PCCERT_CONTEXT serverCert, | |
1349 | /* I - Server certificate */ | |
1350 | const CHAR *serverName, | |
1351 | /* I - Server name */ | |
1352 | DWORD dwCertFlags) | |
1353 | /* I - Verification flags */ | |
1354 | { | |
1355 | HTTPSPolicyCallbackData httpsPolicy; | |
1356 | /* HTTPS Policy Struct */ | |
1357 | CERT_CHAIN_POLICY_PARA policyPara; | |
1358 | /* Cert chain policy parameters */ | |
1359 | CERT_CHAIN_POLICY_STATUS policyStatus; | |
1360 | /* Cert chain policy status */ | |
1361 | CERT_CHAIN_PARA chainPara; | |
1362 | /* Used for searching and matching criteria */ | |
1363 | PCCERT_CHAIN_CONTEXT chainContext = NULL; | |
1364 | /* Certificate chain */ | |
1365 | PWSTR serverNameUnicode = NULL; | |
1366 | /* Unicode server name */ | |
1367 | LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH, | |
1368 | szOID_SERVER_GATED_CRYPTO, | |
1369 | szOID_SGC_NETSCAPE }; | |
1370 | /* How are we using this certificate? */ | |
1371 | DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR); | |
1372 | /* Number of ites in rgszUsages */ | |
1373 | DWORD count; /* 32 bit count variable */ | |
1374 | DWORD status; /* Return value */ | |
1375 | ||
1376 | if (!serverCert) | |
1377 | { | |
1378 | status = SEC_E_WRONG_PRINCIPAL; | |
1379 | goto cleanup; | |
1380 | } | |
1381 | ||
1382 | /* | |
1383 | * Convert server name to unicode. | |
1384 | */ | |
1385 | if (!serverName || (strlen(serverName) == 0)) | |
1386 | { | |
1387 | status = SEC_E_WRONG_PRINCIPAL; | |
1388 | goto cleanup; | |
1389 | } | |
1390 | ||
1391 | count = MultiByteToWideChar(CP_ACP, 0, serverName, -1, NULL, 0); | |
1392 | serverNameUnicode = LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR)); | |
1393 | if (!serverNameUnicode) | |
1394 | { | |
1395 | status = SEC_E_INSUFFICIENT_MEMORY; | |
1396 | goto cleanup; | |
1397 | } | |
1398 | count = MultiByteToWideChar(CP_ACP, 0, serverName, -1, serverNameUnicode, count); | |
1399 | if (count == 0) | |
1400 | { | |
1401 | status = SEC_E_WRONG_PRINCIPAL; | |
1402 | goto cleanup; | |
1403 | } | |
1404 | ||
1405 | /* | |
1406 | * Build certificate chain. | |
1407 | */ | |
1408 | ZeroMemory(&chainPara, sizeof(chainPara)); | |
1409 | chainPara.cbSize = sizeof(chainPara); | |
1410 | chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; | |
1411 | chainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages; | |
1412 | chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; | |
1413 | ||
1414 | if (!CertGetCertificateChain(NULL, serverCert, NULL, serverCert->hCertStore, | |
1415 | &chainPara, 0, NULL, &chainContext)) | |
1416 | { | |
1417 | status = GetLastError(); | |
1418 | DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status)); | |
1419 | goto cleanup; | |
1420 | } | |
1421 | ||
1422 | /* | |
1423 | * Validate certificate chain. | |
321d8d57 | 1424 | */ |
cc754834 MS |
1425 | ZeroMemory(&httpsPolicy, sizeof(HTTPSPolicyCallbackData)); |
1426 | httpsPolicy.cbStruct = sizeof(HTTPSPolicyCallbackData); | |
1427 | httpsPolicy.dwAuthType = AUTHTYPE_SERVER; | |
1428 | httpsPolicy.fdwChecks = dwCertFlags; | |
1429 | httpsPolicy.pwszServerName = serverNameUnicode; | |
1430 | ||
1431 | memset(&policyPara, 0, sizeof(policyPara)); | |
1432 | policyPara.cbSize = sizeof(policyPara); | |
1433 | policyPara.pvExtraPolicyPara = &httpsPolicy; | |
1434 | ||
1435 | memset(&policyStatus, 0, sizeof(policyStatus)); | |
1436 | policyStatus.cbSize = sizeof(policyStatus); | |
1437 | ||
1438 | if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext, | |
1439 | &policyPara, &policyStatus)) | |
1440 | { | |
1441 | status = GetLastError(); | |
1442 | DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status)); | |
1443 | goto cleanup; | |
1444 | } | |
1445 | ||
1446 | if (policyStatus.dwError) | |
1447 | { | |
1448 | status = policyStatus.dwError; | |
1449 | goto cleanup; | |
1450 | } | |
1451 | ||
1452 | status = SEC_E_OK; | |
1453 | ||
1454 | cleanup: | |
1455 | ||
1456 | if (chainContext) | |
1457 | CertFreeCertificateChain(chainContext); | |
1458 | ||
1459 | if (serverNameUnicode) | |
1460 | LocalFree(serverNameUnicode); | |
1461 | ||
1462 | return (status); | |
1463 | } | |
321d8d57 MS |
1464 | |
1465 | ||
1466 | /* | |
71f63681 | 1467 | * End of "$Id: sspi.c 11760 2014-03-28 12:58:24Z msweet $". |
321d8d57 | 1468 | */ |