2 * Windows SSPI SSL implementation for the Common UNIX Printing System (CUPS).
4 * Copyright 2010 by Apple Inc.
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/".
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
28 * Include necessary headers...
31 #include "sspi-private.h"
32 #include "debug-private.h"
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")
41 #if !defined(SECURITY_FLAG_IGNORE_UNKNOWN_CA)
42 # define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100 /* Untrusted root */
45 #if !defined(SECURITY_FLAG_IGNORE_CERT_DATE_INVALID)
46 # define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */
49 static DWORD
sspi_verify_certificate(PCCERT_CONTEXT serverCert
,
50 const CHAR
*serverName
,
55 * 'sspi_alloc()' - Allocate SSPI ssl object
57 _sspi_struct_t
* /* O - New SSPI/SSL object */
60 _sspi_struct_t
*conn
= calloc(sizeof(_sspi_struct_t
), 1);
63 conn
->sock
= INVALID_SOCKET
;
70 * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store
71 * If one cannot be found, one is created.
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 */
80 /* I - Is caller a server? */
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
;
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 */
108 if (!CryptAcquireContextW(&hProv
, (LPWSTR
) container
, MS_DEF_PROV_W
,
110 CRYPT_NEWKEYSET
| CRYPT_MACHINE_KEYSET
))
112 if (GetLastError() == NTE_EXISTS
)
114 if (!CryptAcquireContextW(&hProv
, (LPWSTR
) container
, MS_DEF_PROV_W
,
115 PROV_RSA_FULL
, CRYPT_MACHINE_KEYSET
))
117 DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n",
125 store
= CertOpenStore(CERT_STORE_PROV_SYSTEM
,
126 X509_ASN_ENCODING
|PKCS_7_ASN_ENCODING
,
128 CERT_SYSTEM_STORE_LOCAL_MACHINE
|
129 CERT_STORE_NO_CRYPT_RELEASE_FLAG
|
130 CERT_STORE_OPEN_EXISTING_FLAG
,
135 DEBUG_printf(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n",
143 if (!CertStrToName(X509_ASN_ENCODING
, cn
, CERT_OID_NAME_STR
,
144 NULL
, NULL
, &dwSize
, NULL
))
146 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n",
152 p
= (PBYTE
) malloc(dwSize
);
156 DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize
));
161 if (!CertStrToName(X509_ASN_ENCODING
, cn
, CERT_OID_NAME_STR
, NULL
,
164 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x",
173 storedContext
= CertFindCertificateInStore(store
, X509_ASN_ENCODING
|PKCS_7_ASN_ENCODING
,
174 0, CERT_FIND_SUBJECT_NAME
, &sib
, NULL
);
179 * If we couldn't find the context, then we'll
182 if (!CryptGenKey(hProv
, AT_KEYEXCHANGE
, CRYPT_EXPORTABLE
, &hKey
))
184 DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x",
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
;
200 ZeroMemory(&exts
, sizeof(exts
));
202 createdContext
= CertCreateSelfSignCertificate(hProv
, &sib
, 0, &kpi
, NULL
, NULL
,
207 DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x",
213 if (!CertAddCertificateContextToStore(store
, createdContext
,
214 CERT_STORE_ADD_REPLACE_EXISTING
,
217 DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x",
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
;
230 if (!CertSetCertificateContextProperty(storedContext
,
231 CERT_KEY_PROV_INFO_PROP_ID
,
234 DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x",
241 ZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
243 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
244 SchannelCred
.cCreds
= 1;
245 SchannelCred
.paCred
= &storedContext
;
248 * SSPI doesn't seem to like it if grbitEnabledProtocols
249 * is set for a client
252 SchannelCred
.grbitEnabledProtocols
= SP_PROT_SSL3TLS1
;
255 * Create an SSPI credential.
257 Status
= AcquireCredentialsHandle(NULL
, UNISP_NAME
,
258 isServer
? SECPKG_CRED_INBOUND
:SECPKG_CRED_OUTBOUND
,
259 NULL
, &SchannelCred
, NULL
, NULL
, &conn
->creds
,
261 if (Status
!= SEC_E_OK
)
263 DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status
));
274 CryptDestroyKey(hKey
);
277 CertFreeCertificateContext(createdContext
);
280 CertFreeCertificateContext(storedContext
);
286 CertCloseStore(store
, 0);
289 CryptReleaseContext(hProv
, 0);
296 * '_sspiConnect()' - Make an SSL connection. This function
297 * assumes a TCP/IP connection has already
298 * been successfully made
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 */
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 */
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
|
326 * Initiate a ClientHello message and generate a token.
328 outBuffers
[0].pvBuffer
= NULL
;
329 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
330 outBuffers
[0].cbBuffer
= 0;
332 outBuffer
.cBuffers
= 1;
333 outBuffer
.pBuffers
= outBuffers
;
334 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
336 scRet
= InitializeSecurityContext(&conn
->creds
, NULL
, TEXT(""), dwSSPIFlags
,
337 0, SECURITY_NATIVE_DREP
, NULL
, 0, &conn
->context
,
338 &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
340 if (scRet
!= SEC_I_CONTINUE_NEEDED
)
342 DEBUG_printf(("_sspiConnect: InitializeSecurityContext(1) failed: %x", scRet
));
348 * Send response to server if there is one.
350 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
352 cbData
= send(conn
->sock
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
354 if ((cbData
== SOCKET_ERROR
) || !cbData
)
356 DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
357 FreeContextBuffer(outBuffers
[0].pvBuffer
);
358 DeleteSecurityContext(&conn
->context
);
363 DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData
));
366 * Free output buffer.
368 FreeContextBuffer(outBuffers
[0].pvBuffer
);
369 outBuffers
[0].pvBuffer
= NULL
;
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
|
380 conn
->decryptBufferUsed
= 0;
383 * Loop until the handshake is finished or an error occurs.
385 scRet
= SEC_I_CONTINUE_NEEDED
;
387 while(scRet
== SEC_I_CONTINUE_NEEDED
||
388 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
389 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
391 if ((conn
->decryptBufferUsed
== 0) || (scRet
== SEC_E_INCOMPLETE_MESSAGE
))
393 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
395 conn
->decryptBufferLength
+= 4096;
396 conn
->decryptBuffer
= (BYTE
*) realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
);
398 if (!conn
->decryptBuffer
)
400 DEBUG_printf(("_sspiConnect: unable to allocate %d byte decrypt buffer",
401 conn
->decryptBufferLength
));
402 SetLastError(E_OUTOFMEMORY
);
408 cbData
= recv(conn
->sock
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
,
409 (int) (conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
411 if (cbData
== SOCKET_ERROR
)
413 DEBUG_printf(("_sspiConnect: recv failed: %d", WSAGetLastError()));
417 else if (cbData
== 0)
419 DEBUG_printf(("_sspiConnect: server unexpectedly disconnected"));
424 DEBUG_printf(("_sspiConnect: %d bytes of handshake data received",
427 conn
->decryptBufferUsed
+= cbData
;
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.
436 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
437 inBuffers
[0].cbBuffer
= (unsigned long) conn
->decryptBufferUsed
;
438 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
440 inBuffers
[1].pvBuffer
= NULL
;
441 inBuffers
[1].cbBuffer
= 0;
442 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
444 inBuffer
.cBuffers
= 2;
445 inBuffer
.pBuffers
= inBuffers
;
446 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
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
453 outBuffers
[0].pvBuffer
= NULL
;
454 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
455 outBuffers
[0].cbBuffer
= 0;
457 outBuffer
.cBuffers
= 1;
458 outBuffer
.pBuffers
= outBuffers
;
459 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
462 * Call InitializeSecurityContext.
464 scRet
= InitializeSecurityContext(&conn
->creds
, &conn
->context
, NULL
, dwSSPIFlags
,
465 0, SECURITY_NATIVE_DREP
, &inBuffer
, 0, NULL
,
466 &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
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.
473 if (scRet
== SEC_E_OK
||
474 scRet
== SEC_I_CONTINUE_NEEDED
||
475 FAILED(scRet
) && (dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
))
477 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
479 cbData
= send(conn
->sock
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
481 if ((cbData
== SOCKET_ERROR
) || !cbData
)
483 DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
484 FreeContextBuffer(outBuffers
[0].pvBuffer
);
485 DeleteSecurityContext(&conn
->context
);
490 DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData
));
493 * Free output buffer.
495 FreeContextBuffer(outBuffers
[0].pvBuffer
);
496 outBuffers
[0].pvBuffer
= NULL
;
501 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
502 * then we need to read more data from the server and try again.
504 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
508 * If InitializeSecurityContext returned SEC_E_OK, then the
509 * handshake completed successfully.
511 if (scRet
== SEC_E_OK
)
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.
518 DEBUG_printf(("_sspiConnect: Handshake was successful"));
520 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
522 if (conn
->decryptBufferLength
< inBuffers
[1].cbBuffer
)
524 conn
->decryptBuffer
= realloc(conn
->decryptBuffer
, inBuffers
[1].cbBuffer
);
526 if (!conn
->decryptBuffer
)
528 DEBUG_printf(("_sspiConnect: unable to allocate %d bytes for decrypt buffer",
529 inBuffers
[1].cbBuffer
));
530 SetLastError(E_OUTOFMEMORY
);
536 memmove(conn
->decryptBuffer
,
537 conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
),
538 inBuffers
[1].cbBuffer
);
540 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
542 DEBUG_printf(("_sspiConnect: %d bytes of app data was bundled with handshake data",
543 conn
->decryptBufferUsed
));
546 conn
->decryptBufferUsed
= 0;
555 * Check for fatal error.
559 DEBUG_printf(("_sspiConnect: InitializeSecurityContext(2) failed: %x", scRet
));
565 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
566 * then the server just requested client authentication.
568 if (scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
573 DEBUG_printf(("_sspiConnect: server requested client credentials"));
579 * Copy any leftover data from the "extra" buffer, and go around
582 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
584 memmove(conn
->decryptBuffer
,
585 conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
),
586 inBuffers
[1].cbBuffer
);
588 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
592 conn
->decryptBufferUsed
= 0;
598 conn
->contextInitialized
= TRUE
;
601 * Get the server cert
603 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_REMOTE_CERT_CONTEXT
, (VOID
*) &serverCert
);
605 if (scRet
!= SEC_E_OK
)
607 DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet
));
612 scRet
= sspi_verify_certificate(serverCert
, hostname
, conn
->certFlags
);
614 if (scRet
!= SEC_E_OK
)
616 DEBUG_printf(("_sspiConnect: sspi_verify_certificate failed: %x", scRet
));
622 * Find out how big the header/trailer will be:
624 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
626 if (scRet
!= SEC_E_OK
)
628 DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet
));
636 CertFreeCertificateContext(serverCert
);
643 * '_sspiAccept()' - Accept an SSL/TLS connection
645 BOOL
/* O - 1 on success, 0 on failure */
646 _sspiAccept(_sspi_struct_t
*conn
) /* I - Client connection */
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 */
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
|
671 conn
->decryptBufferUsed
= 0;
674 * Set OutBuffer for AcceptSecurityContext call
676 outBuffer
.cBuffers
= 1;
677 outBuffer
.pBuffers
= outBuffers
;
678 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
680 scRet
= SEC_I_CONTINUE_NEEDED
;
682 while (scRet
== SEC_I_CONTINUE_NEEDED
||
683 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
684 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
686 if ((conn
->decryptBufferUsed
== 0) || (scRet
== SEC_E_INCOMPLETE_MESSAGE
))
688 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
690 conn
->decryptBufferLength
+= 4096;
691 conn
->decryptBuffer
= (BYTE
*) realloc(conn
->decryptBuffer
,
692 conn
->decryptBufferLength
);
694 if (!conn
->decryptBuffer
)
696 DEBUG_printf(("_sspiAccept: unable to allocate %d byte decrypt buffer",
697 conn
->decryptBufferLength
));
705 num
= recv(conn
->sock
,
706 conn
->decryptBuffer
+ conn
->decryptBufferUsed
,
707 (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
),
710 if ((num
== SOCKET_ERROR
) && (WSAGetLastError() == WSAEWOULDBLOCK
))
716 if (num
== SOCKET_ERROR
)
718 DEBUG_printf(("_sspiAccept: recv failed: %d", WSAGetLastError()));
724 DEBUG_printf(("_sspiAccept: client disconnected"));
729 DEBUG_printf(("_sspiAccept: received %d (handshake) bytes from client",
731 conn
->decryptBufferUsed
+= num
;
735 * InBuffers[1] is for getting extra data that
736 * SSPI/SCHANNEL doesn't proccess on this
737 * run around the loop.
739 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
740 inBuffers
[0].cbBuffer
= (unsigned long) conn
->decryptBufferUsed
;
741 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
743 inBuffers
[1].pvBuffer
= NULL
;
744 inBuffers
[1].cbBuffer
= 0;
745 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
747 inBuffer
.cBuffers
= 2;
748 inBuffer
.pBuffers
= inBuffers
;
749 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
752 * Initialize these so if we fail, pvBuffer contains NULL,
753 * so we don't try to free random garbage at the quit
755 outBuffers
[0].pvBuffer
= NULL
;
756 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
757 outBuffers
[0].cbBuffer
= 0;
759 scRet
= AcceptSecurityContext(&conn
->creds
, (fInitContext
?NULL
:&conn
->context
),
760 &inBuffer
, dwSSPIFlags
, SECURITY_NATIVE_DREP
,
761 (fInitContext
?&conn
->context
:NULL
), &outBuffer
,
762 &dwSSPIOutFlags
, &tsExpiry
);
764 fInitContext
= FALSE
;
766 if (scRet
== SEC_E_OK
||
767 scRet
== SEC_I_CONTINUE_NEEDED
||
768 (FAILED(scRet
) && ((dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
) != 0)))
770 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
773 * Send response to server if there is one
775 num
= send(conn
->sock
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
777 if ((num
== SOCKET_ERROR
) || (num
== 0))
779 DEBUG_printf(("_sspiAccept: handshake send failed: %d", WSAGetLastError()));
784 DEBUG_printf(("_sspiAccept: send %d handshake bytes to client",
785 outBuffers
[0].cbBuffer
));
787 FreeContextBuffer(outBuffers
[0].pvBuffer
);
788 outBuffers
[0].pvBuffer
= NULL
;
792 if (scRet
== SEC_E_OK
)
795 * If there's extra data then save it for
796 * next time we go to decrypt
798 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
800 memcpy(conn
->decryptBuffer
,
801 (LPBYTE
) (conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
)),
802 inBuffers
[1].cbBuffer
);
803 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
807 conn
->decryptBufferUsed
= 0;
813 else if (FAILED(scRet
) && (scRet
!= SEC_E_INCOMPLETE_MESSAGE
))
815 DEBUG_printf(("_sspiAccept: AcceptSecurityContext failed: %x", scRet
));
820 if (scRet
!= SEC_E_INCOMPLETE_MESSAGE
&&
821 scRet
!= SEC_I_INCOMPLETE_CREDENTIALS
)
823 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
825 memcpy(conn
->decryptBuffer
,
826 (LPBYTE
) (conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
)),
827 inBuffers
[1].cbBuffer
);
828 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
832 conn
->decryptBufferUsed
= 0;
839 conn
->contextInitialized
= TRUE
;
842 * Find out how big the header will be:
844 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
846 if (scRet
!= SEC_E_OK
)
848 DEBUG_printf(("_sspiAccept: QueryContextAttributes failed: %x", scRet
));
860 * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
863 _sspiSetAllowsAnyRoot(_sspi_struct_t
*conn
,
864 /* I - Client connection */
866 /* I - Allow any root */
868 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_UNKNOWN_CA
:
869 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_UNKNOWN_CA
;
874 * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
877 _sspiSetAllowsExpiredCerts(_sspi_struct_t
*conn
,
878 /* I - Client connection */
880 /* I - Allow expired certs */
882 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
:
883 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
888 * '_sspiWrite()' - Write a buffer to an ssl socket
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 */
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 */
903 if (!conn
|| !buf
|| !len
)
905 WSASetLastError(WSAEINVAL
);
910 bufferLen
= conn
->streamSizes
.cbMaximumMessage
+
911 conn
->streamSizes
.cbHeader
+
912 conn
->streamSizes
.cbTrailer
;
914 buffer
= (BYTE
*) malloc(bufferLen
);
918 DEBUG_printf(("_sspiWrite: buffer alloc of %d bytes failed", bufferLen
));
919 WSASetLastError(E_OUTOFMEMORY
);
928 size_t chunk
= min(conn
->streamSizes
.cbMaximumMessage
, /* Size of data to write */
930 SECURITY_STATUS scRet
; /* SSPI status */
933 * Copy user data into the buffer, starting
934 * just past the header
936 memcpy(buffer
+ conn
->streamSizes
.cbHeader
,
937 ((BYTE
*) buf
) + index
,
941 * Setup the SSPI buffers
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
;
960 scRet
= EncryptMessage(&conn
->context
, 0, &message
, 0);
964 DEBUG_printf(("_sspiWrite: EncryptMessage failed: %x", scRet
));
965 WSASetLastError(WSASYSCALLFAILURE
);
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
977 num
= send(conn
->sock
,
979 buffers
[0].cbBuffer
+ buffers
[1].cbBuffer
+ buffers
[2].cbBuffer
,
982 if ((num
== SOCKET_ERROR
) || (num
== 0))
984 DEBUG_printf(("_sspiWrite: send failed: %ld", WSAGetLastError()));
988 bytesLeft
-= (int) chunk
;
989 index
+= (int) chunk
;
1004 * '_sspiRead()' - Read a buffer from an ssl socket
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 */
1011 SecBufferDesc message
; /* Array of SecBuffer struct */
1012 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
1013 int num
= 0; /* Return value */
1017 WSASetLastError(WSAEINVAL
);
1023 * If there are bytes that have already been
1024 * decrypted and have not yet been read, return
1027 if (buf
&& (conn
->readBufferUsed
> 0))
1029 int bytesToCopy
= (int) min(conn
->readBufferUsed
, len
); /* Amount of bytes to copy */
1030 /* from read buffer */
1032 memcpy(buf
, conn
->readBuffer
, bytesToCopy
);
1033 conn
->readBufferUsed
-= bytesToCopy
;
1035 if (conn
->readBufferUsed
> 0)
1037 * If the caller didn't request all the bytes
1038 * we have in the buffer, then move the unread
1041 memmove(conn
->readBuffer
,
1042 conn
->readBuffer
+ bytesToCopy
,
1043 conn
->readBufferUsed
);
1049 PSecBuffer pDataBuffer
; /* Data buffer */
1050 PSecBuffer pExtraBuffer
; /* Excess data buffer */
1051 SECURITY_STATUS scRet
; /* SSPI status */
1052 int i
; /* Loop control variable */
1055 * Initialize security buffer structs
1057 message
.ulVersion
= SECBUFFER_VERSION
;
1058 message
.cBuffers
= 4;
1059 message
.pBuffers
= buffers
;
1064 * If there is not enough space in the
1065 * buffer, then increase it's size
1067 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1069 conn
->decryptBufferLength
+= 4096;
1070 conn
->decryptBuffer
= (BYTE
*) realloc(conn
->decryptBuffer
,
1071 conn
->decryptBufferLength
);
1073 if (!conn
->decryptBuffer
)
1075 DEBUG_printf(("_sspiRead: unable to allocate %d byte buffer",
1076 conn
->decryptBufferLength
));
1077 WSASetLastError(E_OUTOFMEMORY
);
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
;
1090 scRet
= DecryptMessage(&conn
->context
, &message
, 0, NULL
);
1092 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1096 num
= recv(conn
->sock
,
1097 conn
->decryptBuffer
+ conn
->decryptBufferUsed
,
1098 (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
),
1100 if (num
== SOCKET_ERROR
)
1102 DEBUG_printf(("_sspiRead: recv failed: %d", WSAGetLastError()));
1107 DEBUG_printf(("_sspiRead: server disconnected"));
1111 conn
->decryptBufferUsed
+= num
;
1115 num
= (int) conn
->readBufferUsed
;
1120 while (scRet
== SEC_E_INCOMPLETE_MESSAGE
);
1122 if (scRet
== SEC_I_CONTEXT_EXPIRED
)
1124 DEBUG_printf(("_sspiRead: context expired"));
1125 WSASetLastError(WSAECONNRESET
);
1129 else if (scRet
!= SEC_E_OK
)
1131 DEBUG_printf(("_sspiRead: DecryptMessage failed: %lx", scRet
));
1132 WSASetLastError(WSASYSCALLFAILURE
);
1138 * The decryption worked. Now, locate data buffer.
1141 pExtraBuffer
= NULL
;
1142 for (i
= 1; i
< 4; i
++)
1144 if (buffers
[i
].BufferType
== SECBUFFER_DATA
)
1145 pDataBuffer
= &buffers
[i
];
1146 else if (!pExtraBuffer
&& (buffers
[i
].BufferType
== SECBUFFER_EXTRA
))
1147 pExtraBuffer
= &buffers
[i
];
1151 * If a data buffer is found, then copy
1152 * the decrypted bytes to the passed-in
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 */
1163 memcpy(buf
, pDataBuffer
->pvBuffer
, bytesToCopy
);
1166 * If there are more decrypted bytes than can be
1167 * copied to the passed in buffer, then save them
1171 if ((int)(conn
->readBufferLength
- conn
->readBufferUsed
) < bytesToSave
)
1173 conn
->readBufferLength
= conn
->readBufferUsed
+ bytesToSave
;
1174 conn
->readBuffer
= realloc(conn
->readBuffer
,
1175 conn
->readBufferLength
);
1177 if (!conn
->readBuffer
)
1179 DEBUG_printf(("_sspiRead: unable to allocate %d bytes", conn
->readBufferLength
));
1180 WSASetLastError(E_OUTOFMEMORY
);
1186 memcpy(((BYTE
*) conn
->readBuffer
) + conn
->readBufferUsed
,
1187 ((BYTE
*) pDataBuffer
->pvBuffer
) + bytesToCopy
,
1190 conn
->readBufferUsed
+= bytesToSave
;
1193 num
= (buf
) ? bytesToCopy
: (int) conn
->readBufferUsed
;
1197 DEBUG_printf(("_sspiRead: unable to find data buffer"));
1198 WSASetLastError(WSASYSCALLFAILURE
);
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.
1210 memmove(conn
->decryptBuffer
, pExtraBuffer
->pvBuffer
, pExtraBuffer
->cbBuffer
);
1211 conn
->decryptBufferUsed
= pExtraBuffer
->cbBuffer
;
1215 conn
->decryptBufferUsed
= 0;
1226 * '_sspiPending()' - Returns the number of available bytes
1228 int /* O - Number of available bytes */
1229 _sspiPending(_sspi_struct_t
*conn
) /* I - Client connection */
1231 return (_sspiRead(conn
, NULL
, 0));
1236 * '_sspiFree()' - Close a connection and free resources
1239 _sspiFree(_sspi_struct_t
*conn
) /* I - Client connection */
1244 if (conn
->contextInitialized
)
1246 SecBufferDesc message
; /* Array of SecBuffer struct */
1247 SecBuffer buffers
[1] = { 0 };
1248 /* Security package buffer */
1249 DWORD dwType
; /* Type */
1250 DWORD status
; /* Status */
1253 * Notify schannel that we are about to close the connection.
1255 dwType
= SCHANNEL_SHUTDOWN
;
1257 buffers
[0].pvBuffer
= &dwType
;
1258 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
1259 buffers
[0].cbBuffer
= sizeof(dwType
);
1261 message
.cBuffers
= 1;
1262 message
.pBuffers
= buffers
;
1263 message
.ulVersion
= SECBUFFER_VERSION
;
1265 status
= ApplyControlToken(&conn
->context
, &message
);
1267 if (SUCCEEDED(status
))
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 */
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
|
1283 buffers
[0].pvBuffer
= NULL
;
1284 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
1285 buffers
[0].cbBuffer
= 0;
1287 message
.cBuffers
= 1;
1288 message
.pBuffers
= buffers
;
1289 message
.ulVersion
= SECBUFFER_VERSION
;
1291 status
= AcceptSecurityContext(&conn
->creds
, &conn
->context
, NULL
,
1292 dwSSPIFlags
, SECURITY_NATIVE_DREP
, NULL
,
1293 &message
, &dwSSPIOutFlags
, &tsExpiry
);
1295 if (SUCCEEDED(status
))
1297 pbMessage
= buffers
[0].pvBuffer
;
1298 cbMessage
= buffers
[0].cbBuffer
;
1301 * Send the close notify message to the client.
1303 if (pbMessage
&& cbMessage
)
1305 cbData
= send(conn
->sock
, pbMessage
, cbMessage
, 0);
1306 if ((cbData
== SOCKET_ERROR
) || (cbData
== 0))
1308 status
= WSAGetLastError();
1309 DEBUG_printf(("_sspiFree: sending close notify failed: %d", status
));
1313 FreeContextBuffer(pbMessage
);
1319 DEBUG_printf(("_sspiFree: AcceptSecurityContext failed: %x", status
));
1324 DEBUG_printf(("_sspiFree: ApplyControlToken failed: %x", status
));
1327 DeleteSecurityContext(&conn
->context
);
1328 conn
->contextInitialized
= FALSE
;
1331 if (conn
->decryptBuffer
)
1333 free(conn
->decryptBuffer
);
1334 conn
->decryptBuffer
= NULL
;
1337 if (conn
->readBuffer
)
1339 free(conn
->readBuffer
);
1340 conn
->readBuffer
= NULL
;
1343 if (conn
->sock
!= INVALID_SOCKET
)
1345 closesocket(conn
->sock
);
1346 conn
->sock
= INVALID_SOCKET
;
1354 * 'sspi_verify_certificate()' - Verify a server certificate
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 */
1362 /* I - Verification flags */
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 */
1387 status
= SEC_E_WRONG_PRINCIPAL
;
1392 * Convert server name to unicode.
1394 if (!serverName
|| (strlen(serverName
) == 0))
1396 status
= SEC_E_WRONG_PRINCIPAL
;
1400 count
= MultiByteToWideChar(CP_ACP
, 0, serverName
, -1, NULL
, 0);
1401 serverNameUnicode
= LocalAlloc(LMEM_FIXED
, count
* sizeof(WCHAR
));
1402 if (!serverNameUnicode
)
1404 status
= SEC_E_INSUFFICIENT_MEMORY
;
1407 count
= MultiByteToWideChar(CP_ACP
, 0, serverName
, -1, serverNameUnicode
, count
);
1410 status
= SEC_E_WRONG_PRINCIPAL
;
1415 * Build certificate chain.
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
;
1423 if (!CertGetCertificateChain(NULL
, serverCert
, NULL
, serverCert
->hCertStore
,
1424 &chainPara
, 0, NULL
, &chainContext
))
1426 status
= GetLastError();
1427 DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status
));
1432 * Validate certificate chain.
1434 ZeroMemory(&httpsPolicy
, sizeof(HTTPSPolicyCallbackData
));
1435 httpsPolicy
.cbStruct
= sizeof(HTTPSPolicyCallbackData
);
1436 httpsPolicy
.dwAuthType
= AUTHTYPE_SERVER
;
1437 httpsPolicy
.fdwChecks
= dwCertFlags
;
1438 httpsPolicy
.pwszServerName
= serverNameUnicode
;
1440 memset(&policyPara
, 0, sizeof(policyPara
));
1441 policyPara
.cbSize
= sizeof(policyPara
);
1442 policyPara
.pvExtraPolicyPara
= &httpsPolicy
;
1444 memset(&policyStatus
, 0, sizeof(policyStatus
));
1445 policyStatus
.cbSize
= sizeof(policyStatus
);
1447 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL
, chainContext
,
1448 &policyPara
, &policyStatus
))
1450 status
= GetLastError();
1451 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status
));
1455 if (policyStatus
.dwError
)
1457 status
= policyStatus
.dwError
;
1466 CertFreeCertificateChain(chainContext
);
1468 if (serverNameUnicode
)
1469 LocalFree(serverNameUnicode
);