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