📄 ssl.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*--
Module Name: SSL.CPP
Abstract: SSL handling.
--*/
#include "httpd.h"
// This is an undocumented registry setting. If the user wishes to have HTTPD
// bind to port 443 but wants to use an ISAPI read filter instead of our SSL,
// they should set COMM\HTTP\SSL\Enabled=SKIP_SSL_PROCESSING and HTTPD will
// call recv but will not process the requests.
// This is not for the faint of heart! This has never received any test and is
// included as a last ditch effort for the desperate.
#define SKIP_SSL_PROCESSING 0x01191977
static HINSTANCE hCryptLib;
static HINSTANCE hSchannelLib;
PFN_CERTGETNAMESTRINGA pCertGetNameStringA;
static PFN_CERTGETNAMESTRINGW pCertGetNameStringW;
static PFN_CERTFREECERTIFICATECONTEXT pCertFreeCertificateContext;
static PFN_CERTFINDCERTIFICATEINSTORE pCertFindCertificateInStore;
static PFN_CERTOPENSTORE pCertOpenStore;
static PFN_CERTCLOSESTORE pCertCloseStore;
static PFN_SSLCRACKCERTIFICATE pSSLCrackCert;
static PFN_CERTGETCERTIFICATECHAIN pCertGetCertificateChain;
static PFN_CERTCERTFREECERTIFICATECHAIN pCertFreeCertificateChain;
// Modified from wincrypt.h
#define pCertOpenSystemStore(hProv,szSubsystemProtocol) \
pCertOpenStore( \
CERT_STORE_PROV_SYSTEM_W, \
0, \
(hProv), \
CERT_STORE_NO_CRYPT_RELEASE_FLAG|CERT_SYSTEM_STORE_CURRENT_USER, \
(const void *) (szSubsystemProtocol) \
)
void ResetSecurityFcnPtrs(void) {
pCertFreeCertificateContext = NULL;
pCertGetNameStringA = NULL;
pCertGetNameStringW = NULL;
pCertFindCertificateInStore = NULL;
pCertOpenStore = NULL;
pCertCloseStore = NULL;
pCertGetCertificateChain = NULL;
pCertFreeCertificateChain = NULL;
}
// To allow for devices that don't have CAPI2 but still include HTTPAUTH component,
// load functions at runtime.
BOOL SSLGetFunctionPointers(void) {
if (pCertCloseStore)
return TRUE;
hCryptLib = LoadLibrary(L"\\windows\\crypt32.dll");
if (hCryptLib == 0) {
DEBUGMSG(ZONE_INIT,(L"HTTPD: Unable to load crypt32.dll, GLE=0x%08x\r\n",GetLastError()));
return FALSE;
}
pCertFreeCertificateContext = (PFN_CERTFREECERTIFICATECONTEXT) GetProcAddress(hCryptLib,CE_STRING("CertFreeCertificateContext"));
pCertGetNameStringA = (PFN_CERTGETNAMESTRINGA) GetProcAddress(hCryptLib,CE_STRING("CertGetNameStringA"));
pCertGetNameStringW = (PFN_CERTGETNAMESTRINGW) GetProcAddress(hCryptLib,CE_STRING("CertGetNameStringW"));
pCertFindCertificateInStore = (PFN_CERTFINDCERTIFICATEINSTORE) GetProcAddress(hCryptLib,CE_STRING("CertFindCertificateInStore"));
pCertOpenStore = (PFN_CERTOPENSTORE) GetProcAddress(hCryptLib,CE_STRING("CertOpenStore"));
pCertCloseStore = (PFN_CERTCLOSESTORE) GetProcAddress(hCryptLib,CE_STRING("CertCloseStore"));
pCertGetCertificateChain = (PFN_CERTGETCERTIFICATECHAIN) GetProcAddress(hCryptLib,CE_STRING("CertGetCertificateChain"));
pCertFreeCertificateChain = (PFN_CERTCERTFREECERTIFICATECHAIN) GetProcAddress(hCryptLib,CE_STRING("CertFreeCertificateChain"));
if (!pCertFreeCertificateContext || !pCertGetNameStringA || !pCertFindCertificateInStore ||
!pCertOpenStore || !pCertCloseStore || !pCertGetNameStringW || !pCertFreeCertificateChain) {
DEBUGMSG(ZONE_INIT,(L"HTTPD: GetProcAddress fails on Cert functions from crypt32.dll, GLE=0x%08x\r\n",GetLastError()));
ResetSecurityFcnPtrs();
return FALSE;
}
// Failing this is non-fatal
hSchannelLib = LoadLibrary(L"\\windows\\schannel.dll");
if (hSchannelLib) {
pSSLCrackCert = (PFN_SSLCRACKCERTIFICATE) GetProcAddress(hSchannelLib,SSL_CRACK_CERTIFICATE_NAME);
}
return TRUE;
}
// Called when web server is starting up.
void CGlobalVariables::InitSSL(CReg *pWebsite) {
DEBUG_CODE_INIT;
DWORD dwErr = 0;
WCHAR wszSubject[MAX_PATH+1];
PCCERT_CONTEXT pCertContext = NULL;
SCHANNEL_CRED SchannelCred;
DWORD dwLen;
DEBUGCHK((m_fSSL == FALSE) && (m_hSSLCertStore == 0) && (m_fHasSSLCreds == 0));
DEBUGCHK(m_fRootSite); // this should only be called for default site, non-default websites use this still.
DWORD fEnableSSL;
CReg reg((HKEY)*pWebsite,RK_SSL);
m_dwSSLPort = reg.ValueDW(RV_SSL_PORT,IPPORT_SSL);
if (! (fEnableSSL = reg.ValueDW(RV_SSL_ENABLE))) {
DEBUGMSG(ZONE_SSL,(L"HTTPD: SSL not enabled via registry settings\r\n"));
myleave(800);
}
if (fEnableSSL == SKIP_SSL_PROCESSING) {
// HTTPD won't call it's SSL processing, presumably there's an ISAPI filter setup to handle this.
m_fSSLSkipProcessing = TRUE;
m_fSSL = TRUE;
myleave(0);
}
if (!SSLGetFunctionPointers())
myleave(0);
if (! reg.ValueSZ(RV_SSL_CERT_SUBJECT,wszSubject,ARRAYSIZEOF(wszSubject))) {
DEBUGMSG(ZONE_ERROR | ZONE_INIT,(L"HTTPD: SSL has been turned on through registry but key %s wasn't set, required value\r\n",RV_SSL_CERT_SUBJECT));
myleave(801);
}
if (0 == (m_hSSLCertStore = pCertOpenSystemStore(0, L"MY"))) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: CertOpenStore failed, no SSL will be performed. GLE=0x%08x\r\n",GetLastError()));
dwErr = GetLastError();
myleave(802);
}
pCertContext = pCertFindCertificateInStore(m_hSSLCertStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR_W,
wszSubject,
NULL);
if (!pCertContext) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: CertFindCertificateInStore failed, no SSL will be performed. GLE=0x%08x\r\n",GetLastError()));
dwErr = GetLastError();
myleave(803);
}
ZeroMemory(&SchannelCred, sizeof(SchannelCred));
SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
SchannelCred.cCreds = 1;
SchannelCred.paCred = &pCertContext;
dwErr = m_SecurityInterface.AcquireCredentialsHandle(
NULL, // Name of principal
UNISP_NAME, // Name of package
SECPKG_CRED_INBOUND, // Flags indicating use
NULL, // Pointer to logon ID
&SchannelCred, // Package specific data
NULL, // Pointer to GetKey() func
NULL, // Value to pass to GetKey()
&m_hSSLCreds, // (out) Cred Handle
NULL); // (out) Lifetime (optional)
if (dwErr != SEC_E_OK) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: AcquireCredentialsHandle failed, no SSL will be performed. Error = 0x%08x\r\n",dwErr));
myleave(804);
}
m_fHasSSLCreds = TRUE;
#if defined (UNDER_CE)
dwLen = pCertGetNameStringA(pCertContext,CERT_NAME_SIMPLE_DISPLAY_TYPE,CERT_NAME_ISSUER_FLAG,NULL,NULL,0);
if (dwLen != 0 && dwLen != 1) {
if (NULL != (m_pszSSLIssuer = MyRgAllocNZ(CHAR,dwLen)))
pCertGetNameStringA(pCertContext,CERT_NAME_SIMPLE_DISPLAY_TYPE,CERT_NAME_ISSUER_FLAG,NULL,m_pszSSLIssuer,dwLen);
}
dwLen = pCertGetNameStringA(pCertContext,CERT_NAME_SIMPLE_DISPLAY_TYPE,0,NULL,NULL,0);
if (dwLen != 0 && dwLen != 1) {
if (NULL != (m_pszSSLSubject = MyRgAllocNZ(CHAR,dwLen)))
pCertGetNameStringA(pCertContext,CERT_NAME_SIMPLE_DISPLAY_TYPE,0,NULL,m_pszSSLSubject,dwLen);
}
if (!m_pszSSLIssuer)
DEBUGMSG(ZONE_SSL,(L"HTTPD: Unable to determine Certificate Issuer\r\n"));
if (!m_pszSSLSubject)
DEBUGMSG(ZONE_SSL,(L"HTTPD: Unable to determine Certificate Subject\r\n"));
#endif
if (NULL == (m_SSLUserMemDescr = svsutil_AllocFixedMemDescr(sizeof(SSLUserMap),10)))
myleave(809);
m_SSLUsers.InitializeSSLUsers(®,m_SSLUserMemDescr);
// options to ignore client cert errors.
m_dwSSLCertTrustOverride = reg.ValueDW(RV_SSL_CERT_TRUST_OVERRIDE,0);
m_dwSSLCertTrustOverride = (~m_dwSSLCertTrustOverride);
m_fSSL = TRUE;
done:
if (pCertContext)
pCertFreeCertificateContext(pCertContext);
if (!m_fSSL)
FreeSSLResources();
if (dwErr) {
DEBUGCHK(!m_fSSL);
m_pLog->WriteEvent(IDS_HTTPD_SSL_INIT_ERROR,dwErr);
}
}
// Called when web server is shutting down.
void CGlobalVariables::FreeSSLResources(void) {
DEBUGCHK(m_fRootSite);
if (m_hSSLCertStore) {
pCertCloseStore(m_hSSLCertStore,0);
m_hSSLCertStore = 0;
}
if (m_fHasSSLCreds) {
m_SecurityInterface.FreeCredentialHandle(&m_hSSLCreds);
m_fHasSSLCreds = FALSE;
}
if (hCryptLib) {
FreeLibrary(hCryptLib);
hCryptLib = 0;
}
if (hSchannelLib) {
FreeLibrary(hSchannelLib);
hSchannelLib = 0;
}
m_SSLUsers.DeInitUsers();
if (m_SSLUserMemDescr)
svsutil_ReleaseFixedNonEmpty(m_SSLUserMemDescr);
ResetSecurityFcnPtrs();
}
// Shuts down SSL handeling when an HTTP session is over.
void CHttpRequest::CloseSSLSession() {
DEBUG_CODE_INIT;
DWORD dwType;
SecBufferDesc OutBuffer;
SecBuffer OutBuffers[1];
DWORD dwSSPIFlags;
DWORD dwSSPIOutFlags;
if (m_SSLInfo.m_pCertChainContext)
pCertFreeCertificateChain(m_SSLInfo.m_pCertChainContext);
if (m_SSLInfo.m_pClientCertContext)
pCertFreeCertificateContext(m_SSLInfo.m_pClientCertContext);
if (m_SSLInfo.m_fHasCtxt) {
dwType = SCHANNEL_SHUTDOWN;
OutBuffers[0].pvBuffer = &dwType;
OutBuffers[0].BufferType = SECBUFFER_TOKEN;
OutBuffers[0].cbBuffer = sizeof(dwType);
OutBuffer.cBuffers = 1;
OutBuffer.pBuffers = OutBuffers;
OutBuffer.ulVersion = SECBUFFER_VERSION;
if (FAILED(g_pVars->m_SecurityInterface.ApplyControlToken(&m_SSLInfo.m_hcred, &OutBuffer))) {
DEBUGMSG(ZONE_SSL,(L"HTTPD: ApplyControlToken failed on SCHANNEL_SHUTDOWN."));
myleave(2000);
}
dwSSPIFlags = ASC_REQ_SEQUENCE_DETECT | ASC_REQ_REPLAY_DETECT |
ASC_REQ_CONFIDENTIALITY | ASC_REQ_EXTENDED_ERROR |
ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_STREAM;
OutBuffers[0].pvBuffer = NULL;
OutBuffers[0].BufferType = SECBUFFER_TOKEN;
OutBuffers[0].cbBuffer = 0;
OutBuffer.cBuffers = 1;
OutBuffer.pBuffers = OutBuffers;
OutBuffer.ulVersion = SECBUFFER_VERSION;
if (FAILED(g_pVars->m_SecurityInterface.AcceptSecurityContext(&g_pVars->m_hSSLCreds,&m_SSLInfo.m_hcred,NULL,
dwSSPIFlags,SECURITY_NATIVE_DREP,NULL,&OutBuffer,&dwSSPIOutFlags,NULL))) {
DEBUGMSG(ZONE_SSL,(L"HTTPD: AcceptSecurity context failed on shutting down SSL connection"));
myleave (2001);
}
if (OutBuffers[0].pvBuffer && OutBuffers[0].cbBuffer) {
send(m_socket, (PSTR) OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
g_pVars->m_SecurityInterface.FreeContextBuffer(OutBuffers[0].pvBuffer);
}
}
done:
if (m_SSLInfo.m_fHasCtxt)
g_pVars->m_SecurityInterface.DeleteSecurityContext(&m_SSLInfo.m_hcred);
}
// from IIS
#define CRED_STATUS_INVALID_TIME 0x00001000
#define CRED_STATUS_REVOKED 0x00002000
BOOL CHttpRequest::CheckClientCert(void) {
BOOL fRet = FALSE;
CERT_CHAIN_PARA ChainPara;
LPSTR rgpszClientUsage[] = {szOID_PKIX_KP_CLIENT_AUTH,};
DWORD dwClientUsageCount = (sizeof(rgpszClientUsage)/sizeof(rgpszClientUsage[0]));
DWORD dwErrorStatus;
if (m_SSLInfo.m_pClientCertContext || m_SSLInfo.m_pCertChainContext) {
// I don't believe this can happen because web server doesn't accept multiple renegotiates.
// If it does we'll ignore new client certificate sent across and use existing one.
DEBUGCHK(0);
return TRUE;
}
DEBUGCHK(m_SSLInfo.m_dwCertFlags == 0);
// Get Cert Chain information.
SECURITY_STATUS scRet = g_pVars->m_SecurityInterface.QueryContextAttributes(&m_SSLInfo.m_hcred,
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
&m_SSLInfo.m_pClientCertContext);
if (scRet == S_OK && !m_SSLInfo.m_pClientCertContext && ! (GetPerms() & HSE_URL_FLAGS_REQUIRE_CERT)) {
// If we're only doing HSE_URL_FLAGS_NEGO_CERT and we don't have a certificate
// then there's no problems. However if the certificate is garbage (i.e.
// pCertGetCertificateChain check fails) then we'll fail request in this case.
fRet = TRUE;
goto done;
}
if (S_OK != scRet || !m_SSLInfo.m_pClientCertContext || !m_SSLInfo.m_pClientCertContext->hCertStore)
goto done;
memset(&ChainPara,0,sizeof(ChainPara));
ChainPara.cbSize = sizeof(CERT_CHAIN_PARA);
ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
ChainPara.RequestedUsage.Usage.cUsageIdentifier = dwClientUsageCount;
ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgpszClientUsage;
if (! pCertGetCertificateChain(NULL,m_SSLInfo.m_pClientCertContext,NULL,NULL,
&ChainPara,0,NULL,&m_SSLInfo.m_pCertChainContext)) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: CertGetCertificateChain fails, error=0x%08x\r\n",GetLastError()));
goto done;
}
dwErrorStatus = m_SSLInfo.m_pCertChainContext->TrustStatus.dwErrorStatus;
if (dwErrorStatus) {
if (dwErrorStatus & g_pVars->m_dwSSLCertTrustOverride) {
DEBUGMSG(ZONE_ERROR,(L"HTTPD: SSL Client cert is invalid, failing request. pCertChainContext->TrustStatus.dwErrorStatus=0x%08x. Terminating connection.\r\n",dwErrorStatus));
m_fKeepAlive = FALSE;
goto done;
}
DEBUGMSG(ZONE_SSL,(L"HTTPD: Warning, pCertChainContext->TrustStatus.dwErrorStatus=0x%08x (indicates failure) but was overriden by registry CertTrustOverride setting\r\n",dwErrorStatus));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -