4 * TLS support for CUPS on Windows using SSPI.
6 * Copyright 2010-2013 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/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * Include necessary headers...
21 #include "sspi-private.h"
22 #include "debug-private.h"
25 /* required to link this library for certificate functions */
26 #pragma comment(lib, "Crypt32.lib")
27 #pragma comment(lib, "Secur32.lib")
28 #pragma comment(lib, "Ws2_32.lib")
31 #if !defined(SECURITY_FLAG_IGNORE_UNKNOWN_CA)
32 # define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100 /* Untrusted root */
35 #if !defined(SECURITY_FLAG_IGNORE_CERT_DATE_INVALID)
36 # define SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 0x00002000 /* Expired X509 Cert. */
39 static DWORD
sspi_verify_certificate(PCCERT_CONTEXT serverCert
,
40 const CHAR
*serverName
,
45 * 'http_tls_initialize()' - Initialize the TLS stack.
49 http_tls_initialize(void)
53 * Initialize GNU TLS...
58 #elif defined(HAVE_LIBSSL)
60 * Initialize OpenSSL...
63 SSL_load_error_strings();
67 * Using the current time is a dubious random seed, but on some systems
68 * it is the best we can do (on others, this seed isn't even used...)
71 CUPS_SRAND(time(NULL
));
73 for (i
= 0; i
< sizeof(data
); i
++)
74 data
[i
] = CUPS_RAND();
76 RAND_seed(data
, sizeof(data
));
77 #endif /* HAVE_GNUTLS */
83 * 'http_tls_read()' - Read from a SSL/TLS connection.
86 static int /* O - Bytes read */
87 http_tls_read(http_t
*http
, /* I - Connection to server */
88 char *buf
, /* I - Buffer to store data */
89 int len
) /* I - Length of buffer */
91 # if defined(HAVE_LIBSSL)
92 return (SSL_read((SSL
*)(http
->tls
), buf
, len
));
94 # elif defined(HAVE_GNUTLS)
95 ssize_t result
; /* Return value */
98 result
= gnutls_record_recv(http
->tls
, buf
, len
);
100 if (result
< 0 && !errno
)
103 * Convert GNU TLS error to errno value...
108 case GNUTLS_E_INTERRUPTED
:
112 case GNUTLS_E_AGAIN
:
124 return ((int)result
);
126 # elif defined(HAVE_CDSASSL)
127 int result
; /* Return value */
128 OSStatus error
; /* Error info */
129 size_t processed
; /* Number of bytes processed */
132 error
= SSLRead(http
->tls
, buf
, len
, &processed
);
133 DEBUG_printf(("6http_tls_read: error=%d, processed=%d", (int)error
,
138 result
= (int)processed
;
141 case errSSLWouldBlock
:
143 result
= (int)processed
;
151 case errSSLClosedGraceful
:
154 result
= (int)processed
;
165 # elif defined(HAVE_SSPISSL)
166 return _sspiRead((_sspi_struct_t
*) http
->tls
, buf
, len
);
167 # endif /* HAVE_LIBSSL */
169 #endif /* HAVE_SSL */
174 * 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
177 static int /* O - 0 on success, -1 on failure */
178 http_setup_ssl(http_t
*http
) /* I - Connection to server */
180 char hostname
[256], /* Hostname */
181 *hostptr
; /* Pointer into hostname */
184 SSL_CTX
*context
; /* Context for encryption */
185 BIO
*bio
; /* BIO data */
186 const char *message
= NULL
;/* Error message */
187 # elif defined(HAVE_GNUTLS)
188 int status
; /* Status of handshake */
189 gnutls_certificate_client_credentials
*credentials
;
190 /* TLS credentials */
191 # elif defined(HAVE_CDSASSL)
192 _cups_globals_t
*cg
= _cupsGlobals();
193 /* Pointer to library globals */
194 OSStatus error
; /* Error code */
195 const char *message
= NULL
;/* Error message */
196 cups_array_t
*credentials
; /* Credentials array */
197 cups_array_t
*names
; /* CUPS distinguished names */
198 CFArrayRef dn_array
; /* CF distinguished names array */
199 CFIndex count
; /* Number of credentials */
200 CFDataRef data
; /* Certificate data */
201 int i
; /* Looping var */
202 http_credential_t
*credential
; /* Credential data */
203 # elif defined(HAVE_SSPISSL)
204 TCHAR username
[256]; /* Username returned from GetUserName() */
205 TCHAR commonName
[256];/* Common name for certificate */
206 DWORD dwSize
; /* 32 bit size */
207 # endif /* HAVE_LIBSSL */
210 DEBUG_printf(("7http_setup_ssl(http=%p)", http
));
213 * Get the hostname to use for SSL...
216 if (httpAddrLocalhost(http
->hostaddr
))
218 strlcpy(hostname
, "localhost", sizeof(hostname
));
223 * Otherwise make sure the hostname we have does not end in a trailing dot.
226 strlcpy(hostname
, http
->hostname
, sizeof(hostname
));
227 if ((hostptr
= hostname
+ strlen(hostname
) - 1) >= hostname
&&
233 context
= SSL_CTX_new(SSLv23_client_method());
235 SSL_CTX_set_options(context
, SSL_OP_NO_SSLv2
); /* Only use SSLv3 or TLS */
237 bio
= BIO_new(_httpBIOMethods());
238 BIO_ctrl(bio
, BIO_C_SET_FILE_PTR
, 0, (char *)http
);
240 http
->tls
= SSL_new(context
);
241 SSL_set_bio(http
->tls
, bio
, bio
);
243 # ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
244 SSL_set_tlsext_host_name(http
->tls
, hostname
);
245 # endif /* HAVE_SSL_SET_TLSEXT_HOST_NAME */
247 if (SSL_connect(http
->tls
) != 1)
249 unsigned long error
; /* Error code */
251 while ((error
= ERR_get_error()) != 0)
253 message
= ERR_error_string(error
, NULL
);
254 DEBUG_printf(("8http_setup_ssl: %s", message
));
257 SSL_CTX_free(context
);
262 http
->error
= WSAGetLastError();
266 http
->status
= HTTP_STATUS_ERROR
;
269 message
= _("Unable to establish a secure connection to host.");
271 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
, message
, 1);
276 # elif defined(HAVE_GNUTLS)
277 credentials
= (gnutls_certificate_client_credentials
*)
278 malloc(sizeof(gnutls_certificate_client_credentials
));
279 if (credentials
== NULL
)
281 DEBUG_printf(("8http_setup_ssl: Unable to allocate credentials: %s",
284 http
->status
= HTTP_STATUS_ERROR
;
285 _cupsSetHTTPError(HTTP_STATUS_ERROR
);
290 gnutls_certificate_allocate_credentials(credentials
);
292 gnutls_init(&http
->tls
, GNUTLS_CLIENT
);
293 gnutls_set_default_priority(http
->tls
);
294 gnutls_server_name_set(http
->tls
, GNUTLS_NAME_DNS
, hostname
,
296 gnutls_credentials_set(http
->tls
, GNUTLS_CRD_CERTIFICATE
, *credentials
);
297 gnutls_transport_set_ptr(http
->tls
, (gnutls_transport_ptr
)http
);
298 gnutls_transport_set_pull_function(http
->tls
, _httpReadGNUTLS
);
299 gnutls_transport_set_push_function(http
->tls
, _httpWriteGNUTLS
);
301 while ((status
= gnutls_handshake(http
->tls
)) != GNUTLS_E_SUCCESS
)
303 DEBUG_printf(("8http_setup_ssl: gnutls_handshake returned %d (%s)",
304 status
, gnutls_strerror(status
)));
306 if (gnutls_error_is_fatal(status
))
309 http
->status
= HTTP_STATUS_ERROR
;
311 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
, gnutls_strerror(status
), 0);
313 gnutls_deinit(http
->tls
);
314 gnutls_certificate_free_credentials(*credentials
);
322 http
->tls_credentials
= credentials
;
324 # elif defined(HAVE_CDSASSL)
325 if ((http
->tls
= SSLCreateContext(kCFAllocatorDefault
, kSSLClientSide
,
326 kSSLStreamType
)) == NULL
)
328 DEBUG_puts("4http_setup_ssl: SSLCreateContext failed.");
329 http
->error
= errno
= ENOMEM
;
330 http
->status
= HTTP_STATUS_ERROR
;
331 _cupsSetHTTPError(HTTP_STATUS_ERROR
);
336 error
= SSLSetConnection(http
->tls
, http
);
337 DEBUG_printf(("4http_setup_ssl: SSLSetConnection, error=%d", (int)error
));
341 error
= SSLSetIOFuncs(http
->tls
, _httpReadCDSA
, _httpWriteCDSA
);
342 DEBUG_printf(("4http_setup_ssl: SSLSetIOFuncs, error=%d", (int)error
));
347 error
= SSLSetSessionOption(http
->tls
, kSSLSessionOptionBreakOnServerAuth
,
349 DEBUG_printf(("4http_setup_ssl: SSLSetSessionOption, error=%d",
355 if (cg
->client_cert_cb
)
357 error
= SSLSetSessionOption(http
->tls
,
358 kSSLSessionOptionBreakOnCertRequested
, true);
359 DEBUG_printf(("4http_setup_ssl: kSSLSessionOptionBreakOnCertRequested, "
360 "error=%d", (int)error
));
364 error
= http_set_credentials(http
);
365 DEBUG_printf(("4http_setup_ssl: http_set_credentials, error=%d",
371 * Let the server know which hostname/domain we are trying to connect to
372 * in case it wants to serve up a certificate with a matching common name.
377 error
= SSLSetPeerDomainName(http
->tls
, hostname
, strlen(hostname
));
379 DEBUG_printf(("4http_setup_ssl: SSLSetPeerDomainName, error=%d",
385 int done
= 0; /* Are we done yet? */
387 while (!error
&& !done
)
389 error
= SSLHandshake(http
->tls
);
391 DEBUG_printf(("4http_setup_ssl: SSLHandshake returned %d.", (int)error
));
399 case errSSLWouldBlock
:
400 error
= noErr
; /* Force a retry */
401 usleep(1000); /* in 1 millisecond */
404 case errSSLServerAuthCompleted
:
406 if (cg
->server_cert_cb
)
408 error
= httpCopyCredentials(http
, &credentials
);
411 error
= (cg
->server_cert_cb
)(http
, http
->tls
, credentials
,
412 cg
->server_cert_data
);
413 httpFreeCredentials(credentials
);
416 DEBUG_printf(("4http_setup_ssl: Server certificate callback "
417 "returned %d.", (int)error
));
421 case errSSLClientCertRequested
:
424 if (cg
->client_cert_cb
)
427 if (!(error
= SSLCopyDistinguishedNames(http
->tls
, &dn_array
)) &&
430 if ((names
= cupsArrayNew(NULL
, NULL
)) != NULL
)
432 for (i
= 0, count
= CFArrayGetCount(dn_array
); i
< count
; i
++)
434 data
= (CFDataRef
)CFArrayGetValueAtIndex(dn_array
, i
);
436 if ((credential
= malloc(sizeof(*credential
))) != NULL
)
438 credential
->datalen
= CFDataGetLength(data
);
439 if ((credential
->data
= malloc(credential
->datalen
)))
441 memcpy((void *)credential
->data
, CFDataGetBytePtr(data
),
442 credential
->datalen
);
443 cupsArrayAdd(names
, credential
);
456 error
= (cg
->client_cert_cb
)(http
, http
->tls
, names
,
457 cg
->client_cert_data
);
459 DEBUG_printf(("4http_setup_ssl: Client certificate callback "
460 "returned %d.", (int)error
));
463 httpFreeCredentials(names
);
467 case errSSLUnknownRootCert
:
468 message
= _("Unable to establish a secure connection to host "
469 "(untrusted certificate).");
472 case errSSLNoRootCert
:
473 message
= _("Unable to establish a secure connection to host "
474 "(self-signed certificate).");
477 case errSSLCertExpired
:
478 message
= _("Unable to establish a secure connection to host "
479 "(expired certificate).");
482 case errSSLCertNotYetValid
:
483 message
= _("Unable to establish a secure connection to host "
484 "(certificate not yet valid).");
487 case errSSLHostNameMismatch
:
488 message
= _("Unable to establish a secure connection to host "
489 "(host name mismatch).");
492 case errSSLXCertChainInvalid
:
493 message
= _("Unable to establish a secure connection to host "
494 "(certificate chain invalid).");
497 case errSSLConnectionRefused
:
498 message
= _("Unable to establish a secure connection to host "
499 "(peer dropped connection before responding).");
511 http
->status
= HTTP_STATUS_ERROR
;
512 errno
= ECONNREFUSED
;
514 CFRelease(http
->tls
);
518 * If an error string wasn't set by the callbacks use a generic one...
522 #ifdef HAVE_CSSMERRORSTRING
523 message
= cssmErrorString(error
);
525 message
= _("Unable to establish a secure connection to host.");
526 #endif /* HAVE_CSSMERRORSTRING */
528 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
, message
, 1);
533 # elif defined(HAVE_SSPISSL)
534 http
->tls
= _sspiAlloc();
538 _cupsSetHTTPError(HTTP_STATUS_ERROR
);
542 http
->tls
->sock
= http
->fd
;
543 dwSize
= sizeof(username
) / sizeof(TCHAR
);
544 GetUserName(username
, &dwSize
);
545 _sntprintf_s(commonName
, sizeof(commonName
) / sizeof(TCHAR
),
546 sizeof(commonName
) / sizeof(TCHAR
), TEXT("CN=%s"), username
);
548 if (!_sspiGetCredentials(http
->tls
, L
"ClientContainer",
551 _sspiFree(http
->tls
);
555 http
->status
= HTTP_STATUS_ERROR
;
557 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
558 _("Unable to establish a secure connection to host."), 1);
563 _sspiSetAllowsAnyRoot(http
->tls
, TRUE
);
564 _sspiSetAllowsExpiredCerts(http
->tls
, TRUE
);
566 if (!_sspiConnect(http
->tls
, hostname
))
568 _sspiFree(http
->tls
);
572 http
->status
= HTTP_STATUS_ERROR
;
574 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI
,
575 _("Unable to establish a secure connection to host."), 1);
579 # endif /* HAVE_CDSASSL */
586 * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection.
590 http_shutdown_ssl(http_t
*http
) /* I - Connection to server */
593 SSL_CTX
*context
; /* Context for encryption */
595 context
= SSL_get_SSL_CTX(http
->tls
);
597 SSL_shutdown(http
->tls
);
598 SSL_CTX_free(context
);
601 # elif defined(HAVE_GNUTLS)
602 gnutls_certificate_client_credentials
*credentials
;
603 /* TLS credentials */
605 credentials
= (gnutls_certificate_client_credentials
*)(http
->tls_credentials
);
607 gnutls_bye(http
->tls
, GNUTLS_SHUT_RDWR
);
608 gnutls_deinit(http
->tls
);
609 gnutls_certificate_free_credentials(*credentials
);
612 # elif defined(HAVE_CDSASSL)
613 while (SSLClose(http
->tls
) == errSSLWouldBlock
)
616 CFRelease(http
->tls
);
618 if (http
->tls_credentials
)
619 CFRelease(http
->tls_credentials
);
621 # elif defined(HAVE_SSPISSL)
622 _sspiFree(http
->tls
);
623 # endif /* HAVE_LIBSSL */
626 http
->tls_credentials
= NULL
;
628 #endif /* HAVE_SSL */
633 * 'http_write_ssl()' - Write to a SSL/TLS connection.
636 static int /* O - Bytes written */
637 http_write_ssl(http_t
*http
, /* I - Connection to server */
638 const char *buf
, /* I - Buffer holding data */
639 int len
) /* I - Length of buffer */
641 ssize_t result
; /* Return value */
644 DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http
, buf
, len
));
646 # if defined(HAVE_LIBSSL)
647 result
= SSL_write((SSL
*)(http
->tls
), buf
, len
);
649 # elif defined(HAVE_GNUTLS)
650 result
= gnutls_record_send(http
->tls
, buf
, len
);
652 if (result
< 0 && !errno
)
655 * Convert GNU TLS error to errno value...
660 case GNUTLS_E_INTERRUPTED
:
664 case GNUTLS_E_AGAIN
:
676 # elif defined(HAVE_CDSASSL)
677 OSStatus error
; /* Error info */
678 size_t processed
; /* Number of bytes processed */
681 error
= SSLWrite(http
->tls
, buf
, len
, &processed
);
686 result
= (int)processed
;
689 case errSSLWouldBlock
:
691 result
= (int)processed
;
699 case errSSLClosedGraceful
:
702 result
= (int)processed
;
710 # elif defined(HAVE_SSPISSL)
711 return _sspiWrite((_sspi_struct_t
*)http
->tls
, (void *)buf
, len
);
712 # endif /* HAVE_LIBSSL */
714 DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result
));
716 return ((int)result
);
718 #endif /* HAVE_SSL */
723 * 'http_write_ssl()' - Write to a SSL/TLS connection.
726 static int /* O - Bytes written */
727 http_write_ssl(http_t
*http
, /* I - Connection to server */
728 const char *buf
, /* I - Buffer holding data */
729 int len
) /* I - Length of buffer */
731 ssize_t result
; /* Return value */
734 DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http
, buf
, len
));
736 # if defined(HAVE_LIBSSL)
737 result
= SSL_write((SSL
*)(http
->tls
), buf
, len
);
739 # elif defined(HAVE_GNUTLS)
740 result
= gnutls_record_send(http
->tls
, buf
, len
);
742 if (result
< 0 && !errno
)
745 * Convert GNU TLS error to errno value...
750 case GNUTLS_E_INTERRUPTED
:
754 case GNUTLS_E_AGAIN
:
766 # elif defined(HAVE_CDSASSL)
767 OSStatus error
; /* Error info */
768 size_t processed
; /* Number of bytes processed */
771 error
= SSLWrite(http
->tls
, buf
, len
, &processed
);
776 result
= (int)processed
;
779 case errSSLWouldBlock
:
781 result
= (int)processed
;
789 case errSSLClosedGraceful
:
792 result
= (int)processed
;
800 # elif defined(HAVE_SSPISSL)
801 return _sspiWrite((_sspi_struct_t
*)http
->tls
, (void *)buf
, len
);
802 # endif /* HAVE_LIBSSL */
804 DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result
));
806 return ((int)result
);
808 #endif /* HAVE_SSL */
812 * 'sspi_alloc()' - Allocate SSPI ssl object
814 _sspi_struct_t
* /* O - New SSPI/SSL object */
817 _sspi_struct_t
*conn
= calloc(sizeof(_sspi_struct_t
), 1);
820 conn
->sock
= INVALID_SOCKET
;
827 * '_sspiGetCredentials()' - Retrieve an SSL/TLS certificate from the system store
828 * If one cannot be found, one is created.
830 BOOL
/* O - 1 on success, 0 on failure */
831 _sspiGetCredentials(_sspi_struct_t
*conn
,
832 /* I - Client connection */
833 const LPWSTR container
,
834 /* I - Cert container name */
835 const TCHAR
*cn
, /* I - Common name of certificate */
837 /* I - Is caller a server? */
839 HCERTSTORE store
= NULL
; /* Certificate store */
840 PCCERT_CONTEXT storedContext
= NULL
;
841 /* Context created from the store */
842 PCCERT_CONTEXT createdContext
= NULL
;
843 /* Context created by us */
844 DWORD dwSize
= 0; /* 32 bit size */
845 PBYTE p
= NULL
; /* Temporary storage */
846 HCRYPTPROV hProv
= (HCRYPTPROV
) NULL
;
847 /* Handle to a CSP */
848 CERT_NAME_BLOB sib
; /* Arbitrary array of bytes */
849 SCHANNEL_CRED SchannelCred
; /* Schannel credential data */
850 TimeStamp tsExpiry
; /* Time stamp */
851 SECURITY_STATUS Status
; /* Status */
852 HCRYPTKEY hKey
= (HCRYPTKEY
) NULL
;
853 /* Handle to crypto key */
854 CRYPT_KEY_PROV_INFO kpi
; /* Key container info */
855 SYSTEMTIME et
; /* System time */
856 CERT_EXTENSIONS exts
; /* Array of cert extensions */
857 CRYPT_KEY_PROV_INFO ckp
; /* Handle to crypto key */
858 BOOL ok
= TRUE
; /* Return value */
865 if (!CryptAcquireContextW(&hProv
, (LPWSTR
) container
, MS_DEF_PROV_W
,
867 CRYPT_NEWKEYSET
| CRYPT_MACHINE_KEYSET
))
869 if (GetLastError() == NTE_EXISTS
)
871 if (!CryptAcquireContextW(&hProv
, (LPWSTR
) container
, MS_DEF_PROV_W
,
872 PROV_RSA_FULL
, CRYPT_MACHINE_KEYSET
))
874 DEBUG_printf(("_sspiGetCredentials: CryptAcquireContext failed: %x\n",
882 store
= CertOpenStore(CERT_STORE_PROV_SYSTEM
,
883 X509_ASN_ENCODING
|PKCS_7_ASN_ENCODING
,
885 CERT_SYSTEM_STORE_LOCAL_MACHINE
|
886 CERT_STORE_NO_CRYPT_RELEASE_FLAG
|
887 CERT_STORE_OPEN_EXISTING_FLAG
,
892 DEBUG_printf(("_sspiGetCredentials: CertOpenSystemStore failed: %x\n",
900 if (!CertStrToName(X509_ASN_ENCODING
, cn
, CERT_OID_NAME_STR
,
901 NULL
, NULL
, &dwSize
, NULL
))
903 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x\n",
909 p
= (PBYTE
) malloc(dwSize
);
913 DEBUG_printf(("_sspiGetCredentials: malloc failed for %d bytes", dwSize
));
918 if (!CertStrToName(X509_ASN_ENCODING
, cn
, CERT_OID_NAME_STR
, NULL
,
921 DEBUG_printf(("_sspiGetCredentials: CertStrToName failed: %x",
930 storedContext
= CertFindCertificateInStore(store
, X509_ASN_ENCODING
|PKCS_7_ASN_ENCODING
,
931 0, CERT_FIND_SUBJECT_NAME
, &sib
, NULL
);
936 * If we couldn't find the context, then we'll
939 if (!CryptGenKey(hProv
, AT_KEYEXCHANGE
, CRYPT_EXPORTABLE
, &hKey
))
941 DEBUG_printf(("_sspiGetCredentials: CryptGenKey failed: %x",
947 ZeroMemory(&kpi
, sizeof(kpi
));
948 kpi
.pwszContainerName
= (LPWSTR
) container
;
949 kpi
.pwszProvName
= MS_DEF_PROV_W
;
950 kpi
.dwProvType
= PROV_RSA_FULL
;
951 kpi
.dwFlags
= CERT_SET_KEY_CONTEXT_PROP_ID
;
952 kpi
.dwKeySpec
= AT_KEYEXCHANGE
;
957 ZeroMemory(&exts
, sizeof(exts
));
959 createdContext
= CertCreateSelfSignCertificate(hProv
, &sib
, 0, &kpi
, NULL
, NULL
,
964 DEBUG_printf(("_sspiGetCredentials: CertCreateSelfSignCertificate failed: %x",
970 if (!CertAddCertificateContextToStore(store
, createdContext
,
971 CERT_STORE_ADD_REPLACE_EXISTING
,
974 DEBUG_printf(("_sspiGetCredentials: CertAddCertificateContextToStore failed: %x",
980 ZeroMemory(&ckp
, sizeof(ckp
));
981 ckp
.pwszContainerName
= (LPWSTR
) container
;
982 ckp
.pwszProvName
= MS_DEF_PROV_W
;
983 ckp
.dwProvType
= PROV_RSA_FULL
;
984 ckp
.dwFlags
= CRYPT_MACHINE_KEYSET
;
985 ckp
.dwKeySpec
= AT_KEYEXCHANGE
;
987 if (!CertSetCertificateContextProperty(storedContext
,
988 CERT_KEY_PROV_INFO_PROP_ID
,
991 DEBUG_printf(("_sspiGetCredentials: CertSetCertificateContextProperty failed: %x",
998 ZeroMemory(&SchannelCred
, sizeof(SchannelCred
));
1000 SchannelCred
.dwVersion
= SCHANNEL_CRED_VERSION
;
1001 SchannelCred
.cCreds
= 1;
1002 SchannelCred
.paCred
= &storedContext
;
1005 * SSPI doesn't seem to like it if grbitEnabledProtocols
1006 * is set for a client
1009 SchannelCred
.grbitEnabledProtocols
= SP_PROT_SSL3TLS1
;
1012 * Create an SSPI credential.
1014 Status
= AcquireCredentialsHandle(NULL
, UNISP_NAME
,
1015 isServer
? SECPKG_CRED_INBOUND
:SECPKG_CRED_OUTBOUND
,
1016 NULL
, &SchannelCred
, NULL
, NULL
, &conn
->creds
,
1018 if (Status
!= SEC_E_OK
)
1020 DEBUG_printf(("_sspiGetCredentials: AcquireCredentialsHandle failed: %x", Status
));
1031 CryptDestroyKey(hKey
);
1034 CertFreeCertificateContext(createdContext
);
1037 CertFreeCertificateContext(storedContext
);
1043 CertCloseStore(store
, 0);
1046 CryptReleaseContext(hProv
, 0);
1053 * '_sspiConnect()' - Make an SSL connection. This function
1054 * assumes a TCP/IP connection has already
1055 * been successfully made
1057 BOOL
/* O - 1 on success, 0 on failure */
1058 _sspiConnect(_sspi_struct_t
*conn
, /* I - Client connection */
1059 const CHAR
*hostname
) /* I - Server hostname */
1061 PCCERT_CONTEXT serverCert
; /* Server certificate */
1062 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1063 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1064 TimeStamp tsExpiry
; /* Time stamp */
1065 SECURITY_STATUS scRet
; /* Status */
1066 DWORD cbData
; /* Data count */
1067 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1068 SecBuffer inBuffers
[2]; /* Security package buffer */
1069 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1070 SecBuffer outBuffers
[1]; /* Security package buffer */
1071 BOOL ok
= TRUE
; /* Return value */
1075 dwSSPIFlags
= ISC_REQ_SEQUENCE_DETECT
|
1076 ISC_REQ_REPLAY_DETECT
|
1077 ISC_REQ_CONFIDENTIALITY
|
1078 ISC_RET_EXTENDED_ERROR
|
1079 ISC_REQ_ALLOCATE_MEMORY
|
1083 * Initiate a ClientHello message and generate a token.
1085 outBuffers
[0].pvBuffer
= NULL
;
1086 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1087 outBuffers
[0].cbBuffer
= 0;
1089 outBuffer
.cBuffers
= 1;
1090 outBuffer
.pBuffers
= outBuffers
;
1091 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1093 scRet
= InitializeSecurityContext(&conn
->creds
, NULL
, TEXT(""), dwSSPIFlags
,
1094 0, SECURITY_NATIVE_DREP
, NULL
, 0, &conn
->context
,
1095 &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1097 if (scRet
!= SEC_I_CONTINUE_NEEDED
)
1099 DEBUG_printf(("_sspiConnect: InitializeSecurityContext(1) failed: %x", scRet
));
1105 * Send response to server if there is one.
1107 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1109 cbData
= send(conn
->sock
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1111 if ((cbData
== SOCKET_ERROR
) || !cbData
)
1113 DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
1114 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1115 DeleteSecurityContext(&conn
->context
);
1120 DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData
));
1123 * Free output buffer.
1125 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1126 outBuffers
[0].pvBuffer
= NULL
;
1129 dwSSPIFlags
= ISC_REQ_MANUAL_CRED_VALIDATION
|
1130 ISC_REQ_SEQUENCE_DETECT
|
1131 ISC_REQ_REPLAY_DETECT
|
1132 ISC_REQ_CONFIDENTIALITY
|
1133 ISC_RET_EXTENDED_ERROR
|
1134 ISC_REQ_ALLOCATE_MEMORY
|
1137 conn
->decryptBufferUsed
= 0;
1140 * Loop until the handshake is finished or an error occurs.
1142 scRet
= SEC_I_CONTINUE_NEEDED
;
1144 while(scRet
== SEC_I_CONTINUE_NEEDED
||
1145 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1146 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1148 if ((conn
->decryptBufferUsed
== 0) || (scRet
== SEC_E_INCOMPLETE_MESSAGE
))
1150 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1152 conn
->decryptBufferLength
+= 4096;
1153 conn
->decryptBuffer
= (BYTE
*) realloc(conn
->decryptBuffer
, conn
->decryptBufferLength
);
1155 if (!conn
->decryptBuffer
)
1157 DEBUG_printf(("_sspiConnect: unable to allocate %d byte decrypt buffer",
1158 conn
->decryptBufferLength
));
1159 SetLastError(E_OUTOFMEMORY
);
1165 cbData
= recv(conn
->sock
, conn
->decryptBuffer
+ conn
->decryptBufferUsed
,
1166 (int) (conn
->decryptBufferLength
- conn
->decryptBufferUsed
), 0);
1168 if (cbData
== SOCKET_ERROR
)
1170 DEBUG_printf(("_sspiConnect: recv failed: %d", WSAGetLastError()));
1174 else if (cbData
== 0)
1176 DEBUG_printf(("_sspiConnect: server unexpectedly disconnected"));
1181 DEBUG_printf(("_sspiConnect: %d bytes of handshake data received",
1184 conn
->decryptBufferUsed
+= cbData
;
1188 * Set up the input buffers. Buffer 0 is used to pass in data
1189 * received from the server. Schannel will consume some or all
1190 * of this. Leftover data (if any) will be placed in buffer 1 and
1191 * given a buffer type of SECBUFFER_EXTRA.
1193 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
1194 inBuffers
[0].cbBuffer
= (unsigned long) conn
->decryptBufferUsed
;
1195 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1197 inBuffers
[1].pvBuffer
= NULL
;
1198 inBuffers
[1].cbBuffer
= 0;
1199 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1201 inBuffer
.cBuffers
= 2;
1202 inBuffer
.pBuffers
= inBuffers
;
1203 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1206 * Set up the output buffers. These are initialized to NULL
1207 * so as to make it less likely we'll attempt to free random
1210 outBuffers
[0].pvBuffer
= NULL
;
1211 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1212 outBuffers
[0].cbBuffer
= 0;
1214 outBuffer
.cBuffers
= 1;
1215 outBuffer
.pBuffers
= outBuffers
;
1216 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1219 * Call InitializeSecurityContext.
1221 scRet
= InitializeSecurityContext(&conn
->creds
, &conn
->context
, NULL
, dwSSPIFlags
,
1222 0, SECURITY_NATIVE_DREP
, &inBuffer
, 0, NULL
,
1223 &outBuffer
, &dwSSPIOutFlags
, &tsExpiry
);
1226 * If InitializeSecurityContext was successful (or if the error was
1227 * one of the special extended ones), send the contends of the output
1228 * buffer to the server.
1230 if (scRet
== SEC_E_OK
||
1231 scRet
== SEC_I_CONTINUE_NEEDED
||
1232 FAILED(scRet
) && (dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
))
1234 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1236 cbData
= send(conn
->sock
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1238 if ((cbData
== SOCKET_ERROR
) || !cbData
)
1240 DEBUG_printf(("_sspiConnect: send failed: %d", WSAGetLastError()));
1241 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1242 DeleteSecurityContext(&conn
->context
);
1247 DEBUG_printf(("_sspiConnect: %d bytes of handshake data sent", cbData
));
1250 * Free output buffer.
1252 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1253 outBuffers
[0].pvBuffer
= NULL
;
1258 * If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
1259 * then we need to read more data from the server and try again.
1261 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1265 * If InitializeSecurityContext returned SEC_E_OK, then the
1266 * handshake completed successfully.
1268 if (scRet
== SEC_E_OK
)
1271 * If the "extra" buffer contains data, this is encrypted application
1272 * protocol layer stuff. It needs to be saved. The application layer
1273 * will later decrypt it with DecryptMessage.
1275 DEBUG_printf(("_sspiConnect: Handshake was successful"));
1277 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1279 if (conn
->decryptBufferLength
< inBuffers
[1].cbBuffer
)
1281 conn
->decryptBuffer
= realloc(conn
->decryptBuffer
, inBuffers
[1].cbBuffer
);
1283 if (!conn
->decryptBuffer
)
1285 DEBUG_printf(("_sspiConnect: unable to allocate %d bytes for decrypt buffer",
1286 inBuffers
[1].cbBuffer
));
1287 SetLastError(E_OUTOFMEMORY
);
1293 memmove(conn
->decryptBuffer
,
1294 conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
),
1295 inBuffers
[1].cbBuffer
);
1297 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1299 DEBUG_printf(("_sspiConnect: %d bytes of app data was bundled with handshake data",
1300 conn
->decryptBufferUsed
));
1303 conn
->decryptBufferUsed
= 0;
1312 * Check for fatal error.
1316 DEBUG_printf(("_sspiConnect: InitializeSecurityContext(2) failed: %x", scRet
));
1322 * If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1323 * then the server just requested client authentication.
1325 if (scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1330 DEBUG_printf(("_sspiConnect: server requested client credentials"));
1336 * Copy any leftover data from the "extra" buffer, and go around
1339 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1341 memmove(conn
->decryptBuffer
,
1342 conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
),
1343 inBuffers
[1].cbBuffer
);
1345 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1349 conn
->decryptBufferUsed
= 0;
1355 conn
->contextInitialized
= TRUE
;
1358 * Get the server cert
1360 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_REMOTE_CERT_CONTEXT
, (VOID
*) &serverCert
);
1362 if (scRet
!= SEC_E_OK
)
1364 DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_REMOTE_CERT_CONTEXT): %x", scRet
));
1369 scRet
= sspi_verify_certificate(serverCert
, hostname
, conn
->certFlags
);
1371 if (scRet
!= SEC_E_OK
)
1373 DEBUG_printf(("_sspiConnect: sspi_verify_certificate failed: %x", scRet
));
1379 * Find out how big the header/trailer will be:
1381 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
1383 if (scRet
!= SEC_E_OK
)
1385 DEBUG_printf(("_sspiConnect: QueryContextAttributes failed(SECPKG_ATTR_STREAM_SIZES): %x", scRet
));
1393 CertFreeCertificateContext(serverCert
);
1400 * '_sspiAccept()' - Accept an SSL/TLS connection
1402 BOOL
/* O - 1 on success, 0 on failure */
1403 _sspiAccept(_sspi_struct_t
*conn
) /* I - Client connection */
1405 DWORD dwSSPIFlags
; /* SSL connection attributes we want */
1406 DWORD dwSSPIOutFlags
; /* SSL connection attributes we got */
1407 TimeStamp tsExpiry
; /* Time stamp */
1408 SECURITY_STATUS scRet
; /* SSPI Status */
1409 SecBufferDesc inBuffer
; /* Array of SecBuffer structs */
1410 SecBuffer inBuffers
[2]; /* Security package buffer */
1411 SecBufferDesc outBuffer
; /* Array of SecBuffer structs */
1412 SecBuffer outBuffers
[1]; /* Security package buffer */
1413 DWORD num
= 0; /* 32 bit status value */
1414 BOOL fInitContext
= TRUE
;
1415 /* Has the context been init'd? */
1416 BOOL ok
= TRUE
; /* Return value */
1421 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
1422 ASC_REQ_REPLAY_DETECT
|
1423 ASC_REQ_CONFIDENTIALITY
|
1424 ASC_REQ_EXTENDED_ERROR
|
1425 ASC_REQ_ALLOCATE_MEMORY
|
1428 conn
->decryptBufferUsed
= 0;
1431 * Set OutBuffer for AcceptSecurityContext call
1433 outBuffer
.cBuffers
= 1;
1434 outBuffer
.pBuffers
= outBuffers
;
1435 outBuffer
.ulVersion
= SECBUFFER_VERSION
;
1437 scRet
= SEC_I_CONTINUE_NEEDED
;
1439 while (scRet
== SEC_I_CONTINUE_NEEDED
||
1440 scRet
== SEC_E_INCOMPLETE_MESSAGE
||
1441 scRet
== SEC_I_INCOMPLETE_CREDENTIALS
)
1443 if ((conn
->decryptBufferUsed
== 0) || (scRet
== SEC_E_INCOMPLETE_MESSAGE
))
1445 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1447 conn
->decryptBufferLength
+= 4096;
1448 conn
->decryptBuffer
= (BYTE
*) realloc(conn
->decryptBuffer
,
1449 conn
->decryptBufferLength
);
1451 if (!conn
->decryptBuffer
)
1453 DEBUG_printf(("_sspiAccept: unable to allocate %d byte decrypt buffer",
1454 conn
->decryptBufferLength
));
1462 num
= recv(conn
->sock
,
1463 conn
->decryptBuffer
+ conn
->decryptBufferUsed
,
1464 (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
),
1467 if ((num
== SOCKET_ERROR
) && (WSAGetLastError() == WSAEWOULDBLOCK
))
1473 if (num
== SOCKET_ERROR
)
1475 DEBUG_printf(("_sspiAccept: recv failed: %d", WSAGetLastError()));
1481 DEBUG_printf(("_sspiAccept: client disconnected"));
1486 DEBUG_printf(("_sspiAccept: received %d (handshake) bytes from client",
1488 conn
->decryptBufferUsed
+= num
;
1492 * InBuffers[1] is for getting extra data that
1493 * SSPI/SCHANNEL doesn't proccess on this
1494 * run around the loop.
1496 inBuffers
[0].pvBuffer
= conn
->decryptBuffer
;
1497 inBuffers
[0].cbBuffer
= (unsigned long) conn
->decryptBufferUsed
;
1498 inBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1500 inBuffers
[1].pvBuffer
= NULL
;
1501 inBuffers
[1].cbBuffer
= 0;
1502 inBuffers
[1].BufferType
= SECBUFFER_EMPTY
;
1504 inBuffer
.cBuffers
= 2;
1505 inBuffer
.pBuffers
= inBuffers
;
1506 inBuffer
.ulVersion
= SECBUFFER_VERSION
;
1509 * Initialize these so if we fail, pvBuffer contains NULL,
1510 * so we don't try to free random garbage at the quit
1512 outBuffers
[0].pvBuffer
= NULL
;
1513 outBuffers
[0].BufferType
= SECBUFFER_TOKEN
;
1514 outBuffers
[0].cbBuffer
= 0;
1516 scRet
= AcceptSecurityContext(&conn
->creds
, (fInitContext
?NULL
:&conn
->context
),
1517 &inBuffer
, dwSSPIFlags
, SECURITY_NATIVE_DREP
,
1518 (fInitContext
?&conn
->context
:NULL
), &outBuffer
,
1519 &dwSSPIOutFlags
, &tsExpiry
);
1521 fInitContext
= FALSE
;
1523 if (scRet
== SEC_E_OK
||
1524 scRet
== SEC_I_CONTINUE_NEEDED
||
1525 (FAILED(scRet
) && ((dwSSPIOutFlags
& ISC_RET_EXTENDED_ERROR
) != 0)))
1527 if (outBuffers
[0].cbBuffer
&& outBuffers
[0].pvBuffer
)
1530 * Send response to server if there is one
1532 num
= send(conn
->sock
, outBuffers
[0].pvBuffer
, outBuffers
[0].cbBuffer
, 0);
1534 if ((num
== SOCKET_ERROR
) || (num
== 0))
1536 DEBUG_printf(("_sspiAccept: handshake send failed: %d", WSAGetLastError()));
1541 DEBUG_printf(("_sspiAccept: send %d handshake bytes to client",
1542 outBuffers
[0].cbBuffer
));
1544 FreeContextBuffer(outBuffers
[0].pvBuffer
);
1545 outBuffers
[0].pvBuffer
= NULL
;
1549 if (scRet
== SEC_E_OK
)
1552 * If there's extra data then save it for
1553 * next time we go to decrypt
1555 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1557 memcpy(conn
->decryptBuffer
,
1558 (LPBYTE
) (conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
)),
1559 inBuffers
[1].cbBuffer
);
1560 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1564 conn
->decryptBufferUsed
= 0;
1570 else if (FAILED(scRet
) && (scRet
!= SEC_E_INCOMPLETE_MESSAGE
))
1572 DEBUG_printf(("_sspiAccept: AcceptSecurityContext failed: %x", scRet
));
1577 if (scRet
!= SEC_E_INCOMPLETE_MESSAGE
&&
1578 scRet
!= SEC_I_INCOMPLETE_CREDENTIALS
)
1580 if (inBuffers
[1].BufferType
== SECBUFFER_EXTRA
)
1582 memcpy(conn
->decryptBuffer
,
1583 (LPBYTE
) (conn
->decryptBuffer
+ (conn
->decryptBufferUsed
- inBuffers
[1].cbBuffer
)),
1584 inBuffers
[1].cbBuffer
);
1585 conn
->decryptBufferUsed
= inBuffers
[1].cbBuffer
;
1589 conn
->decryptBufferUsed
= 0;
1596 conn
->contextInitialized
= TRUE
;
1599 * Find out how big the header will be:
1601 scRet
= QueryContextAttributes(&conn
->context
, SECPKG_ATTR_STREAM_SIZES
, &conn
->streamSizes
);
1603 if (scRet
!= SEC_E_OK
)
1605 DEBUG_printf(("_sspiAccept: QueryContextAttributes failed: %x", scRet
));
1617 * '_sspiSetAllowsAnyRoot()' - Set the client cert policy for untrusted root certs
1620 _sspiSetAllowsAnyRoot(_sspi_struct_t
*conn
,
1621 /* I - Client connection */
1623 /* I - Allow any root */
1625 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_UNKNOWN_CA
:
1626 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_UNKNOWN_CA
;
1631 * '_sspiSetAllowsExpiredCerts()' - Set the client cert policy for expired root certs
1634 _sspiSetAllowsExpiredCerts(_sspi_struct_t
*conn
,
1635 /* I - Client connection */
1637 /* I - Allow expired certs */
1639 conn
->certFlags
= (allow
) ? conn
->certFlags
| SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
:
1640 conn
->certFlags
& ~SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
;
1645 * '_sspiWrite()' - Write a buffer to an ssl socket
1647 int /* O - Bytes written or SOCKET_ERROR */
1648 _sspiWrite(_sspi_struct_t
*conn
, /* I - Client connection */
1649 void *buf
, /* I - Buffer */
1650 size_t len
) /* I - Buffer length */
1652 SecBufferDesc message
; /* Array of SecBuffer struct */
1653 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
1654 BYTE
*buffer
= NULL
; /* Scratch buffer */
1655 int bufferLen
; /* Buffer length */
1656 size_t bytesLeft
; /* Bytes left to write */
1657 int index
= 0; /* Index into buffer */
1658 int num
= 0; /* Return value */
1660 if (!conn
|| !buf
|| !len
)
1662 WSASetLastError(WSAEINVAL
);
1667 bufferLen
= conn
->streamSizes
.cbMaximumMessage
+
1668 conn
->streamSizes
.cbHeader
+
1669 conn
->streamSizes
.cbTrailer
;
1671 buffer
= (BYTE
*) malloc(bufferLen
);
1675 DEBUG_printf(("_sspiWrite: buffer alloc of %d bytes failed", bufferLen
));
1676 WSASetLastError(E_OUTOFMEMORY
);
1685 size_t chunk
= min(conn
->streamSizes
.cbMaximumMessage
, /* Size of data to write */
1687 SECURITY_STATUS scRet
; /* SSPI status */
1690 * Copy user data into the buffer, starting
1691 * just past the header
1693 memcpy(buffer
+ conn
->streamSizes
.cbHeader
,
1694 ((BYTE
*) buf
) + index
,
1698 * Setup the SSPI buffers
1700 message
.ulVersion
= SECBUFFER_VERSION
;
1701 message
.cBuffers
= 4;
1702 message
.pBuffers
= buffers
;
1703 buffers
[0].pvBuffer
= buffer
;
1704 buffers
[0].cbBuffer
= conn
->streamSizes
.cbHeader
;
1705 buffers
[0].BufferType
= SECBUFFER_STREAM_HEADER
;
1706 buffers
[1].pvBuffer
= buffer
+ conn
->streamSizes
.cbHeader
;
1707 buffers
[1].cbBuffer
= (unsigned long) chunk
;
1708 buffers
[1].BufferType
= SECBUFFER_DATA
;
1709 buffers
[2].pvBuffer
= buffer
+ conn
->streamSizes
.cbHeader
+ chunk
;
1710 buffers
[2].cbBuffer
= conn
->streamSizes
.cbTrailer
;
1711 buffers
[2].BufferType
= SECBUFFER_STREAM_TRAILER
;
1712 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
1717 scRet
= EncryptMessage(&conn
->context
, 0, &message
, 0);
1721 DEBUG_printf(("_sspiWrite: EncryptMessage failed: %x", scRet
));
1722 WSASetLastError(WSASYSCALLFAILURE
);
1728 * Send the data. Remember the size of
1729 * the total data to send is the size
1730 * of the header, the size of the data
1731 * the caller passed in and the size
1734 num
= send(conn
->sock
,
1736 buffers
[0].cbBuffer
+ buffers
[1].cbBuffer
+ buffers
[2].cbBuffer
,
1739 if ((num
== SOCKET_ERROR
) || (num
== 0))
1741 DEBUG_printf(("_sspiWrite: send failed: %ld", WSAGetLastError()));
1745 bytesLeft
-= (int) chunk
;
1746 index
+= (int) chunk
;
1761 * '_sspiRead()' - Read a buffer from an ssl socket
1763 int /* O - Bytes read or SOCKET_ERROR */
1764 _sspiRead(_sspi_struct_t
*conn
, /* I - Client connection */
1765 void *buf
, /* I - Buffer */
1766 size_t len
) /* I - Buffer length */
1768 SecBufferDesc message
; /* Array of SecBuffer struct */
1769 SecBuffer buffers
[4] = { 0 }; /* Security package buffer */
1770 int num
= 0; /* Return value */
1774 WSASetLastError(WSAEINVAL
);
1780 * If there are bytes that have already been
1781 * decrypted and have not yet been read, return
1784 if (buf
&& (conn
->readBufferUsed
> 0))
1786 int bytesToCopy
= (int) min(conn
->readBufferUsed
, len
); /* Amount of bytes to copy */
1787 /* from read buffer */
1789 memcpy(buf
, conn
->readBuffer
, bytesToCopy
);
1790 conn
->readBufferUsed
-= bytesToCopy
;
1792 if (conn
->readBufferUsed
> 0)
1794 * If the caller didn't request all the bytes
1795 * we have in the buffer, then move the unread
1798 memmove(conn
->readBuffer
,
1799 conn
->readBuffer
+ bytesToCopy
,
1800 conn
->readBufferUsed
);
1806 PSecBuffer pDataBuffer
; /* Data buffer */
1807 PSecBuffer pExtraBuffer
; /* Excess data buffer */
1808 SECURITY_STATUS scRet
; /* SSPI status */
1809 int i
; /* Loop control variable */
1812 * Initialize security buffer structs
1814 message
.ulVersion
= SECBUFFER_VERSION
;
1815 message
.cBuffers
= 4;
1816 message
.pBuffers
= buffers
;
1821 * If there is not enough space in the
1822 * buffer, then increase it's size
1824 if (conn
->decryptBufferLength
<= conn
->decryptBufferUsed
)
1826 conn
->decryptBufferLength
+= 4096;
1827 conn
->decryptBuffer
= (BYTE
*) realloc(conn
->decryptBuffer
,
1828 conn
->decryptBufferLength
);
1830 if (!conn
->decryptBuffer
)
1832 DEBUG_printf(("_sspiRead: unable to allocate %d byte buffer",
1833 conn
->decryptBufferLength
));
1834 WSASetLastError(E_OUTOFMEMORY
);
1840 buffers
[0].pvBuffer
= conn
->decryptBuffer
;
1841 buffers
[0].cbBuffer
= (unsigned long) conn
->decryptBufferUsed
;
1842 buffers
[0].BufferType
= SECBUFFER_DATA
;
1843 buffers
[1].BufferType
= SECBUFFER_EMPTY
;
1844 buffers
[2].BufferType
= SECBUFFER_EMPTY
;
1845 buffers
[3].BufferType
= SECBUFFER_EMPTY
;
1847 scRet
= DecryptMessage(&conn
->context
, &message
, 0, NULL
);
1849 if (scRet
== SEC_E_INCOMPLETE_MESSAGE
)
1853 num
= recv(conn
->sock
,
1854 conn
->decryptBuffer
+ conn
->decryptBufferUsed
,
1855 (int)(conn
->decryptBufferLength
- conn
->decryptBufferUsed
),
1857 if (num
== SOCKET_ERROR
)
1859 DEBUG_printf(("_sspiRead: recv failed: %d", WSAGetLastError()));
1864 DEBUG_printf(("_sspiRead: server disconnected"));
1868 conn
->decryptBufferUsed
+= num
;
1872 num
= (int) conn
->readBufferUsed
;
1877 while (scRet
== SEC_E_INCOMPLETE_MESSAGE
);
1879 if (scRet
== SEC_I_CONTEXT_EXPIRED
)
1881 DEBUG_printf(("_sspiRead: context expired"));
1882 WSASetLastError(WSAECONNRESET
);
1886 else if (scRet
!= SEC_E_OK
)
1888 DEBUG_printf(("_sspiRead: DecryptMessage failed: %lx", scRet
));
1889 WSASetLastError(WSASYSCALLFAILURE
);
1895 * The decryption worked. Now, locate data buffer.
1898 pExtraBuffer
= NULL
;
1899 for (i
= 1; i
< 4; i
++)
1901 if (buffers
[i
].BufferType
== SECBUFFER_DATA
)
1902 pDataBuffer
= &buffers
[i
];
1903 else if (!pExtraBuffer
&& (buffers
[i
].BufferType
== SECBUFFER_EXTRA
))
1904 pExtraBuffer
= &buffers
[i
];
1908 * If a data buffer is found, then copy
1909 * the decrypted bytes to the passed-in
1914 int bytesToCopy
= min(pDataBuffer
->cbBuffer
, (int) len
);
1915 /* Number of bytes to copy into buf */
1916 int bytesToSave
= pDataBuffer
->cbBuffer
- bytesToCopy
;
1917 /* Number of bytes to save in our read buffer */
1920 memcpy(buf
, pDataBuffer
->pvBuffer
, bytesToCopy
);
1923 * If there are more decrypted bytes than can be
1924 * copied to the passed in buffer, then save them
1928 if ((int)(conn
->readBufferLength
- conn
->readBufferUsed
) < bytesToSave
)
1930 conn
->readBufferLength
= conn
->readBufferUsed
+ bytesToSave
;
1931 conn
->readBuffer
= realloc(conn
->readBuffer
,
1932 conn
->readBufferLength
);
1934 if (!conn
->readBuffer
)
1936 DEBUG_printf(("_sspiRead: unable to allocate %d bytes", conn
->readBufferLength
));
1937 WSASetLastError(E_OUTOFMEMORY
);
1943 memcpy(((BYTE
*) conn
->readBuffer
) + conn
->readBufferUsed
,
1944 ((BYTE
*) pDataBuffer
->pvBuffer
) + bytesToCopy
,
1947 conn
->readBufferUsed
+= bytesToSave
;
1950 num
= (buf
) ? bytesToCopy
: (int) conn
->readBufferUsed
;
1954 DEBUG_printf(("_sspiRead: unable to find data buffer"));
1955 WSASetLastError(WSASYSCALLFAILURE
);
1961 * If the decryption process left extra bytes,
1962 * then save those back in decryptBuffer. They will
1963 * be processed the next time through the loop.
1967 memmove(conn
->decryptBuffer
, pExtraBuffer
->pvBuffer
, pExtraBuffer
->cbBuffer
);
1968 conn
->decryptBufferUsed
= pExtraBuffer
->cbBuffer
;
1972 conn
->decryptBufferUsed
= 0;
1983 * '_sspiPending()' - Returns the number of available bytes
1985 int /* O - Number of available bytes */
1986 _sspiPending(_sspi_struct_t
*conn
) /* I - Client connection */
1988 return (_sspiRead(conn
, NULL
, 0));
1993 * '_sspiFree()' - Close a connection and free resources
1996 _sspiFree(_sspi_struct_t
*conn
) /* I - Client connection */
2001 if (conn
->contextInitialized
)
2003 SecBufferDesc message
; /* Array of SecBuffer struct */
2004 SecBuffer buffers
[1] = { 0 };
2005 /* Security package buffer */
2006 DWORD dwType
; /* Type */
2007 DWORD status
; /* Status */
2010 * Notify schannel that we are about to close the connection.
2012 dwType
= SCHANNEL_SHUTDOWN
;
2014 buffers
[0].pvBuffer
= &dwType
;
2015 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
2016 buffers
[0].cbBuffer
= sizeof(dwType
);
2018 message
.cBuffers
= 1;
2019 message
.pBuffers
= buffers
;
2020 message
.ulVersion
= SECBUFFER_VERSION
;
2022 status
= ApplyControlToken(&conn
->context
, &message
);
2024 if (SUCCEEDED(status
))
2026 PBYTE pbMessage
; /* Message buffer */
2027 DWORD cbMessage
; /* Message buffer count */
2028 DWORD cbData
; /* Data count */
2029 DWORD dwSSPIFlags
; /* SSL attributes we requested */
2030 DWORD dwSSPIOutFlags
; /* SSL attributes we received */
2031 TimeStamp tsExpiry
; /* Time stamp */
2033 dwSSPIFlags
= ASC_REQ_SEQUENCE_DETECT
|
2034 ASC_REQ_REPLAY_DETECT
|
2035 ASC_REQ_CONFIDENTIALITY
|
2036 ASC_REQ_EXTENDED_ERROR
|
2037 ASC_REQ_ALLOCATE_MEMORY
|
2040 buffers
[0].pvBuffer
= NULL
;
2041 buffers
[0].BufferType
= SECBUFFER_TOKEN
;
2042 buffers
[0].cbBuffer
= 0;
2044 message
.cBuffers
= 1;
2045 message
.pBuffers
= buffers
;
2046 message
.ulVersion
= SECBUFFER_VERSION
;
2048 status
= AcceptSecurityContext(&conn
->creds
, &conn
->context
, NULL
,
2049 dwSSPIFlags
, SECURITY_NATIVE_DREP
, NULL
,
2050 &message
, &dwSSPIOutFlags
, &tsExpiry
);
2052 if (SUCCEEDED(status
))
2054 pbMessage
= buffers
[0].pvBuffer
;
2055 cbMessage
= buffers
[0].cbBuffer
;
2058 * Send the close notify message to the client.
2060 if (pbMessage
&& cbMessage
)
2062 cbData
= send(conn
->sock
, pbMessage
, cbMessage
, 0);
2063 if ((cbData
== SOCKET_ERROR
) || (cbData
== 0))
2065 status
= WSAGetLastError();
2066 DEBUG_printf(("_sspiFree: sending close notify failed: %d", status
));
2070 FreeContextBuffer(pbMessage
);
2076 DEBUG_printf(("_sspiFree: AcceptSecurityContext failed: %x", status
));
2081 DEBUG_printf(("_sspiFree: ApplyControlToken failed: %x", status
));
2084 DeleteSecurityContext(&conn
->context
);
2085 conn
->contextInitialized
= FALSE
;
2088 if (conn
->decryptBuffer
)
2090 free(conn
->decryptBuffer
);
2091 conn
->decryptBuffer
= NULL
;
2094 if (conn
->readBuffer
)
2096 free(conn
->readBuffer
);
2097 conn
->readBuffer
= NULL
;
2100 if (conn
->sock
!= INVALID_SOCKET
)
2102 closesocket(conn
->sock
);
2103 conn
->sock
= INVALID_SOCKET
;
2111 * 'sspi_verify_certificate()' - Verify a server certificate
2113 static DWORD
/* 0 - Error code (0 == No error) */
2114 sspi_verify_certificate(PCCERT_CONTEXT serverCert
,
2115 /* I - Server certificate */
2116 const CHAR
*serverName
,
2117 /* I - Server name */
2119 /* I - Verification flags */
2121 HTTPSPolicyCallbackData httpsPolicy
;
2122 /* HTTPS Policy Struct */
2123 CERT_CHAIN_POLICY_PARA policyPara
;
2124 /* Cert chain policy parameters */
2125 CERT_CHAIN_POLICY_STATUS policyStatus
;
2126 /* Cert chain policy status */
2127 CERT_CHAIN_PARA chainPara
;
2128 /* Used for searching and matching criteria */
2129 PCCERT_CHAIN_CONTEXT chainContext
= NULL
;
2130 /* Certificate chain */
2131 PWSTR serverNameUnicode
= NULL
;
2132 /* Unicode server name */
2133 LPSTR rgszUsages
[] = { szOID_PKIX_KP_SERVER_AUTH
,
2134 szOID_SERVER_GATED_CRYPTO
,
2135 szOID_SGC_NETSCAPE
};
2136 /* How are we using this certificate? */
2137 DWORD cUsages
= sizeof(rgszUsages
) / sizeof(LPSTR
);
2138 /* Number of ites in rgszUsages */
2139 DWORD count
; /* 32 bit count variable */
2140 DWORD status
; /* Return value */
2144 status
= SEC_E_WRONG_PRINCIPAL
;
2149 * Convert server name to unicode.
2151 if (!serverName
|| (strlen(serverName
) == 0))
2153 status
= SEC_E_WRONG_PRINCIPAL
;
2157 count
= MultiByteToWideChar(CP_ACP
, 0, serverName
, -1, NULL
, 0);
2158 serverNameUnicode
= LocalAlloc(LMEM_FIXED
, count
* sizeof(WCHAR
));
2159 if (!serverNameUnicode
)
2161 status
= SEC_E_INSUFFICIENT_MEMORY
;
2164 count
= MultiByteToWideChar(CP_ACP
, 0, serverName
, -1, serverNameUnicode
, count
);
2167 status
= SEC_E_WRONG_PRINCIPAL
;
2172 * Build certificate chain.
2174 ZeroMemory(&chainPara
, sizeof(chainPara
));
2175 chainPara
.cbSize
= sizeof(chainPara
);
2176 chainPara
.RequestedUsage
.dwType
= USAGE_MATCH_TYPE_OR
;
2177 chainPara
.RequestedUsage
.Usage
.cUsageIdentifier
= cUsages
;
2178 chainPara
.RequestedUsage
.Usage
.rgpszUsageIdentifier
= rgszUsages
;
2180 if (!CertGetCertificateChain(NULL
, serverCert
, NULL
, serverCert
->hCertStore
,
2181 &chainPara
, 0, NULL
, &chainContext
))
2183 status
= GetLastError();
2184 DEBUG_printf(("CertGetCertificateChain returned 0x%x\n", status
));
2189 * Validate certificate chain.
2191 ZeroMemory(&httpsPolicy
, sizeof(HTTPSPolicyCallbackData
));
2192 httpsPolicy
.cbStruct
= sizeof(HTTPSPolicyCallbackData
);
2193 httpsPolicy
.dwAuthType
= AUTHTYPE_SERVER
;
2194 httpsPolicy
.fdwChecks
= dwCertFlags
;
2195 httpsPolicy
.pwszServerName
= serverNameUnicode
;
2197 memset(&policyPara
, 0, sizeof(policyPara
));
2198 policyPara
.cbSize
= sizeof(policyPara
);
2199 policyPara
.pvExtraPolicyPara
= &httpsPolicy
;
2201 memset(&policyStatus
, 0, sizeof(policyStatus
));
2202 policyStatus
.cbSize
= sizeof(policyStatus
);
2204 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL
, chainContext
,
2205 &policyPara
, &policyStatus
))
2207 status
= GetLastError();
2208 DEBUG_printf(("CertVerifyCertificateChainPolicy returned %d", status
));
2212 if (policyStatus
.dwError
)
2214 status
= policyStatus
.dwError
;
2223 CertFreeCertificateChain(chainContext
);
2225 if (serverNameUnicode
)
2226 LocalFree(serverNameUnicode
);