2 * "$Id: sspi.c 11760 2014-03-28 12:58:24Z msweet $"
4 * Windows SSPI SSL implementation for CUPS.
6 * Copyright 2010-2014 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 * Include necessary headers...
19 #include "sspi-private.h"
20 #include "debug-private.h"
23 /* required to link this library for certificate functions */
24 #pragma comment(lib, "Crypt32.lib")
25 #pragma comment(lib, "Secur32.lib")
26 #pragma comment(lib, "Ws2_32.lib")
29 #if !defined(SECURITY_FLAG_IGNORE_UNKNOWN_CA)
30 # define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100 /* Untrusted root */
33 #if !defined(SECURITY_FLAG_IGNORE_CERT_DATE_INVALID)
34 # define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */
37 static DWORD
sspi_verify_certificate(PCCERT_CONTEXT serverCert
,
38 const CHAR
*serverName
,
43 * 'sspi_alloc()' - Allocate SSPI ssl object
45 _sspi_struct_t
* /* O - New SSPI/SSL object */
48 _sspi_struct_t
*conn
= calloc(sizeof(_sspi_struct_t
), 1);
51 conn
->sock
= INVALID_SOCKET
;
58 * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store
59 * If one cannot be found, one is created.
61 BOOL
/* O - 1 on success, 0 on failure */
62 _sspiGetCredentials(_sspi_struct_t
*conn
,
63 /* I - Client connection */
64 const LPWSTR container
,
65 /* I - Cert container name */
66 const TCHAR
*cn
, /* I - Common name of certificate */
68 /* I - Is caller a server? */
70 HCERTSTORE store
= NULL
; /* Certificate store */
71 PCCERT_CONTEXT storedContext
= NULL
;
72 /* Context created from the store */
73 PCCERT_CONTEXT createdContext
= NULL
;
74 /* Context created by us */
75 DWORD dwSize
= 0; /* 32 bit size */
76 PBYTE p
= NULL
; /* Temporary storage */
77 HCRYPTPROV hProv
= (HCRYPTPROV
) NULL
;
79 CERT_NAME_BLOB sib
; /* Arbitrary array of bytes */
80 SCHANNEL_CRED SchannelCred
; /* Schannel credential data */
81 TimeStamp tsExpiry
; /* Time stamp */
82 SECURITY_STATUS Status
; /* Status */
83 HCRYPTKEY hKey
= (HCRYPTKEY
) NULL
;
84 /* Handle to crypto key */
85 CRYPT_KEY_PROV_INFO kpi
; /* Key container info */
86 SYSTEMTIME et
; /* System time */
87 CERT_EXTENSIONS exts
; /* Array of cert extensions */
88 CRYPT_KEY_PROV_INFO ckp
; /* Handle to crypto key */
89 BOOL ok
= TRUE
; /* Return value */
92 DEBUG_printf(("_sspiGetCredentials(conn=%p, container=%p, cn=\"%s\", isServer=%d)", conn
, container
, cn
, isServer
));
99 if (!CryptAcquireContextW(&hProv
, (LPWSTR
) container
, MS_DEF_PROV_W
,
101 CRYPT_NEWKEYSET
| CRYPT_MACHINE_KEYSET
))
103 if (GetLastError() == NTE_EXISTS
)
105 if (!CryptAcquireContextW(&hProv
, (LPWSTR
) container
, MS_DEF_PROV_W
,
106 PROV_RSA_FULL
, CRYPT_MACHINE_KEYSET
))
108 DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n",
116 store
= CertOpenStore(CERT_STORE_PROV_SYSTEM
,
117 X509_ASN_ENCODING
|PKCS_7_ASN_ENCODING
,
119 CERT_SYSTEM_STORE_LOCAL_MACHINE
|
120 CERT_STORE_NO_CRYPT_RELEASE_FLAG
|
121 CERT_STORE_OPEN_EXISTING_FLAG
,
126 DEBUG_printf(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n",
134 if (!CertStrToName(X509_ASN_ENCODING
, cn
, CERT_OID_NAME_STR
,
135 NULL
, NULL
, &dwSize
, NULL
))
137 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n",
143 p
= (PBYTE
) malloc(dwSize
);
147 DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize
));
152 if (!CertStrToName(X509_ASN_ENCODING
, cn
, CERT_OID_NAME_STR
, NULL
,
155 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x",
164 storedContext
= CertFindCertificateInStore(store
, X509_ASN_ENCODING
|PKCS_7_ASN_ENCODING
,
165 0, CERT_FIND_SUBJECT_NAME
, &sib
, NULL
);
170 * If we couldn't find the context, then we'll
173 if (!CryptGenKey(hProv
, AT_KEYEXCHANGE
, CRYPT_EXPORTABLE
, &hKey
))
175 DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x",
181 ZeroMemory(&kpi
, sizeof(kpi
));
182 kpi
.pwszContainerName
= (LPWSTR
) container
;
183 kpi
.pwszProvName
= MS_DEF_PROV_W
;
184 kpi
.dwProvType
= PROV_RSA_FULL
;
185 kpi
.dwFlags
= CERT_SET_KEY_CONTEXT_PROP_ID
;
186 kpi
.dwKeySpec
= AT_KEYEXCHANGE
;
191 ZeroMemory(&exts
, sizeof(exts
));
193 createdContext
= CertCreateSelfSignCertificate(hProv
, &sib
, 0, &kpi
, NULL
, NULL
,
198 DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x",
204 if (!CertAddCertificateContextToStore(store
, createdContext
,
205 CERT_STORE_ADD_REPLACE_EXISTING
,
208 DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x",
214 ZeroMemory(&ckp
, sizeof(ckp
));
215 ckp
.pwszContainerName
= (LPWSTR
) container
;
216 ckp
.pwszProvName
= MS_DEF_PROV_W
;
217 ckp
.dwProvType
= PROV_RSA_FULL
;
218 ckp
.dwFlags
= CRYPT_MACHINE_KEYSET
;
219 ckp
.dwKeySpec
= AT_KEYEXCHANGE
;
221 if (!CertSetCertificateContextProperty(storedContext
,
222 CERT_KEY_PROV_INFO_PROP_ID
,
225 DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x",
232 ZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
234 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
235 SchannelCred
.cCreds
= 1;
236 SchannelCred
.paCred
= &storedContext
;
239 * SSPI doesn't seem to like it if grbitEnabledProtocols
240 * is set for a client
243 SchannelCred
.grbitEnabledProtocols
= SP_PROT_SSL3TLS1
;
246 * Create an SSPI credential.
248 Status
= AcquireCredentialsHandle(NULL
, UNISP_NAME
,
249 isServer
? SECPKG_CRED_INBOUND
:SECPKG_CRED_OUTBOUND
,
250 NULL
, &SchannelCred
, NULL
, NULL
, &conn
->creds
,
252 if (Status
!= SEC_E_OK
)
254 DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status
));
265 CryptDestroyKey(hKey
);
268 CertFreeCertificateContext(createdContext
);
271 CertFreeCertificateContext(storedContext
);
277 CertCloseStore(store
, 0);
280 CryptReleaseContext(hProv
, 0);
287 * '_sspiConnect()' - Make an SSL connection. This function
288 * assumes a TCP/IP connection has already
289 * been successfully made
291 BOOL
/* O - 1 on success, 0 on failure */
292 _sspiConnect(_sspi_struct_t
*conn
, /* I - Client connection */
293 const CHAR
*hostname
) /* I - Server hostname */
295 PCCERT_CONTEXT serverCert
; /* Server certificate */
296 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
297 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
298 TimeStamp tsExpiry
; /* Time stamp */
299 SECURITY_STATUS scRet
; /* Status */
300 DWORD cbData
; /* Data count */
301 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
302 SecBuffer inBuffers
[2]; /* Security package buffer */
303 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
304 SecBuffer outBuffers
[1]; /* Security package buffer */
305 BOOL ok
= TRUE
; /* Return value */
309 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
|
310 ISC_REQ_REPLAY_DETECT
|
311 ISC_REQ_CONFIDENTIALITY
|
312 ISC_RET_EXTENDED_ERROR
|
313 ISC_REQ_ALLOCATE_MEMORY
|
317 * Initiate a ClientHello message and generate a token.
319 outBuffers
[0].pvBuffer
= NULL
;
320 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
321 outBuffers
[0].cbBuffer
= 0;
323 outBuffer
.cBuffers
= 1;
324 outBuffer
.pBuffers
= outBuffers
;
325 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
327 scRet
= InitializeSecurityContext(&conn
->creds
, NULL
, TEXT(""), dwSSPIFlags
,
328 0, SECURITY_NATIVE_DREP
, NULL
, 0, &conn
->context
,
329 &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
331 if (scRet
!= SEC_I_CONTINUE_NEEDED
)
333 DEBUG_printf(("_sspiConnect: InitializeSecurityContext(1) failed: %x", scRet
));
339 * Send response to server if there is one.
341 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
343 cbData
= send(conn
->sock
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
345 if ((cbData
== SOCKET_ERROR
) || !cbData
)
347 DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
348 FreeContextBuffer(outBuffers
[0].pvBuffer
);
349 DeleteSecurityContext(&conn
->context
);
354 DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData
));
357 * Free output buffer.
359 FreeContextBuffer(outBuffers
[0].pvBuffer
);
360 outBuffers
[0].pvBuffer
= NULL
;
363 dwSSPIFlags
= ISC_REQ_MANUAL_CRED_VALIDATION
|
364 ISC_REQ_SEQUENCE_DETECT
|
365 ISC_REQ_REPLAY_DETECT
|
366 ISC_REQ_CONFIDENTIALITY
|
367 ISC_RET_EXTENDED_ERROR
|
368 ISC_REQ_ALLOCATE_MEMORY
|
371 conn
->decryptBufferUsed
= 0;
374 * Loop until the handshake is finished or an error occurs.
376 scRet
= SEC_I_CONTINUE_NEEDED
;
378 while(scRet
== SEC_I_CONTINUE_NEEDED
||
379 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
380 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
382 if ((conn
->decryptBufferUsed
== 0) || (scRet
== SEC_E_INCOMPLETE_MESSAGE
))
384 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
386 conn
->decryptBufferLength
+= 4096;
387 conn
->decryptBuffer
= (BYTE
*) realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
);
389 if (!conn
->decryptBuffer
)
391 DEBUG_printf(("_sspiConnect: unable to allocate %d byte decrypt buffer",
392 conn
->decryptBufferLength
));
393 SetLastError(E_OUTOFMEMORY
);
399 cbData
= recv(conn
->sock
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
,
400 (int) (conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
402 if (cbData
== SOCKET_ERROR
)
404 DEBUG_printf(("_sspiConnect: recv failed: %d", WSAGetLastError()));
408 else if (cbData
== 0)
410 DEBUG_printf(("_sspiConnect: server unexpectedly disconnected"));
415 DEBUG_printf(("_sspiConnect: %d bytes of handshake data received",
418 conn
->decryptBufferUsed
+= cbData
;
422 * Set up the input buffers. Buffer 0 is used to pass in data
423 * received from the server. Schannel will consume some or all
424 * of this. Leftover data (if any) will be placed in buffer 1 and
425 * given a buffer type of SECBUFFER_EXTRA.
427 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
428 inBuffers
[0].cbBuffer
= (unsigned long) conn
->decryptBufferUsed
;
429 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
431 inBuffers
[1].pvBuffer
= NULL
;
432 inBuffers
[1].cbBuffer
= 0;
433 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
435 inBuffer
.cBuffers
= 2;
436 inBuffer
.pBuffers
= inBuffers
;
437 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
440 * Set up the output buffers. These are initialized to NULL
441 * so as to make it less likely we'll attempt to free random
444 outBuffers
[0].pvBuffer
= NULL
;
445 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
446 outBuffers
[0].cbBuffer
= 0;
448 outBuffer
.cBuffers
= 1;
449 outBuffer
.pBuffers
= outBuffers
;
450 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
453 * Call InitializeSecurityContext.
455 scRet
= InitializeSecurityContext(&conn
->creds
, &conn
->context
, NULL
, dwSSPIFlags
,
456 0, SECURITY_NATIVE_DREP
, &inBuffer
, 0, NULL
,
457 &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
460 * If InitializeSecurityContext was successful (or if the error was
461 * one of the special extended ones), send the contends of the output
462 * buffer to the server.
464 if (scRet
== SEC_E_OK
||
465 scRet
== SEC_I_CONTINUE_NEEDED
||
466 FAILED(scRet
) && (dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
))
468 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
470 cbData
= send(conn
->sock
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
472 if ((cbData
== SOCKET_ERROR
) || !cbData
)
474 DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
475 FreeContextBuffer(outBuffers
[0].pvBuffer
);
476 DeleteSecurityContext(&conn
->context
);
481 DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData
));
484 * Free output buffer.
486 FreeContextBuffer(outBuffers
[0].pvBuffer
);
487 outBuffers
[0].pvBuffer
= NULL
;
492 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
493 * then we need to read more data from the server and try again.
495 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
499 * If InitializeSecurityContext returned SEC_E_OK, then the
500 * handshake completed successfully.
502 if (scRet
== SEC_E_OK
)
505 * If the "extra" buffer contains data, this is encrypted application
506 * protocol layer stuff. It needs to be saved. The application layer
507 * will later decrypt it with DecryptMessage.
509 DEBUG_printf(("_sspiConnect: Handshake was successful"));
511 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
513 if (conn
->decryptBufferLength
< inBuffers
[1].cbBuffer
)
515 conn
->decryptBuffer
= realloc(conn
->decryptBuffer
, inBuffers
[1].cbBuffer
);
517 if (!conn
->decryptBuffer
)
519 DEBUG_printf(("_sspiConnect: unable to allocate %d bytes for decrypt buffer",
520 inBuffers
[1].cbBuffer
));
521 SetLastError(E_OUTOFMEMORY
);
527 memmove(conn
->decryptBuffer
,
528 conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
),
529 inBuffers
[1].cbBuffer
);
531 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
533 DEBUG_printf(("_sspiConnect: %d bytes of app data was bundled with handshake data",
534 conn
->decryptBufferUsed
));
537 conn
->decryptBufferUsed
= 0;
546 * Check for fatal error.
550 DEBUG_printf(("_sspiConnect: InitializeSecurityContext(2) failed: %x", scRet
));
556 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
557 * then the server just requested client authentication.
559 if (scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
564 DEBUG_printf(("_sspiConnect: server requested client credentials"));
570 * Copy any leftover data from the "extra" buffer, and go around
573 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
575 memmove(conn
->decryptBuffer
,
576 conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
),
577 inBuffers
[1].cbBuffer
);
579 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
583 conn
->decryptBufferUsed
= 0;
589 conn
->contextInitialized
= TRUE
;
592 * Get the server cert
594 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_REMOTE_CERT_CONTEXT
, (VOID
*) &serverCert
);
596 if (scRet
!= SEC_E_OK
)
598 DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet
));
603 scRet
= sspi_verify_certificate(serverCert
, hostname
, conn
->certFlags
);
605 if (scRet
!= SEC_E_OK
)
607 DEBUG_printf(("_sspiConnect: sspi_verify_certificate failed: %x", scRet
));
613 * Find out how big the header/trailer will be:
615 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
617 if (scRet
!= SEC_E_OK
)
619 DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet
));
627 CertFreeCertificateContext(serverCert
);
634 * '_sspiAccept()' - Accept an SSL/TLS connection
636 BOOL
/* O - 1 on success, 0 on failure */
637 _sspiAccept(_sspi_struct_t
*conn
) /* I - Client connection */
639 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
640 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
641 TimeStamp tsExpiry
; /* Time stamp */
642 SECURITY_STATUS scRet
; /* SSPI Status */
643 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
644 SecBuffer inBuffers
[2]; /* Security package buffer */
645 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
646 SecBuffer outBuffers
[1]; /* Security package buffer */
647 DWORD num
= 0; /* 32 bit status value */
648 BOOL fInitContext
= TRUE
;
649 /* Has the context been init'd? */
650 BOOL ok
= TRUE
; /* Return value */
655 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
656 ASC_REQ_REPLAY_DETECT
|
657 ASC_REQ_CONFIDENTIALITY
|
658 ASC_REQ_EXTENDED_ERROR
|
659 ASC_REQ_ALLOCATE_MEMORY
|
662 conn
->decryptBufferUsed
= 0;
665 * Set OutBuffer for AcceptSecurityContext call
667 outBuffer
.cBuffers
= 1;
668 outBuffer
.pBuffers
= outBuffers
;
669 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
671 scRet
= SEC_I_CONTINUE_NEEDED
;
673 while (scRet
== SEC_I_CONTINUE_NEEDED
||
674 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
675 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
677 if ((conn
->decryptBufferUsed
== 0) || (scRet
== SEC_E_INCOMPLETE_MESSAGE
))
679 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
681 conn
->decryptBufferLength
+= 4096;
682 conn
->decryptBuffer
= (BYTE
*) realloc(conn
->decryptBuffer
,
683 conn
->decryptBufferLength
);
685 if (!conn
->decryptBuffer
)
687 DEBUG_printf(("_sspiAccept: unable to allocate %d byte decrypt buffer",
688 conn
->decryptBufferLength
));
696 num
= recv(conn
->sock
,
697 conn
->decryptBuffer
+ conn
->decryptBufferUsed
,
698 (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
),
701 if ((num
== SOCKET_ERROR
) && (WSAGetLastError() == WSAEWOULDBLOCK
))
707 if (num
== SOCKET_ERROR
)
709 DEBUG_printf(("_sspiAccept: recv failed: %d", WSAGetLastError()));
715 DEBUG_printf(("_sspiAccept: client disconnected"));
720 DEBUG_printf(("_sspiAccept: received %d (handshake) bytes from client",
722 conn
->decryptBufferUsed
+= num
;
726 * InBuffers[1] is for getting extra data that
727 * SSPI/SCHANNEL doesn't proccess on this
728 * run around the loop.
730 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
731 inBuffers
[0].cbBuffer
= (unsigned long) conn
->decryptBufferUsed
;
732 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
734 inBuffers
[1].pvBuffer
= NULL
;
735 inBuffers
[1].cbBuffer
= 0;
736 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
738 inBuffer
.cBuffers
= 2;
739 inBuffer
.pBuffers
= inBuffers
;
740 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
743 * Initialize these so if we fail, pvBuffer contains NULL,
744 * so we don't try to free random garbage at the quit
746 outBuffers
[0].pvBuffer
= NULL
;
747 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
748 outBuffers
[0].cbBuffer
= 0;
750 scRet
= AcceptSecurityContext(&conn
->creds
, (fInitContext
?NULL
:&conn
->context
),
751 &inBuffer
, dwSSPIFlags
, SECURITY_NATIVE_DREP
,
752 (fInitContext
?&conn
->context
:NULL
), &outBuffer
,
753 &dwSSPIOutFlags
, &tsExpiry
);
755 fInitContext
= FALSE
;
757 if (scRet
== SEC_E_OK
||
758 scRet
== SEC_I_CONTINUE_NEEDED
||
759 (FAILED(scRet
) && ((dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
) != 0)))
761 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
764 * Send response to server if there is one
766 num
= send(conn
->sock
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
768 if ((num
== SOCKET_ERROR
) || (num
== 0))
770 DEBUG_printf(("_sspiAccept: handshake send failed: %d", WSAGetLastError()));
775 DEBUG_printf(("_sspiAccept: send %d handshake bytes to client",
776 outBuffers
[0].cbBuffer
));
778 FreeContextBuffer(outBuffers
[0].pvBuffer
);
779 outBuffers
[0].pvBuffer
= NULL
;
783 if (scRet
== SEC_E_OK
)
786 * If there's extra data then save it for
787 * next time we go to decrypt
789 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
791 memcpy(conn
->decryptBuffer
,
792 (LPBYTE
) (conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
)),
793 inBuffers
[1].cbBuffer
);
794 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
798 conn
->decryptBufferUsed
= 0;
804 else if (FAILED(scRet
) && (scRet
!= SEC_E_INCOMPLETE_MESSAGE
))
806 DEBUG_printf(("_sspiAccept: AcceptSecurityContext failed: %x", scRet
));
811 if (scRet
!= SEC_E_INCOMPLETE_MESSAGE
&&
812 scRet
!= SEC_I_INCOMPLETE_CREDENTIALS
)
814 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
816 memcpy(conn
->decryptBuffer
,
817 (LPBYTE
) (conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
)),
818 inBuffers
[1].cbBuffer
);
819 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
823 conn
->decryptBufferUsed
= 0;
830 conn
->contextInitialized
= TRUE
;
833 * Find out how big the header will be:
835 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
837 if (scRet
!= SEC_E_OK
)
839 DEBUG_printf(("_sspiAccept: QueryContextAttributes failed: %x", scRet
));
851 * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
854 _sspiSetAllowsAnyRoot(_sspi_struct_t
*conn
,
855 /* I - Client connection */
857 /* I - Allow any root */
859 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_UNKNOWN_CA
:
860 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_UNKNOWN_CA
;
865 * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
868 _sspiSetAllowsExpiredCerts(_sspi_struct_t
*conn
,
869 /* I - Client connection */
871 /* I - Allow expired certs */
873 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
:
874 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
879 * '_sspiWrite()' - Write a buffer to an ssl socket
881 int /* O - Bytes written or SOCKET_ERROR */
882 _sspiWrite(_sspi_struct_t
*conn
, /* I - Client connection */
883 void *buf
, /* I - Buffer */
884 size_t len
) /* I - Buffer length */
886 SecBufferDesc message
; /* Array of SecBuffer struct */
887 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
888 BYTE
*buffer
= NULL
; /* Scratch buffer */
889 int bufferLen
; /* Buffer length */
890 size_t bytesLeft
; /* Bytes left to write */
891 int index
= 0; /* Index into buffer */
892 int num
= 0; /* Return value */
894 if (!conn
|| !buf
|| !len
)
896 WSASetLastError(WSAEINVAL
);
901 bufferLen
= conn
->streamSizes
.cbMaximumMessage
+
902 conn
->streamSizes
.cbHeader
+
903 conn
->streamSizes
.cbTrailer
;
905 buffer
= (BYTE
*) malloc(bufferLen
);
909 DEBUG_printf(("_sspiWrite: buffer alloc of %d bytes failed", bufferLen
));
910 WSASetLastError(E_OUTOFMEMORY
);
919 size_t chunk
= min(conn
->streamSizes
.cbMaximumMessage
, /* Size of data to write */
921 SECURITY_STATUS scRet
; /* SSPI status */
924 * Copy user data into the buffer, starting
925 * just past the header
927 memcpy(buffer
+ conn
->streamSizes
.cbHeader
,
928 ((BYTE
*) buf
) + index
,
932 * Setup the SSPI buffers
934 message
.ulVersion
= SECBUFFER_VERSION
;
935 message
.cBuffers
= 4;
936 message
.pBuffers
= buffers
;
937 buffers
[0].pvBuffer
= buffer
;
938 buffers
[0].cbBuffer
= conn
->streamSizes
.cbHeader
;
939 buffers
[0].BufferType
= SECBUFFER_STREAM_HEADER
;
940 buffers
[1].pvBuffer
= buffer
+ conn
->streamSizes
.cbHeader
;
941 buffers
[1].cbBuffer
= (unsigned long) chunk
;
942 buffers
[1].BufferType
= SECBUFFER_DATA
;
943 buffers
[2].pvBuffer
= buffer
+ conn
->streamSizes
.cbHeader
+ chunk
;
944 buffers
[2].cbBuffer
= conn
->streamSizes
.cbTrailer
;
945 buffers
[2].BufferType
= SECBUFFER_STREAM_TRAILER
;
946 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
951 scRet
= EncryptMessage(&conn
->context
, 0, &message
, 0);
955 DEBUG_printf(("_sspiWrite: EncryptMessage failed: %x", scRet
));
956 WSASetLastError(WSASYSCALLFAILURE
);
962 * Send the data. Remember the size of
963 * the total data to send is the size
964 * of the header, the size of the data
965 * the caller passed in and the size
968 num
= send(conn
->sock
,
970 buffers
[0].cbBuffer
+ buffers
[1].cbBuffer
+ buffers
[2].cbBuffer
,
973 if ((num
== SOCKET_ERROR
) || (num
== 0))
975 DEBUG_printf(("_sspiWrite: send failed: %ld", WSAGetLastError()));
979 bytesLeft
-= (int) chunk
;
980 index
+= (int) chunk
;
995 * '_sspiRead()' - Read a buffer from an ssl socket
997 int /* O - Bytes read or SOCKET_ERROR */
998 _sspiRead(_sspi_struct_t
*conn
, /* I - Client connection */
999 void *buf
, /* I - Buffer */
1000 size_t len
) /* I - Buffer length */
1002 SecBufferDesc message
; /* Array of SecBuffer struct */
1003 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
1004 int num
= 0; /* Return value */
1008 WSASetLastError(WSAEINVAL
);
1014 * If there are bytes that have already been
1015 * decrypted and have not yet been read, return
1018 if (buf
&& (conn
->readBufferUsed
> 0))
1020 int bytesToCopy
= (int) min(conn
->readBufferUsed
, len
); /* Amount of bytes to copy */
1021 /* from read buffer */
1023 memcpy(buf
, conn
->readBuffer
, bytesToCopy
);
1024 conn
->readBufferUsed
-= bytesToCopy
;
1026 if (conn
->readBufferUsed
> 0)
1028 * If the caller didn't request all the bytes
1029 * we have in the buffer, then move the unread
1032 memmove(conn
->readBuffer
,
1033 conn
->readBuffer
+ bytesToCopy
,
1034 conn
->readBufferUsed
);
1040 PSecBuffer pDataBuffer
; /* Data buffer */
1041 PSecBuffer pExtraBuffer
; /* Excess data buffer */
1042 SECURITY_STATUS scRet
; /* SSPI status */
1043 int i
; /* Loop control variable */
1046 * Initialize security buffer structs
1048 message
.ulVersion
= SECBUFFER_VERSION
;
1049 message
.cBuffers
= 4;
1050 message
.pBuffers
= buffers
;
1055 * If there is not enough space in the
1056 * buffer, then increase it's size
1058 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1060 conn
->decryptBufferLength
+= 4096;
1061 conn
->decryptBuffer
= (BYTE
*) realloc(conn
->decryptBuffer
,
1062 conn
->decryptBufferLength
);
1064 if (!conn
->decryptBuffer
)
1066 DEBUG_printf(("_sspiRead: unable to allocate %d byte buffer",
1067 conn
->decryptBufferLength
));
1068 WSASetLastError(E_OUTOFMEMORY
);
1074 buffers
[0].pvBuffer
= conn
->decryptBuffer
;
1075 buffers
[0].cbBuffer
= (unsigned long) conn
->decryptBufferUsed
;
1076 buffers
[0].BufferType
= SECBUFFER_DATA
;
1077 buffers
[1].BufferType
= SECBUFFER_EMPTY
;
1078 buffers
[2].BufferType
= SECBUFFER_EMPTY
;
1079 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
1081 scRet
= DecryptMessage(&conn
->context
, &message
, 0, NULL
);
1083 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1087 num
= recv(conn
->sock
,
1088 conn
->decryptBuffer
+ conn
->decryptBufferUsed
,
1089 (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
),
1091 if (num
== SOCKET_ERROR
)
1093 DEBUG_printf(("_sspiRead: recv failed: %d", WSAGetLastError()));
1098 DEBUG_printf(("_sspiRead: server disconnected"));
1102 conn
->decryptBufferUsed
+= num
;
1106 num
= (int) conn
->readBufferUsed
;
1111 while (scRet
== SEC_E_INCOMPLETE_MESSAGE
);
1113 if (scRet
== SEC_I_CONTEXT_EXPIRED
)
1115 DEBUG_printf(("_sspiRead: context expired"));
1116 WSASetLastError(WSAECONNRESET
);
1120 else if (scRet
!= SEC_E_OK
)
1122 DEBUG_printf(("_sspiRead: DecryptMessage failed: %lx", scRet
));
1123 WSASetLastError(WSASYSCALLFAILURE
);
1129 * The decryption worked. Now, locate data buffer.
1132 pExtraBuffer
= NULL
;
1133 for (i
= 1; i
< 4; i
++)
1135 if (buffers
[i
].BufferType
== SECBUFFER_DATA
)
1136 pDataBuffer
= &buffers
[i
];
1137 else if (!pExtraBuffer
&& (buffers
[i
].BufferType
== SECBUFFER_EXTRA
))
1138 pExtraBuffer
= &buffers
[i
];
1142 * If a data buffer is found, then copy
1143 * the decrypted bytes to the passed-in
1148 int bytesToCopy
= min(pDataBuffer
->cbBuffer
, (int) len
);
1149 /* Number of bytes to copy into buf */
1150 int bytesToSave
= pDataBuffer
->cbBuffer
- bytesToCopy
;
1151 /* Number of bytes to save in our read buffer */
1154 memcpy(buf
, pDataBuffer
->pvBuffer
, bytesToCopy
);
1157 * If there are more decrypted bytes than can be
1158 * copied to the passed in buffer, then save them
1162 if ((int)(conn
->readBufferLength
- conn
->readBufferUsed
) < bytesToSave
)
1164 conn
->readBufferLength
= conn
->readBufferUsed
+ bytesToSave
;
1165 conn
->readBuffer
= realloc(conn
->readBuffer
,
1166 conn
->readBufferLength
);
1168 if (!conn
->readBuffer
)
1170 DEBUG_printf(("_sspiRead: unable to allocate %d bytes", conn
->readBufferLength
));
1171 WSASetLastError(E_OUTOFMEMORY
);
1177 memcpy(((BYTE
*) conn
->readBuffer
) + conn
->readBufferUsed
,
1178 ((BYTE
*) pDataBuffer
->pvBuffer
) + bytesToCopy
,
1181 conn
->readBufferUsed
+= bytesToSave
;
1184 num
= (buf
) ? bytesToCopy
: (int) conn
->readBufferUsed
;
1188 DEBUG_printf(("_sspiRead: unable to find data buffer"));
1189 WSASetLastError(WSASYSCALLFAILURE
);
1195 * If the decryption process left extra bytes,
1196 * then save those back in decryptBuffer. They will
1197 * be processed the next time through the loop.
1201 memmove(conn
->decryptBuffer
, pExtraBuffer
->pvBuffer
, pExtraBuffer
->cbBuffer
);
1202 conn
->decryptBufferUsed
= pExtraBuffer
->cbBuffer
;
1206 conn
->decryptBufferUsed
= 0;
1217 * '_sspiPending()' - Returns the number of available bytes
1219 int /* O - Number of available bytes */
1220 _sspiPending(_sspi_struct_t
*conn
) /* I - Client connection */
1222 return (_sspiRead(conn
, NULL
, 0));
1227 * '_sspiFree()' - Close a connection and free resources
1230 _sspiFree(_sspi_struct_t
*conn
) /* I - Client connection */
1235 if (conn
->contextInitialized
)
1237 SecBufferDesc message
; /* Array of SecBuffer struct */
1238 SecBuffer buffers
[1] = { 0 };
1239 /* Security package buffer */
1240 DWORD dwType
; /* Type */
1241 DWORD status
; /* Status */
1244 * Notify schannel that we are about to close the connection.
1246 dwType
= SCHANNEL_SHUTDOWN
;
1248 buffers
[0].pvBuffer
= &dwType
;
1249 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
1250 buffers
[0].cbBuffer
= sizeof(dwType
);
1252 message
.cBuffers
= 1;
1253 message
.pBuffers
= buffers
;
1254 message
.ulVersion
= SECBUFFER_VERSION
;
1256 status
= ApplyControlToken(&conn
->context
, &message
);
1258 if (SUCCEEDED(status
))
1260 PBYTE pbMessage
; /* Message buffer */
1261 DWORD cbMessage
; /* Message buffer count */
1262 DWORD cbData
; /* Data count */
1263 DWORD dwSSPIFlags
; /* SSL attributes we requested */
1264 DWORD dwSSPIOutFlags
; /* SSL attributes we received */
1265 TimeStamp tsExpiry
; /* Time stamp */
1267 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
1268 ASC_REQ_REPLAY_DETECT
|
1269 ASC_REQ_CONFIDENTIALITY
|
1270 ASC_REQ_EXTENDED_ERROR
|
1271 ASC_REQ_ALLOCATE_MEMORY
|
1274 buffers
[0].pvBuffer
= NULL
;
1275 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
1276 buffers
[0].cbBuffer
= 0;
1278 message
.cBuffers
= 1;
1279 message
.pBuffers
= buffers
;
1280 message
.ulVersion
= SECBUFFER_VERSION
;
1282 status
= AcceptSecurityContext(&conn
->creds
, &conn
->context
, NULL
,
1283 dwSSPIFlags
, SECURITY_NATIVE_DREP
, NULL
,
1284 &message
, &dwSSPIOutFlags
, &tsExpiry
);
1286 if (SUCCEEDED(status
))
1288 pbMessage
= buffers
[0].pvBuffer
;
1289 cbMessage
= buffers
[0].cbBuffer
;
1292 * Send the close notify message to the client.
1294 if (pbMessage
&& cbMessage
)
1296 cbData
= send(conn
->sock
, pbMessage
, cbMessage
, 0);
1297 if ((cbData
== SOCKET_ERROR
) || (cbData
== 0))
1299 status
= WSAGetLastError();
1300 DEBUG_printf(("_sspiFree: sending close notify failed: %d", status
));
1304 FreeContextBuffer(pbMessage
);
1310 DEBUG_printf(("_sspiFree: AcceptSecurityContext failed: %x", status
));
1315 DEBUG_printf(("_sspiFree: ApplyControlToken failed: %x", status
));
1318 DeleteSecurityContext(&conn
->context
);
1319 conn
->contextInitialized
= FALSE
;
1322 if (conn
->decryptBuffer
)
1324 free(conn
->decryptBuffer
);
1325 conn
->decryptBuffer
= NULL
;
1328 if (conn
->readBuffer
)
1330 free(conn
->readBuffer
);
1331 conn
->readBuffer
= NULL
;
1334 if (conn
->sock
!= INVALID_SOCKET
)
1336 closesocket(conn
->sock
);
1337 conn
->sock
= INVALID_SOCKET
;
1345 * 'sspi_verify_certificate()' - Verify a server certificate
1347 static DWORD
/* 0 - Error code (0 == No error) */
1348 sspi_verify_certificate(PCCERT_CONTEXT serverCert
,
1349 /* I - Server certificate */
1350 const CHAR
*serverName
,
1351 /* I - Server name */
1353 /* I - Verification flags */
1355 HTTPSPolicyCallbackData httpsPolicy
;
1356 /* HTTPS Policy Struct */
1357 CERT_CHAIN_POLICY_PARA policyPara
;
1358 /* Cert chain policy parameters */
1359 CERT_CHAIN_POLICY_STATUS policyStatus
;
1360 /* Cert chain policy status */
1361 CERT_CHAIN_PARA chainPara
;
1362 /* Used for searching and matching criteria */
1363 PCCERT_CHAIN_CONTEXT chainContext
= NULL
;
1364 /* Certificate chain */
1365 PWSTR serverNameUnicode
= NULL
;
1366 /* Unicode server name */
1367 LPSTR rgszUsages
[] = { szOID_PKIX_KP_SERVER_AUTH
,
1368 szOID_SERVER_GATED_CRYPTO
,
1369 szOID_SGC_NETSCAPE
};
1370 /* How are we using this certificate? */
1371 DWORD cUsages
= sizeof(rgszUsages
) / sizeof(LPSTR
);
1372 /* Number of ites in rgszUsages */
1373 DWORD count
; /* 32 bit count variable */
1374 DWORD status
; /* Return value */
1378 status
= SEC_E_WRONG_PRINCIPAL
;
1383 * Convert server name to unicode.
1385 if (!serverName
|| (strlen(serverName
) == 0))
1387 status
= SEC_E_WRONG_PRINCIPAL
;
1391 count
= MultiByteToWideChar(CP_ACP
, 0, serverName
, -1, NULL
, 0);
1392 serverNameUnicode
= LocalAlloc(LMEM_FIXED
, count
* sizeof(WCHAR
));
1393 if (!serverNameUnicode
)
1395 status
= SEC_E_INSUFFICIENT_MEMORY
;
1398 count
= MultiByteToWideChar(CP_ACP
, 0, serverName
, -1, serverNameUnicode
, count
);
1401 status
= SEC_E_WRONG_PRINCIPAL
;
1406 * Build certificate chain.
1408 ZeroMemory(&chainPara
, sizeof(chainPara
));
1409 chainPara
.cbSize
= sizeof(chainPara
);
1410 chainPara
.RequestedUsage
.dwType
= USAGE_MATCH_TYPE_OR
;
1411 chainPara
.RequestedUsage
.Usage
.cUsageIdentifier
= cUsages
;
1412 chainPara
.RequestedUsage
.Usage
.rgpszUsageIdentifier
= rgszUsages
;
1414 if (!CertGetCertificateChain(NULL
, serverCert
, NULL
, serverCert
->hCertStore
,
1415 &chainPara
, 0, NULL
, &chainContext
))
1417 status
= GetLastError();
1418 DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status
));
1423 * Validate certificate chain.
1425 ZeroMemory(&httpsPolicy
, sizeof(HTTPSPolicyCallbackData
));
1426 httpsPolicy
.cbStruct
= sizeof(HTTPSPolicyCallbackData
);
1427 httpsPolicy
.dwAuthType
= AUTHTYPE_SERVER
;
1428 httpsPolicy
.fdwChecks
= dwCertFlags
;
1429 httpsPolicy
.pwszServerName
= serverNameUnicode
;
1431 memset(&policyPara
, 0, sizeof(policyPara
));
1432 policyPara
.cbSize
= sizeof(policyPara
);
1433 policyPara
.pvExtraPolicyPara
= &httpsPolicy
;
1435 memset(&policyStatus
, 0, sizeof(policyStatus
));
1436 policyStatus
.cbSize
= sizeof(policyStatus
);
1438 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL
, chainContext
,
1439 &policyPara
, &policyStatus
))
1441 status
= GetLastError();
1442 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status
));
1446 if (policyStatus
.dwError
)
1448 status
= policyStatus
.dwError
;
1457 CertFreeCertificateChain(chainContext
);
1459 if (serverNameUnicode
)
1460 LocalFree(serverNameUnicode
);
1467 * End of "$Id: sspi.c 11760 2014-03-28 12:58:24Z msweet $".