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