]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/sspi.c
Merge changes from CUPS 1.5svn-r9136.
[thirdparty/cups.git] / cups / sspi.c
CommitLineData
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
49static 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 */
73BOOL /* 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
268cleanup:
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 */
300BOOL /* 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
633cleanup:
634
635 if (serverCert)
636 CertFreeCertificateContext(serverCert);
637
638 return (ok);
639}
640
641
642/*
643 * '_sspiAccept()' - Accept an SSL/TLS connection
644 */
645BOOL /* 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
853cleanup:
854
855 return (ok);
856}
857
858
859/*
860 * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
861 */
862void
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 */
876void
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 */
890int /* 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
994cleanup:
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 */
1006int /* 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
1219cleanup:
1220
1221 return (num);
1222}
1223
1224
1225/*
1226 * '_sspiPending()' - Returns the number of available bytes
1227 */
1228int /* 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 */
1238void
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 */
1356static DWORD /* 0 - Error code (0 == No error) */
1357sspi_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
1463cleanup:
1464
1465 if (chainContext)
1466 CertFreeCertificateChain(chainContext);
1467
1468 if (serverNameUnicode)
1469 LocalFree(serverNameUnicode);
1470
1471 return (status);
1472}