2 * "$Id: sspi.c 3247 2011-05-12 06:22:31Z msweet $"
4 * Windows SSPI SSL implementation for CUPS.
6 * Copyright 2010-2011 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
16 * sspi_alloc() - Allocate SSPI ssl object
17 * _sspiGetCredentials() - Retrieve an SSL/TLS certificate from the
18 * system store If one cannot be found, one is
20 * _sspiConnect() - Make an SSL connection. This function
21 * assumes a TCP/IP connection has already been
23 * _sspiAccept() - Accept an SSL/TLS connection
24 * _sspiSetAllowsAnyRoot() - Set the client cert policy for untrusted
26 * _sspiSetAllowsExpiredCerts() - Set the client cert policy for expired root
28 * _sspiWrite() - Write a buffer to an ssl socket
29 * _sspiRead() - Read a buffer from an ssl socket
30 * _sspiPending() - Returns the number of available bytes
31 * _sspiFree() - Close a connection and free resources
32 * sspi_verify_certificate() - Verify a server certificate
36 * Include necessary headers...
39 #include "sspi-private.h"
40 #include "debug-private.h"
43 /* required to link this library for certificate functions */
44 #pragma comment(lib, "Crypt32.lib")
45 #pragma comment(lib, "Secur32.lib")
46 #pragma comment(lib, "Ws2_32.lib")
49 #if !defined(SECURITY_FLAG_IGNORE_UNKNOWN_CA)
50 # define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100 /* Untrusted root */
53 #if !defined(SECURITY_FLAG_IGNORE_CERT_DATE_INVALID)
54 # define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */
57 static DWORD
sspi_verify_certificate(PCCERT_CONTEXT serverCert
,
58 const CHAR
*serverName
,
63 * 'sspi_alloc()' - Allocate SSPI ssl object
65 _sspi_struct_t
* /* O - New SSPI/SSL object */
68 _sspi_struct_t
*conn
= calloc(sizeof(_sspi_struct_t
), 1);
71 conn
->sock
= INVALID_SOCKET
;
78 * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store
79 * If one cannot be found, one is created.
81 BOOL
/* O - 1 on success, 0 on failure */
82 _sspiGetCredentials(_sspi_struct_t
*conn
,
83 /* I - Client connection */
84 const LPWSTR container
,
85 /* I - Cert container name */
86 const TCHAR
*cn
, /* I - Common name of certificate */
88 /* I - Is caller a server? */
90 HCERTSTORE store
= NULL
; /* Certificate store */
91 PCCERT_CONTEXT storedContext
= NULL
;
92 /* Context created from the store */
93 PCCERT_CONTEXT createdContext
= NULL
;
94 /* Context created by us */
95 DWORD dwSize
= 0; /* 32 bit size */
96 PBYTE p
= NULL
; /* Temporary storage */
97 HCRYPTPROV hProv
= (HCRYPTPROV
) NULL
;
99 CERT_NAME_BLOB sib
; /* Arbitrary array of bytes */
100 SCHANNEL_CRED SchannelCred
; /* Schannel credential data */
101 TimeStamp tsExpiry
; /* Time stamp */
102 SECURITY_STATUS Status
; /* Status */
103 HCRYPTKEY hKey
= (HCRYPTKEY
) NULL
;
104 /* Handle to crypto key */
105 CRYPT_KEY_PROV_INFO kpi
; /* Key container info */
106 SYSTEMTIME et
; /* System time */
107 CERT_EXTENSIONS exts
; /* Array of cert extensions */
108 CRYPT_KEY_PROV_INFO ckp
; /* Handle to crypto key */
109 BOOL ok
= TRUE
; /* Return value */
116 if (!CryptAcquireContextW(&hProv
, (LPWSTR
) container
, MS_DEF_PROV_W
,
118 CRYPT_NEWKEYSET
| CRYPT_MACHINE_KEYSET
))
120 if (GetLastError() == NTE_EXISTS
)
122 if (!CryptAcquireContextW(&hProv
, (LPWSTR
) container
, MS_DEF_PROV_W
,
123 PROV_RSA_FULL
, CRYPT_MACHINE_KEYSET
))
125 DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n",
133 store
= CertOpenStore(CERT_STORE_PROV_SYSTEM
,
134 X509_ASN_ENCODING
|PKCS_7_ASN_ENCODING
,
136 CERT_SYSTEM_STORE_LOCAL_MACHINE
|
137 CERT_STORE_NO_CRYPT_RELEASE_FLAG
|
138 CERT_STORE_OPEN_EXISTING_FLAG
,
143 DEBUG_printf(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n",
151 if (!CertStrToName(X509_ASN_ENCODING
, cn
, CERT_OID_NAME_STR
,
152 NULL
, NULL
, &dwSize
, NULL
))
154 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n",
160 p
= (PBYTE
) malloc(dwSize
);
164 DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize
));
169 if (!CertStrToName(X509_ASN_ENCODING
, cn
, CERT_OID_NAME_STR
, NULL
,
172 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x",
181 storedContext
= CertFindCertificateInStore(store
, X509_ASN_ENCODING
|PKCS_7_ASN_ENCODING
,
182 0, CERT_FIND_SUBJECT_NAME
, &sib
, NULL
);
187 * If we couldn't find the context, then we'll
190 if (!CryptGenKey(hProv
, AT_KEYEXCHANGE
, CRYPT_EXPORTABLE
, &hKey
))
192 DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x",
198 ZeroMemory(&kpi
, sizeof(kpi
));
199 kpi
.pwszContainerName
= (LPWSTR
) container
;
200 kpi
.pwszProvName
= MS_DEF_PROV_W
;
201 kpi
.dwProvType
= PROV_RSA_FULL
;
202 kpi
.dwFlags
= CERT_SET_KEY_CONTEXT_PROP_ID
;
203 kpi
.dwKeySpec
= AT_KEYEXCHANGE
;
208 ZeroMemory(&exts
, sizeof(exts
));
210 createdContext
= CertCreateSelfSignCertificate(hProv
, &sib
, 0, &kpi
, NULL
, NULL
,
215 DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x",
221 if (!CertAddCertificateContextToStore(store
, createdContext
,
222 CERT_STORE_ADD_REPLACE_EXISTING
,
225 DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x",
231 ZeroMemory(&ckp
, sizeof(ckp
));
232 ckp
.pwszContainerName
= (LPWSTR
) container
;
233 ckp
.pwszProvName
= MS_DEF_PROV_W
;
234 ckp
.dwProvType
= PROV_RSA_FULL
;
235 ckp
.dwFlags
= CRYPT_MACHINE_KEYSET
;
236 ckp
.dwKeySpec
= AT_KEYEXCHANGE
;
238 if (!CertSetCertificateContextProperty(storedContext
,
239 CERT_KEY_PROV_INFO_PROP_ID
,
242 DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x",
249 ZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
251 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
252 SchannelCred
.cCreds
= 1;
253 SchannelCred
.paCred
= &storedContext
;
256 * SSPI doesn't seem to like it if grbitEnabledProtocols
257 * is set for a client
260 SchannelCred
.grbitEnabledProtocols
= SP_PROT_SSL3TLS1
;
263 * Create an SSPI credential.
265 Status
= AcquireCredentialsHandle(NULL
, UNISP_NAME
,
266 isServer
? SECPKG_CRED_INBOUND
:SECPKG_CRED_OUTBOUND
,
267 NULL
, &SchannelCred
, NULL
, NULL
, &conn
->creds
,
269 if (Status
!= SEC_E_OK
)
271 DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status
));
282 CryptDestroyKey(hKey
);
285 CertFreeCertificateContext(createdContext
);
288 CertFreeCertificateContext(storedContext
);
294 CertCloseStore(store
, 0);
297 CryptReleaseContext(hProv
, 0);
304 * '_sspiConnect()' - Make an SSL connection. This function
305 * assumes a TCP/IP connection has already
306 * been successfully made
308 BOOL
/* O - 1 on success, 0 on failure */
309 _sspiConnect(_sspi_struct_t
*conn
, /* I - Client connection */
310 const CHAR
*hostname
) /* I - Server hostname */
312 PCCERT_CONTEXT serverCert
; /* Server certificate */
313 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
314 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
315 TimeStamp tsExpiry
; /* Time stamp */
316 SECURITY_STATUS scRet
; /* Status */
317 DWORD cbData
; /* Data count */
318 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
319 SecBuffer inBuffers
[2]; /* Security package buffer */
320 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
321 SecBuffer outBuffers
[1]; /* Security package buffer */
322 BOOL ok
= TRUE
; /* Return value */
326 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
|
327 ISC_REQ_REPLAY_DETECT
|
328 ISC_REQ_CONFIDENTIALITY
|
329 ISC_RET_EXTENDED_ERROR
|
330 ISC_REQ_ALLOCATE_MEMORY
|
334 * Initiate a ClientHello message and generate a token.
336 outBuffers
[0].pvBuffer
= NULL
;
337 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
338 outBuffers
[0].cbBuffer
= 0;
340 outBuffer
.cBuffers
= 1;
341 outBuffer
.pBuffers
= outBuffers
;
342 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
344 scRet
= InitializeSecurityContext(&conn
->creds
, NULL
, TEXT(""), dwSSPIFlags
,
345 0, SECURITY_NATIVE_DREP
, NULL
, 0, &conn
->context
,
346 &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
348 if (scRet
!= SEC_I_CONTINUE_NEEDED
)
350 DEBUG_printf(("_sspiConnect: InitializeSecurityContext(1) failed: %x", scRet
));
356 * Send response to server if there is one.
358 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
360 cbData
= send(conn
->sock
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
362 if ((cbData
== SOCKET_ERROR
) || !cbData
)
364 DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
365 FreeContextBuffer(outBuffers
[0].pvBuffer
);
366 DeleteSecurityContext(&conn
->context
);
371 DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData
));
374 * Free output buffer.
376 FreeContextBuffer(outBuffers
[0].pvBuffer
);
377 outBuffers
[0].pvBuffer
= NULL
;
380 dwSSPIFlags
= ISC_REQ_MANUAL_CRED_VALIDATION
|
381 ISC_REQ_SEQUENCE_DETECT
|
382 ISC_REQ_REPLAY_DETECT
|
383 ISC_REQ_CONFIDENTIALITY
|
384 ISC_RET_EXTENDED_ERROR
|
385 ISC_REQ_ALLOCATE_MEMORY
|
388 conn
->decryptBufferUsed
= 0;
391 * Loop until the handshake is finished or an error occurs.
393 scRet
= SEC_I_CONTINUE_NEEDED
;
395 while(scRet
== SEC_I_CONTINUE_NEEDED
||
396 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
397 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
399 if ((conn
->decryptBufferUsed
== 0) || (scRet
== SEC_E_INCOMPLETE_MESSAGE
))
401 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
403 conn
->decryptBufferLength
+= 4096;
404 conn
->decryptBuffer
= (BYTE
*) realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
);
406 if (!conn
->decryptBuffer
)
408 DEBUG_printf(("_sspiConnect: unable to allocate %d byte decrypt buffer",
409 conn
->decryptBufferLength
));
410 SetLastError(E_OUTOFMEMORY
);
416 cbData
= recv(conn
->sock
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
,
417 (int) (conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
419 if (cbData
== SOCKET_ERROR
)
421 DEBUG_printf(("_sspiConnect: recv failed: %d", WSAGetLastError()));
425 else if (cbData
== 0)
427 DEBUG_printf(("_sspiConnect: server unexpectedly disconnected"));
432 DEBUG_printf(("_sspiConnect: %d bytes of handshake data received",
435 conn
->decryptBufferUsed
+= cbData
;
439 * Set up the input buffers. Buffer 0 is used to pass in data
440 * received from the server. Schannel will consume some or all
441 * of this. Leftover data (if any) will be placed in buffer 1 and
442 * given a buffer type of SECBUFFER_EXTRA.
444 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
445 inBuffers
[0].cbBuffer
= (unsigned long) conn
->decryptBufferUsed
;
446 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
448 inBuffers
[1].pvBuffer
= NULL
;
449 inBuffers
[1].cbBuffer
= 0;
450 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
452 inBuffer
.cBuffers
= 2;
453 inBuffer
.pBuffers
= inBuffers
;
454 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
457 * Set up the output buffers. These are initialized to NULL
458 * so as to make it less likely we'll attempt to free random
461 outBuffers
[0].pvBuffer
= NULL
;
462 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
463 outBuffers
[0].cbBuffer
= 0;
465 outBuffer
.cBuffers
= 1;
466 outBuffer
.pBuffers
= outBuffers
;
467 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
470 * Call InitializeSecurityContext.
472 scRet
= InitializeSecurityContext(&conn
->creds
, &conn
->context
, NULL
, dwSSPIFlags
,
473 0, SECURITY_NATIVE_DREP
, &inBuffer
, 0, NULL
,
474 &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
477 * If InitializeSecurityContext was successful (or if the error was
478 * one of the special extended ones), send the contends of the output
479 * buffer to the server.
481 if (scRet
== SEC_E_OK
||
482 scRet
== SEC_I_CONTINUE_NEEDED
||
483 FAILED(scRet
) && (dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
))
485 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
487 cbData
= send(conn
->sock
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
489 if ((cbData
== SOCKET_ERROR
) || !cbData
)
491 DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
492 FreeContextBuffer(outBuffers
[0].pvBuffer
);
493 DeleteSecurityContext(&conn
->context
);
498 DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData
));
501 * Free output buffer.
503 FreeContextBuffer(outBuffers
[0].pvBuffer
);
504 outBuffers
[0].pvBuffer
= NULL
;
509 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
510 * then we need to read more data from the server and try again.
512 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
516 * If InitializeSecurityContext returned SEC_E_OK, then the
517 * handshake completed successfully.
519 if (scRet
== SEC_E_OK
)
522 * If the "extra" buffer contains data, this is encrypted application
523 * protocol layer stuff. It needs to be saved. The application layer
524 * will later decrypt it with DecryptMessage.
526 DEBUG_printf(("_sspiConnect: Handshake was successful"));
528 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
530 if (conn
->decryptBufferLength
< inBuffers
[1].cbBuffer
)
532 conn
->decryptBuffer
= realloc(conn
->decryptBuffer
, inBuffers
[1].cbBuffer
);
534 if (!conn
->decryptBuffer
)
536 DEBUG_printf(("_sspiConnect: unable to allocate %d bytes for decrypt buffer",
537 inBuffers
[1].cbBuffer
));
538 SetLastError(E_OUTOFMEMORY
);
544 memmove(conn
->decryptBuffer
,
545 conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
),
546 inBuffers
[1].cbBuffer
);
548 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
550 DEBUG_printf(("_sspiConnect: %d bytes of app data was bundled with handshake data",
551 conn
->decryptBufferUsed
));
554 conn
->decryptBufferUsed
= 0;
563 * Check for fatal error.
567 DEBUG_printf(("_sspiConnect: InitializeSecurityContext(2) failed: %x", scRet
));
573 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
574 * then the server just requested client authentication.
576 if (scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
581 DEBUG_printf(("_sspiConnect: server requested client credentials"));
587 * Copy any leftover data from the "extra" buffer, and go around
590 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
592 memmove(conn
->decryptBuffer
,
593 conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
),
594 inBuffers
[1].cbBuffer
);
596 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
600 conn
->decryptBufferUsed
= 0;
606 conn
->contextInitialized
= TRUE
;
609 * Get the server cert
611 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_REMOTE_CERT_CONTEXT
, (VOID
*) &serverCert
);
613 if (scRet
!= SEC_E_OK
)
615 DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet
));
620 scRet
= sspi_verify_certificate(serverCert
, hostname
, conn
->certFlags
);
622 if (scRet
!= SEC_E_OK
)
624 DEBUG_printf(("_sspiConnect: sspi_verify_certificate failed: %x", scRet
));
630 * Find out how big the header/trailer will be:
632 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
634 if (scRet
!= SEC_E_OK
)
636 DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet
));
644 CertFreeCertificateContext(serverCert
);
651 * '_sspiAccept()' - Accept an SSL/TLS connection
653 BOOL
/* O - 1 on success, 0 on failure */
654 _sspiAccept(_sspi_struct_t
*conn
) /* I - Client connection */
656 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
657 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
658 TimeStamp tsExpiry
; /* Time stamp */
659 SECURITY_STATUS scRet
; /* SSPI Status */
660 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
661 SecBuffer inBuffers
[2]; /* Security package buffer */
662 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
663 SecBuffer outBuffers
[1]; /* Security package buffer */
664 DWORD num
= 0; /* 32 bit status value */
665 BOOL fInitContext
= TRUE
;
666 /* Has the context been init'd? */
667 BOOL ok
= TRUE
; /* Return value */
672 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
673 ASC_REQ_REPLAY_DETECT
|
674 ASC_REQ_CONFIDENTIALITY
|
675 ASC_REQ_EXTENDED_ERROR
|
676 ASC_REQ_ALLOCATE_MEMORY
|
679 conn
->decryptBufferUsed
= 0;
682 * Set OutBuffer for AcceptSecurityContext call
684 outBuffer
.cBuffers
= 1;
685 outBuffer
.pBuffers
= outBuffers
;
686 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
688 scRet
= SEC_I_CONTINUE_NEEDED
;
690 while (scRet
== SEC_I_CONTINUE_NEEDED
||
691 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
692 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
694 if ((conn
->decryptBufferUsed
== 0) || (scRet
== SEC_E_INCOMPLETE_MESSAGE
))
696 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
698 conn
->decryptBufferLength
+= 4096;
699 conn
->decryptBuffer
= (BYTE
*) realloc(conn
->decryptBuffer
,
700 conn
->decryptBufferLength
);
702 if (!conn
->decryptBuffer
)
704 DEBUG_printf(("_sspiAccept: unable to allocate %d byte decrypt buffer",
705 conn
->decryptBufferLength
));
713 num
= recv(conn
->sock
,
714 conn
->decryptBuffer
+ conn
->decryptBufferUsed
,
715 (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
),
718 if ((num
== SOCKET_ERROR
) && (WSAGetLastError() == WSAEWOULDBLOCK
))
724 if (num
== SOCKET_ERROR
)
726 DEBUG_printf(("_sspiAccept: recv failed: %d", WSAGetLastError()));
732 DEBUG_printf(("_sspiAccept: client disconnected"));
737 DEBUG_printf(("_sspiAccept: received %d (handshake) bytes from client",
739 conn
->decryptBufferUsed
+= num
;
743 * InBuffers[1] is for getting extra data that
744 * SSPI/SCHANNEL doesn't proccess on this
745 * run around the loop.
747 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
748 inBuffers
[0].cbBuffer
= (unsigned long) conn
->decryptBufferUsed
;
749 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
751 inBuffers
[1].pvBuffer
= NULL
;
752 inBuffers
[1].cbBuffer
= 0;
753 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
755 inBuffer
.cBuffers
= 2;
756 inBuffer
.pBuffers
= inBuffers
;
757 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
760 * Initialize these so if we fail, pvBuffer contains NULL,
761 * so we don't try to free random garbage at the quit
763 outBuffers
[0].pvBuffer
= NULL
;
764 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
765 outBuffers
[0].cbBuffer
= 0;
767 scRet
= AcceptSecurityContext(&conn
->creds
, (fInitContext
?NULL
:&conn
->context
),
768 &inBuffer
, dwSSPIFlags
, SECURITY_NATIVE_DREP
,
769 (fInitContext
?&conn
->context
:NULL
), &outBuffer
,
770 &dwSSPIOutFlags
, &tsExpiry
);
772 fInitContext
= FALSE
;
774 if (scRet
== SEC_E_OK
||
775 scRet
== SEC_I_CONTINUE_NEEDED
||
776 (FAILED(scRet
) && ((dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
) != 0)))
778 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
781 * Send response to server if there is one
783 num
= send(conn
->sock
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
785 if ((num
== SOCKET_ERROR
) || (num
== 0))
787 DEBUG_printf(("_sspiAccept: handshake send failed: %d", WSAGetLastError()));
792 DEBUG_printf(("_sspiAccept: send %d handshake bytes to client",
793 outBuffers
[0].cbBuffer
));
795 FreeContextBuffer(outBuffers
[0].pvBuffer
);
796 outBuffers
[0].pvBuffer
= NULL
;
800 if (scRet
== SEC_E_OK
)
803 * If there's extra data then save it for
804 * next time we go to decrypt
806 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
808 memcpy(conn
->decryptBuffer
,
809 (LPBYTE
) (conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
)),
810 inBuffers
[1].cbBuffer
);
811 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
815 conn
->decryptBufferUsed
= 0;
821 else if (FAILED(scRet
) && (scRet
!= SEC_E_INCOMPLETE_MESSAGE
))
823 DEBUG_printf(("_sspiAccept: AcceptSecurityContext failed: %x", scRet
));
828 if (scRet
!= SEC_E_INCOMPLETE_MESSAGE
&&
829 scRet
!= SEC_I_INCOMPLETE_CREDENTIALS
)
831 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
833 memcpy(conn
->decryptBuffer
,
834 (LPBYTE
) (conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
)),
835 inBuffers
[1].cbBuffer
);
836 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
840 conn
->decryptBufferUsed
= 0;
847 conn
->contextInitialized
= TRUE
;
850 * Find out how big the header will be:
852 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
854 if (scRet
!= SEC_E_OK
)
856 DEBUG_printf(("_sspiAccept: QueryContextAttributes failed: %x", scRet
));
868 * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
871 _sspiSetAllowsAnyRoot(_sspi_struct_t
*conn
,
872 /* I - Client connection */
874 /* I - Allow any root */
876 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_UNKNOWN_CA
:
877 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_UNKNOWN_CA
;
882 * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
885 _sspiSetAllowsExpiredCerts(_sspi_struct_t
*conn
,
886 /* I - Client connection */
888 /* I - Allow expired certs */
890 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
:
891 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
896 * '_sspiWrite()' - Write a buffer to an ssl socket
898 int /* O - Bytes written or SOCKET_ERROR */
899 _sspiWrite(_sspi_struct_t
*conn
, /* I - Client connection */
900 void *buf
, /* I - Buffer */
901 size_t len
) /* I - Buffer length */
903 SecBufferDesc message
; /* Array of SecBuffer struct */
904 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
905 BYTE
*buffer
= NULL
; /* Scratch buffer */
906 int bufferLen
; /* Buffer length */
907 size_t bytesLeft
; /* Bytes left to write */
908 int index
= 0; /* Index into buffer */
909 int num
= 0; /* Return value */
911 if (!conn
|| !buf
|| !len
)
913 WSASetLastError(WSAEINVAL
);
918 bufferLen
= conn
->streamSizes
.cbMaximumMessage
+
919 conn
->streamSizes
.cbHeader
+
920 conn
->streamSizes
.cbTrailer
;
922 buffer
= (BYTE
*) malloc(bufferLen
);
926 DEBUG_printf(("_sspiWrite: buffer alloc of %d bytes failed", bufferLen
));
927 WSASetLastError(E_OUTOFMEMORY
);
936 size_t chunk
= min(conn
->streamSizes
.cbMaximumMessage
, /* Size of data to write */
938 SECURITY_STATUS scRet
; /* SSPI status */
941 * Copy user data into the buffer, starting
942 * just past the header
944 memcpy(buffer
+ conn
->streamSizes
.cbHeader
,
945 ((BYTE
*) buf
) + index
,
949 * Setup the SSPI buffers
951 message
.ulVersion
= SECBUFFER_VERSION
;
952 message
.cBuffers
= 4;
953 message
.pBuffers
= buffers
;
954 buffers
[0].pvBuffer
= buffer
;
955 buffers
[0].cbBuffer
= conn
->streamSizes
.cbHeader
;
956 buffers
[0].BufferType
= SECBUFFER_STREAM_HEADER
;
957 buffers
[1].pvBuffer
= buffer
+ conn
->streamSizes
.cbHeader
;
958 buffers
[1].cbBuffer
= (unsigned long) chunk
;
959 buffers
[1].BufferType
= SECBUFFER_DATA
;
960 buffers
[2].pvBuffer
= buffer
+ conn
->streamSizes
.cbHeader
+ chunk
;
961 buffers
[2].cbBuffer
= conn
->streamSizes
.cbTrailer
;
962 buffers
[2].BufferType
= SECBUFFER_STREAM_TRAILER
;
963 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
968 scRet
= EncryptMessage(&conn
->context
, 0, &message
, 0);
972 DEBUG_printf(("_sspiWrite: EncryptMessage failed: %x", scRet
));
973 WSASetLastError(WSASYSCALLFAILURE
);
979 * Send the data. Remember the size of
980 * the total data to send is the size
981 * of the header, the size of the data
982 * the caller passed in and the size
985 num
= send(conn
->sock
,
987 buffers
[0].cbBuffer
+ buffers
[1].cbBuffer
+ buffers
[2].cbBuffer
,
990 if ((num
== SOCKET_ERROR
) || (num
== 0))
992 DEBUG_printf(("_sspiWrite: send failed: %ld", WSAGetLastError()));
996 bytesLeft
-= (int) chunk
;
997 index
+= (int) chunk
;
1012 * '_sspiRead()' - Read a buffer from an ssl socket
1014 int /* O - Bytes read or SOCKET_ERROR */
1015 _sspiRead(_sspi_struct_t
*conn
, /* I - Client connection */
1016 void *buf
, /* I - Buffer */
1017 size_t len
) /* I - Buffer length */
1019 SecBufferDesc message
; /* Array of SecBuffer struct */
1020 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
1021 int num
= 0; /* Return value */
1025 WSASetLastError(WSAEINVAL
);
1031 * If there are bytes that have already been
1032 * decrypted and have not yet been read, return
1035 if (buf
&& (conn
->readBufferUsed
> 0))
1037 int bytesToCopy
= (int) min(conn
->readBufferUsed
, len
); /* Amount of bytes to copy */
1038 /* from read buffer */
1040 memcpy(buf
, conn
->readBuffer
, bytesToCopy
);
1041 conn
->readBufferUsed
-= bytesToCopy
;
1043 if (conn
->readBufferUsed
> 0)
1045 * If the caller didn't request all the bytes
1046 * we have in the buffer, then move the unread
1049 memmove(conn
->readBuffer
,
1050 conn
->readBuffer
+ bytesToCopy
,
1051 conn
->readBufferUsed
);
1057 PSecBuffer pDataBuffer
; /* Data buffer */
1058 PSecBuffer pExtraBuffer
; /* Excess data buffer */
1059 SECURITY_STATUS scRet
; /* SSPI status */
1060 int i
; /* Loop control variable */
1063 * Initialize security buffer structs
1065 message
.ulVersion
= SECBUFFER_VERSION
;
1066 message
.cBuffers
= 4;
1067 message
.pBuffers
= buffers
;
1072 * If there is not enough space in the
1073 * buffer, then increase it's size
1075 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1077 conn
->decryptBufferLength
+= 4096;
1078 conn
->decryptBuffer
= (BYTE
*) realloc(conn
->decryptBuffer
,
1079 conn
->decryptBufferLength
);
1081 if (!conn
->decryptBuffer
)
1083 DEBUG_printf(("_sspiRead: unable to allocate %d byte buffer",
1084 conn
->decryptBufferLength
));
1085 WSASetLastError(E_OUTOFMEMORY
);
1091 buffers
[0].pvBuffer
= conn
->decryptBuffer
;
1092 buffers
[0].cbBuffer
= (unsigned long) conn
->decryptBufferUsed
;
1093 buffers
[0].BufferType
= SECBUFFER_DATA
;
1094 buffers
[1].BufferType
= SECBUFFER_EMPTY
;
1095 buffers
[2].BufferType
= SECBUFFER_EMPTY
;
1096 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
1098 scRet
= DecryptMessage(&conn
->context
, &message
, 0, NULL
);
1100 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1104 num
= recv(conn
->sock
,
1105 conn
->decryptBuffer
+ conn
->decryptBufferUsed
,
1106 (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
),
1108 if (num
== SOCKET_ERROR
)
1110 DEBUG_printf(("_sspiRead: recv failed: %d", WSAGetLastError()));
1115 DEBUG_printf(("_sspiRead: server disconnected"));
1119 conn
->decryptBufferUsed
+= num
;
1123 num
= (int) conn
->readBufferUsed
;
1128 while (scRet
== SEC_E_INCOMPLETE_MESSAGE
);
1130 if (scRet
== SEC_I_CONTEXT_EXPIRED
)
1132 DEBUG_printf(("_sspiRead: context expired"));
1133 WSASetLastError(WSAECONNRESET
);
1137 else if (scRet
!= SEC_E_OK
)
1139 DEBUG_printf(("_sspiRead: DecryptMessage failed: %lx", scRet
));
1140 WSASetLastError(WSASYSCALLFAILURE
);
1146 * The decryption worked. Now, locate data buffer.
1149 pExtraBuffer
= NULL
;
1150 for (i
= 1; i
< 4; i
++)
1152 if (buffers
[i
].BufferType
== SECBUFFER_DATA
)
1153 pDataBuffer
= &buffers
[i
];
1154 else if (!pExtraBuffer
&& (buffers
[i
].BufferType
== SECBUFFER_EXTRA
))
1155 pExtraBuffer
= &buffers
[i
];
1159 * If a data buffer is found, then copy
1160 * the decrypted bytes to the passed-in
1165 int bytesToCopy
= min(pDataBuffer
->cbBuffer
, (int) len
);
1166 /* Number of bytes to copy into buf */
1167 int bytesToSave
= pDataBuffer
->cbBuffer
- bytesToCopy
;
1168 /* Number of bytes to save in our read buffer */
1171 memcpy(buf
, pDataBuffer
->pvBuffer
, bytesToCopy
);
1174 * If there are more decrypted bytes than can be
1175 * copied to the passed in buffer, then save them
1179 if ((int)(conn
->readBufferLength
- conn
->readBufferUsed
) < bytesToSave
)
1181 conn
->readBufferLength
= conn
->readBufferUsed
+ bytesToSave
;
1182 conn
->readBuffer
= realloc(conn
->readBuffer
,
1183 conn
->readBufferLength
);
1185 if (!conn
->readBuffer
)
1187 DEBUG_printf(("_sspiRead: unable to allocate %d bytes", conn
->readBufferLength
));
1188 WSASetLastError(E_OUTOFMEMORY
);
1194 memcpy(((BYTE
*) conn
->readBuffer
) + conn
->readBufferUsed
,
1195 ((BYTE
*) pDataBuffer
->pvBuffer
) + bytesToCopy
,
1198 conn
->readBufferUsed
+= bytesToSave
;
1201 num
= (buf
) ? bytesToCopy
: (int) conn
->readBufferUsed
;
1205 DEBUG_printf(("_sspiRead: unable to find data buffer"));
1206 WSASetLastError(WSASYSCALLFAILURE
);
1212 * If the decryption process left extra bytes,
1213 * then save those back in decryptBuffer. They will
1214 * be processed the next time through the loop.
1218 memmove(conn
->decryptBuffer
, pExtraBuffer
->pvBuffer
, pExtraBuffer
->cbBuffer
);
1219 conn
->decryptBufferUsed
= pExtraBuffer
->cbBuffer
;
1223 conn
->decryptBufferUsed
= 0;
1234 * '_sspiPending()' - Returns the number of available bytes
1236 int /* O - Number of available bytes */
1237 _sspiPending(_sspi_struct_t
*conn
) /* I - Client connection */
1239 return (_sspiRead(conn
, NULL
, 0));
1244 * '_sspiFree()' - Close a connection and free resources
1247 _sspiFree(_sspi_struct_t
*conn
) /* I - Client connection */
1252 if (conn
->contextInitialized
)
1254 SecBufferDesc message
; /* Array of SecBuffer struct */
1255 SecBuffer buffers
[1] = { 0 };
1256 /* Security package buffer */
1257 DWORD dwType
; /* Type */
1258 DWORD status
; /* Status */
1261 * Notify schannel that we are about to close the connection.
1263 dwType
= SCHANNEL_SHUTDOWN
;
1265 buffers
[0].pvBuffer
= &dwType
;
1266 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
1267 buffers
[0].cbBuffer
= sizeof(dwType
);
1269 message
.cBuffers
= 1;
1270 message
.pBuffers
= buffers
;
1271 message
.ulVersion
= SECBUFFER_VERSION
;
1273 status
= ApplyControlToken(&conn
->context
, &message
);
1275 if (SUCCEEDED(status
))
1277 PBYTE pbMessage
; /* Message buffer */
1278 DWORD cbMessage
; /* Message buffer count */
1279 DWORD cbData
; /* Data count */
1280 DWORD dwSSPIFlags
; /* SSL attributes we requested */
1281 DWORD dwSSPIOutFlags
; /* SSL attributes we received */
1282 TimeStamp tsExpiry
; /* Time stamp */
1284 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
1285 ASC_REQ_REPLAY_DETECT
|
1286 ASC_REQ_CONFIDENTIALITY
|
1287 ASC_REQ_EXTENDED_ERROR
|
1288 ASC_REQ_ALLOCATE_MEMORY
|
1291 buffers
[0].pvBuffer
= NULL
;
1292 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
1293 buffers
[0].cbBuffer
= 0;
1295 message
.cBuffers
= 1;
1296 message
.pBuffers
= buffers
;
1297 message
.ulVersion
= SECBUFFER_VERSION
;
1299 status
= AcceptSecurityContext(&conn
->creds
, &conn
->context
, NULL
,
1300 dwSSPIFlags
, SECURITY_NATIVE_DREP
, NULL
,
1301 &message
, &dwSSPIOutFlags
, &tsExpiry
);
1303 if (SUCCEEDED(status
))
1305 pbMessage
= buffers
[0].pvBuffer
;
1306 cbMessage
= buffers
[0].cbBuffer
;
1309 * Send the close notify message to the client.
1311 if (pbMessage
&& cbMessage
)
1313 cbData
= send(conn
->sock
, pbMessage
, cbMessage
, 0);
1314 if ((cbData
== SOCKET_ERROR
) || (cbData
== 0))
1316 status
= WSAGetLastError();
1317 DEBUG_printf(("_sspiFree: sending close notify failed: %d", status
));
1321 FreeContextBuffer(pbMessage
);
1327 DEBUG_printf(("_sspiFree: AcceptSecurityContext failed: %x", status
));
1332 DEBUG_printf(("_sspiFree: ApplyControlToken failed: %x", status
));
1335 DeleteSecurityContext(&conn
->context
);
1336 conn
->contextInitialized
= FALSE
;
1339 if (conn
->decryptBuffer
)
1341 free(conn
->decryptBuffer
);
1342 conn
->decryptBuffer
= NULL
;
1345 if (conn
->readBuffer
)
1347 free(conn
->readBuffer
);
1348 conn
->readBuffer
= NULL
;
1351 if (conn
->sock
!= INVALID_SOCKET
)
1353 closesocket(conn
->sock
);
1354 conn
->sock
= INVALID_SOCKET
;
1362 * 'sspi_verify_certificate()' - Verify a server certificate
1364 static DWORD
/* 0 - Error code (0 == No error) */
1365 sspi_verify_certificate(PCCERT_CONTEXT serverCert
,
1366 /* I - Server certificate */
1367 const CHAR
*serverName
,
1368 /* I - Server name */
1370 /* I - Verification flags */
1372 HTTPSPolicyCallbackData httpsPolicy
;
1373 /* HTTPS Policy Struct */
1374 CERT_CHAIN_POLICY_PARA policyPara
;
1375 /* Cert chain policy parameters */
1376 CERT_CHAIN_POLICY_STATUS policyStatus
;
1377 /* Cert chain policy status */
1378 CERT_CHAIN_PARA chainPara
;
1379 /* Used for searching and matching criteria */
1380 PCCERT_CHAIN_CONTEXT chainContext
= NULL
;
1381 /* Certificate chain */
1382 PWSTR serverNameUnicode
= NULL
;
1383 /* Unicode server name */
1384 LPSTR rgszUsages
[] = { szOID_PKIX_KP_SERVER_AUTH
,
1385 szOID_SERVER_GATED_CRYPTO
,
1386 szOID_SGC_NETSCAPE
};
1387 /* How are we using this certificate? */
1388 DWORD cUsages
= sizeof(rgszUsages
) / sizeof(LPSTR
);
1389 /* Number of ites in rgszUsages */
1390 DWORD count
; /* 32 bit count variable */
1391 DWORD status
; /* Return value */
1395 status
= SEC_E_WRONG_PRINCIPAL
;
1400 * Convert server name to unicode.
1402 if (!serverName
|| (strlen(serverName
) == 0))
1404 status
= SEC_E_WRONG_PRINCIPAL
;
1408 count
= MultiByteToWideChar(CP_ACP
, 0, serverName
, -1, NULL
, 0);
1409 serverNameUnicode
= LocalAlloc(LMEM_FIXED
, count
* sizeof(WCHAR
));
1410 if (!serverNameUnicode
)
1412 status
= SEC_E_INSUFFICIENT_MEMORY
;
1415 count
= MultiByteToWideChar(CP_ACP
, 0, serverName
, -1, serverNameUnicode
, count
);
1418 status
= SEC_E_WRONG_PRINCIPAL
;
1423 * Build certificate chain.
1425 ZeroMemory(&chainPara
, sizeof(chainPara
));
1426 chainPara
.cbSize
= sizeof(chainPara
);
1427 chainPara
.RequestedUsage
.dwType
= USAGE_MATCH_TYPE_OR
;
1428 chainPara
.RequestedUsage
.Usage
.cUsageIdentifier
= cUsages
;
1429 chainPara
.RequestedUsage
.Usage
.rgpszUsageIdentifier
= rgszUsages
;
1431 if (!CertGetCertificateChain(NULL
, serverCert
, NULL
, serverCert
->hCertStore
,
1432 &chainPara
, 0, NULL
, &chainContext
))
1434 status
= GetLastError();
1435 DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status
));
1440 * Validate certificate chain.
1442 ZeroMemory(&httpsPolicy
, sizeof(HTTPSPolicyCallbackData
));
1443 httpsPolicy
.cbStruct
= sizeof(HTTPSPolicyCallbackData
);
1444 httpsPolicy
.dwAuthType
= AUTHTYPE_SERVER
;
1445 httpsPolicy
.fdwChecks
= dwCertFlags
;
1446 httpsPolicy
.pwszServerName
= serverNameUnicode
;
1448 memset(&policyPara
, 0, sizeof(policyPara
));
1449 policyPara
.cbSize
= sizeof(policyPara
);
1450 policyPara
.pvExtraPolicyPara
= &httpsPolicy
;
1452 memset(&policyStatus
, 0, sizeof(policyStatus
));
1453 policyStatus
.cbSize
= sizeof(policyStatus
);
1455 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL
, chainContext
,
1456 &policyPara
, &policyStatus
))
1458 status
= GetLastError();
1459 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status
));
1463 if (policyStatus
.dwError
)
1465 status
= policyStatus
.dwError
;
1474 CertFreeCertificateChain(chainContext
);
1476 if (serverNameUnicode
)
1477 LocalFree(serverNameUnicode
);
1484 * End of "$Id: sspi.c 3247 2011-05-12 06:22:31Z msweet $".