Newer
Older
return ret;
}
CURLcode
Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
return schannel_connect_common(conn, sockindex, TRUE, done);
}
CURLcode
Curl_schannel_connect(struct connectdata *conn, int sockindex)
{
CURLcode retcode;
bool done = FALSE;
retcode = schannel_connect_common(conn, sockindex, FALSE, &done);
if(retcode)
return retcode;
DEBUGASSERT(done);
return CURLE_OK;
}
bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
{
const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
if(connssl->use) /* SSL/TLS is in use */
return (connssl->encdata_offset > 0 ||
connssl->decdata_offset > 0 ) ? TRUE : FALSE;
else
return FALSE;
}
void Curl_schannel_close(struct connectdata *conn, int sockindex)
{
/* if the SSL/TLS channel hasn't been shut down yet, do that now. */
Curl_ssl_shutdown(conn, sockindex);
}
int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
{
/* See http://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
* Shutting Down an Schannel Connection
*/
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
conn->host.name, conn->remote_port);
if(connssl->cred && connssl->ctxt) {
SecBufferDesc BuffDesc;
SecBuffer Buffer;
SECURITY_STATUS sspi_status;
SecBuffer outbuf;
SecBufferDesc outbuf_desc;
CURLcode code;
TCHAR *host_name;
DWORD dwshut = SCHANNEL_SHUTDOWN;
InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
InitSecBufferDesc(&BuffDesc, &Buffer, 1);
sspi_status = s_pSecFn->ApplyControlToken(&connssl->ctxt->ctxt_handle,
&BuffDesc);
if(sspi_status != SEC_E_OK)
failf(data, "schannel: ApplyControlToken failure: %s",
Curl_sspi_strerror(conn, sspi_status));
host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
if(!host_name)
return CURLE_OUT_OF_MEMORY;
/* setup output buffer */
InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
sspi_status = s_pSecFn->InitializeSecurityContext(
&connssl->cred->cred_handle,
&connssl->ctxt->ctxt_handle,
host_name,
connssl->req_flags,
0,
0,
NULL,
0,
&connssl->ctxt->ctxt_handle,
&outbuf_desc,
&connssl->ret_flags,
&connssl->ctxt->time_stamp);
if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
/* send close message which is in output buffer */
ssize_t written;
code = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
outbuf.cbBuffer, &written);
s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
if((code != CURLE_OK) || (outbuf.cbBuffer != (size_t)written)) {
infof(data, "schannel: failed to send close msg: %s"
" (bytes written: %zd)\n", curl_easy_strerror(code), written);
}
}
/* free SSPI Schannel API security context handle */
if(connssl->ctxt) {
s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
Curl_safefree(connssl->ctxt);
/* decrement the reference counter of the credential/session handle */
if(connssl->cred && connssl->cred->refcount > 0) {
connssl->cred->refcount--;
infof(data, "schannel: decremented credential handle refcount = %d\n",
connssl->cred->refcount);
}
}
/* free internal buffer for received encrypted data */
if(connssl->encdata_buffer != NULL) {
Curl_safefree(connssl->encdata_buffer);
connssl->encdata_length = 0;
connssl->encdata_offset = 0;
}
/* free internal buffer for received decrypted data */
if(connssl->decdata_buffer != NULL) {
Curl_safefree(connssl->decdata_buffer);
connssl->decdata_length = 0;
connssl->decdata_offset = 0;
}
}
void Curl_schannel_session_free(void *ptr)
{
struct curl_schannel_cred *cred = ptr;
if(cred && cred->refcount == 0) {
s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
Curl_safefree(cred);
}
}
int Curl_schannel_init(void)
return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
}
void Curl_schannel_cleanup(void)
Curl_sspi_global_cleanup();
}
size_t Curl_schannel_version(char *buffer, size_t size)
{
size = snprintf(buffer, size, "WinSSL");
}
#ifdef _WIN32_WCE
static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
{
SECURITY_STATUS status;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
CURLcode result = CURLE_OK;
CERT_CONTEXT *pCertContextServer = NULL;
const CERT_CHAIN_CONTEXT *pChainContext = NULL;
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
&pCertContextServer);
if((status != SEC_E_OK) || (pCertContextServer == NULL)) {
failf(data, "schannel: Failed to read remote certificate context: %s",
Curl_sspi_strerror(conn, status));
result = CURLE_PEER_FAILED_VERIFICATION;
}
if(result == CURLE_OK) {
CERT_CHAIN_PARA ChainPara;
memset(&ChainPara, 0, sizeof(ChainPara));
ChainPara.cbSize = sizeof(ChainPara);
if(!CertGetCertificateChain(NULL,
pCertContextServer,
NULL,
pCertContextServer->hCertStore,
&ChainPara,
0,
NULL,
&pChainContext)) {
failf(data, "schannel: CertGetCertificateChain failed: %s",
Curl_sspi_strerror(conn, GetLastError()));
pChainContext = NULL;
result = CURLE_PEER_FAILED_VERIFICATION;
}
if(result == CURLE_OK) {
CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED|
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
CERT_TRUST_REVOCATION_STATUS_UNKNOWN);
dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
if(dwTrustErrorMask) {
if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
failf(data, "schannel: CertGetCertificateChain trust error"
" CERT_TRUST_IS_PARTIAL_CHAIN");
if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
failf(data, "schannel: CertGetCertificateChain trust error"
" CERT_TRUST_IS_UNTRUSTED_ROOT");
if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
failf(data, "schannel: CertGetCertificateChain trust error"
" CERT_TRUST_IS_NOT_TIME_VALID");
failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
dwTrustErrorMask);
result = CURLE_PEER_FAILED_VERIFICATION;
}
}
}
if(result == CURLE_OK) {
if(data->set.ssl.verifyhost == 1) {
infof(data, "warning: ignoring unsupported value (1) ssl.verifyhost\n");
}
else if(data->set.ssl.verifyhost == 2) {
TCHAR cert_hostname_buff[128];
xcharp_u hostname;
xcharp_u cert_hostname;
cert_hostname.const_tchar_ptr = cert_hostname_buff;
hostname.tchar_ptr = Curl_convert_UTF8_to_tchar(conn->host.name);
len = CertGetNameString(pCertContextServer,
CERT_NAME_DNS_TYPE,
0,
NULL,
cert_hostname.tchar_ptr,
128);
if(len > 0 && *cert_hostname.tchar_ptr == '*') {
/* this is a wildcard cert. try matching the last len - 1 chars */
int hostname_len = strlen(conn->host.name);
cert_hostname.tchar_ptr++;
if(_tcsicmp(cert_hostname.const_tchar_ptr,
hostname.const_tchar_ptr + hostname_len - len + 2) != 0)
result = CURLE_PEER_FAILED_VERIFICATION;
}
else if(len == 0 || _tcsicmp(hostname.const_tchar_ptr,
cert_hostname.const_tchar_ptr) != 0) {
result = CURLE_PEER_FAILED_VERIFICATION;
}
if(result == CURLE_PEER_FAILED_VERIFICATION) {
char *_cert_hostname;
_cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname.tchar_ptr);
failf(data, "schannel: CertGetNameString() certificate hostname "
"(%s) did not match connection (%s)",
_cert_hostname, conn->host.name);
}
}
if(pChainContext)
CertFreeCertificateChain(pChainContext);
if(pCertContextServer)
CertFreeCertificateContext(pCertContextServer);
return result;
}
#endif /* _WIN32_WCE */
#endif /* USE_SCHANNEL */