549 lines
15 KiB
C++
Executable File
549 lines
15 KiB
C++
Executable File
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Minimo.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Doug Turner <dougt@meer.net>.
|
|
* Portions created by the Initial Developer are Copyright (C) 2006
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIGenericFactory.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsNetError.h"
|
|
#include "nsNetCID.h"
|
|
|
|
#include "nspr.h"
|
|
#include "private/pprio.h"
|
|
#include "nsString.h"
|
|
#include "nsCRT.h"
|
|
|
|
#include "nsISocketProvider.h"
|
|
#include "nsITransportSecurityInfo.h"
|
|
#include "nsISSLStatusProvider.h"
|
|
#include "nsIStringBundle.h"
|
|
|
|
#include "nsIWebProgressListener.h"
|
|
|
|
#include "nsSecureBrowserUIImpl.h"
|
|
|
|
#include "nsNativeSDR.h"
|
|
|
|
#include <Winsock2.h>
|
|
#include <sslsock.h>
|
|
#include <Wincrypt.h>
|
|
#include <schnlsp.h>
|
|
|
|
#define MINIMO_PROPERTIES_URL "chrome://minimo/locale/minimo.properties"
|
|
|
|
static SSL_CRACK_CERTIFICATE_FN gSslCrackCertificate = 0 ;
|
|
static SSL_FREE_CERTIFICATE_FN gSslFreeCertificate = 0 ;
|
|
static HINSTANCE gSchannel = NULL ;
|
|
|
|
static PRDescIdentity nsWINCESSLIOLayerIdentity;
|
|
static PRIOMethods nsWINCESSLIOLayerMethods;
|
|
|
|
|
|
void MessageBoxWSAError(const char * msg)
|
|
{
|
|
char buffer[100];
|
|
int error = WSAGetLastError();
|
|
if (error == 0)
|
|
return;
|
|
|
|
sprintf(buffer, "%s error is: %d", msg, error);
|
|
MessageBox(0, buffer, buffer, MB_APPLMODAL | MB_TOPMOST | MB_SETFOREGROUND);
|
|
}
|
|
|
|
class nsWINCESSLSocketProvider : public nsISocketProvider
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSISOCKETPROVIDER
|
|
|
|
nsWINCESSLSocketProvider() {};
|
|
virtual ~nsWINCESSLSocketProvider() {}
|
|
};
|
|
|
|
|
|
class nsWINCESSLSocketInfo : public nsITransportSecurityInfo
|
|
{
|
|
public:
|
|
nsWINCESSLSocketInfo()
|
|
{
|
|
mSecurityState = nsIWebProgressListener::STATE_IS_BROKEN;
|
|
mUserAcceptedSSLProblem = PR_FALSE;
|
|
}
|
|
|
|
virtual ~nsWINCESSLSocketInfo() {}
|
|
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSITRANSPORTSECURITYINFO
|
|
|
|
nsresult Init(const char *destinationHost, const char *proxyHost, PRInt32 proxyPort)
|
|
{
|
|
mDestinationHost = destinationHost;
|
|
mProxyHost = proxyHost;
|
|
mProxyPort = proxyPort;
|
|
|
|
return NS_OK;
|
|
};
|
|
|
|
SOCKET mSocket;
|
|
nsCString mDestinationHost;
|
|
nsCString mProxyHost;
|
|
PRInt32 mProxyPort;
|
|
|
|
PRUint32 mSecurityState;
|
|
|
|
PRBool mUserAcceptedSSLProblem;
|
|
|
|
};
|
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS1(nsWINCESSLSocketInfo, nsITransportSecurityInfo)
|
|
|
|
NS_IMETHODIMP
|
|
nsWINCESSLSocketInfo::GetSecurityState(PRUint32* state)
|
|
{
|
|
*state = mSecurityState;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWINCESSLSocketInfo::GetShortSecurityDescription(PRUnichar** aText)
|
|
{
|
|
*aText = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
static int DisplaySSLProblemDialog(nsWINCESSLSocketInfo* info, PRUnichar* type)
|
|
{
|
|
if (info->mUserAcceptedSSLProblem)
|
|
return IDYES;
|
|
|
|
nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID);
|
|
if (!bundleService)
|
|
return IDNO;
|
|
|
|
nsCOMPtr<nsIStringBundle> bundle;
|
|
bundleService->CreateBundle(MINIMO_PROPERTIES_URL, getter_AddRefs(bundle));
|
|
|
|
if (!bundle)
|
|
return IDNO;
|
|
|
|
nsXPIDLString message;
|
|
nsXPIDLString title;
|
|
bundle->GetStringFromName(type, getter_Copies(message));
|
|
bundle->GetStringFromName(NS_LITERAL_STRING("securityWarningTitle").get(), getter_Copies(title));
|
|
|
|
int result = MessageBoxW(0, message.get(), title.get(), MB_YESNO | MB_ICONWARNING | MB_APPLMODAL | MB_TOPMOST | MB_SETFOREGROUND );
|
|
|
|
if (result == IDYES)
|
|
info->mUserAcceptedSSLProblem = PR_TRUE;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static int SSLValidationHook(DWORD dwType, LPVOID pvArg, DWORD dwChainLen, LPBLOB pCertChain, DWORD dwFlags )
|
|
{
|
|
nsWINCESSLSocketInfo* info = (nsWINCESSLSocketInfo*)pvArg;
|
|
info->mSecurityState = nsIWebProgressListener::STATE_IS_BROKEN;
|
|
|
|
if ( SSL_CERT_X509 != dwType )
|
|
return SSL_ERR_BAD_DATA;
|
|
|
|
if ( SSL_CERT_FLAG_ISSUER_UNKNOWN & dwFlags )
|
|
{
|
|
if (DisplaySSLProblemDialog(info, L"confirmUnknownIssuer") != IDYES)
|
|
return SSL_ERR_CERT_UNKNOWN;
|
|
}
|
|
|
|
// see:
|
|
// http://groups.google.com/groups?q=SslCrackCertificate&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=uQf1VcLWBHA.1632%40tkmsftngp05&rnum=3
|
|
|
|
if (!gSslCrackCertificate)
|
|
{
|
|
gSchannel = LoadLibrary ( "schannel.dll" ) ;
|
|
if ( NULL != gSchannel )
|
|
{
|
|
gSslCrackCertificate = (SSL_CRACK_CERTIFICATE_FN)GetProcAddress ( gSchannel, SSL_CRACK_CERTIFICATE_NAME ) ;
|
|
gSslFreeCertificate = (SSL_FREE_CERTIFICATE_FN )GetProcAddress ( gSchannel, SSL_FREE_CERTIFICATE_NAME ) ;
|
|
}
|
|
|
|
if (!gSslCrackCertificate || !gSslFreeCertificate)
|
|
{
|
|
MessageBox(0, "Could not find the right stuff in the default security library", "schannel.dll", MB_APPLMODAL | MB_TOPMOST | MB_SETFOREGROUND);
|
|
return SSL_ERR_BAD_DATA;
|
|
}
|
|
}
|
|
|
|
X509Certificate * cert = 0;
|
|
if ( !gSslCrackCertificate( pCertChain->pBlobData, pCertChain->cbSize, TRUE, &cert ) )
|
|
return SSL_ERR_BAD_DATA;
|
|
|
|
|
|
// all we have to do is compare the name in the cert to
|
|
// what the hostname was when we created this socket.
|
|
|
|
char* subject = strstr(cert->pszSubject, "CN=");
|
|
if (!subject)
|
|
return SSL_ERR_BAD_DATA;
|
|
|
|
subject = subject+3; // pass CN=
|
|
|
|
// check to see if the common name has any ws
|
|
char* end = strpbrk(subject, "' \t,");
|
|
char save;
|
|
if (end)
|
|
{
|
|
save = end[0];
|
|
end[0] = '\0';
|
|
}
|
|
|
|
char* destinationHost = (char*) info->mDestinationHost.get();
|
|
|
|
// if we run across a cert with a *, pass the machine name, and find the host name.
|
|
if (subject[0] == '*')
|
|
{
|
|
destinationHost = strchr(destinationHost, '.');
|
|
if (destinationHost)
|
|
destinationHost++;
|
|
|
|
subject = subject + 2; // pass the *.
|
|
}
|
|
|
|
// do the check
|
|
int res = SSL_ERR_CERT_UNKNOWN;
|
|
|
|
if (! _stricmp (destinationHost, subject))
|
|
{
|
|
info->mSecurityState = nsIWebProgressListener::STATE_IS_SECURE | nsIWebProgressListener::STATE_SECURE_HIGH;
|
|
res = SSL_ERR_OKAY;
|
|
}
|
|
else
|
|
{
|
|
if (DisplaySSLProblemDialog(info, L"confirmMismatch") == IDYES)
|
|
res = SSL_ERR_OKAY;
|
|
}
|
|
|
|
if (end)
|
|
end[0] = save;
|
|
|
|
gSslFreeCertificate ( cert ) ;
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
struct sockaddr *
|
|
nspr2winsock(const PRNetAddr *addr, int *len)
|
|
{
|
|
struct sockaddr_in *ws = (struct sockaddr_in*) malloc(sizeof(*ws));
|
|
if (!ws)
|
|
return NULL;
|
|
|
|
memset(ws, 0, sizeof(*ws));
|
|
|
|
// family
|
|
ws->sin_family = AF_INET;
|
|
|
|
// ip
|
|
memcpy(&ws->sin_addr, &addr->inet.ip, sizeof(ws->sin_addr));
|
|
|
|
// port
|
|
ws->sin_port = addr->inet.port;
|
|
|
|
*len = sizeof(*ws);
|
|
return (struct sockaddr *)ws;
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK
|
|
nsWINCESSLIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime /*timeout*/)
|
|
{
|
|
nsWINCESSLSocketInfo * info = (nsWINCESSLSocketInfo*) fd->secret;
|
|
if (info == NULL)
|
|
return PR_FAILURE;
|
|
|
|
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (!s)
|
|
{
|
|
MessageBox(0, "Failed to create a socket", "connect()", MB_APPLMODAL | MB_TOPMOST | MB_SETFOREGROUND);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
info->mSocket = s;
|
|
|
|
// set the socket to secure mode
|
|
DWORD dwFlag = SO_SEC_SSL ;
|
|
DWORD error = setsockopt (s, SOL_SOCKET, SO_SECURE, (char *)&dwFlag, sizeof(dwFlag));
|
|
if (error != 0)
|
|
{
|
|
MessageBox(0, "Failed to setsockopt", "connect()", MB_APPLMODAL | MB_TOPMOST | MB_SETFOREGROUND);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
// set the certificate validation callback.
|
|
SSLVALIDATECERTHOOK sslHook ;
|
|
sslHook.HookFunc = SSLValidationHook;
|
|
|
|
NS_ADDREF(info);
|
|
sslHook.pvArg = (void *) info; // XXXX Leak! what is the lifespan of this?
|
|
|
|
if ( WSAIoctl (s,
|
|
SO_SSL_SET_VALIDATE_CERT_HOOK,
|
|
&sslHook,
|
|
sizeof sslHook,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL) )
|
|
{
|
|
MessageBox(0, "SO_SSL_SET_VALIDATE_CERT_HOOK", "connect()", MB_APPLMODAL | MB_TOPMOST | MB_SETFOREGROUND);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
int len;
|
|
struct sockaddr* ws = nspr2winsock(addr, &len);
|
|
if (!ws)
|
|
return PR_FAILURE;
|
|
|
|
int result = connect(s, ws, len);
|
|
|
|
if (result == 0)
|
|
return PR_SUCCESS;
|
|
|
|
MessageBoxWSAError("connect()");
|
|
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
static PRStatus PR_CALLBACK
|
|
nsWINCESSLIOLayerClose(PRFileDesc *fd)
|
|
{
|
|
nsWINCESSLSocketInfo * info = (nsWINCESSLSocketInfo*) fd->secret;
|
|
PRDescIdentity id = PR_GetLayersIdentity(fd);
|
|
|
|
if (info && id == nsWINCESSLIOLayerIdentity)
|
|
{
|
|
NS_RELEASE(info);
|
|
fd->identity = PR_INVALID_IO_LAYER;
|
|
}
|
|
|
|
// closesocket(info->mSocket);
|
|
// NS_RELEASE(info);
|
|
// fd->secret = nsnull;
|
|
|
|
return PR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
static PRInt32 PR_CALLBACK
|
|
nsWINCESSLIOLayerAvailable(PRFileDesc *fd)
|
|
{
|
|
MessageBox(0, "available.", "available.", MB_APPLMODAL | MB_TOPMOST | MB_SETFOREGROUND);
|
|
return 1;
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK
|
|
nsWINCESSLIOLayerRead(PRFileDesc* fd, void* buf, PRInt32 amount)
|
|
{
|
|
nsWINCESSLSocketInfo * info = (nsWINCESSLSocketInfo*) fd->secret;
|
|
if (info == NULL)
|
|
return PR_OUT_OF_MEMORY_ERROR;
|
|
|
|
WSASetLastError (0) ;
|
|
PRInt32 rv = recv(info->mSocket, (char*)buf, amount, 0);
|
|
MessageBoxWSAError("read");
|
|
|
|
return rv;
|
|
}
|
|
|
|
static PRInt32 PR_CALLBACK
|
|
nsWINCESSLIOLayerWrite(PRFileDesc* fd, const void* buf, PRInt32 amount)
|
|
{
|
|
nsWINCESSLSocketInfo * info = (nsWINCESSLSocketInfo*) fd->secret;
|
|
if (info == NULL)
|
|
return PR_OUT_OF_MEMORY_ERROR;
|
|
|
|
WSASetLastError (0) ;
|
|
PRInt32 rv = send(info->mSocket, (char*)buf, amount, 0);
|
|
MessageBoxWSAError("write");
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
static PRInt16 PR_CALLBACK nsWINCESSLIOLayerPoll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
|
|
{
|
|
nsWINCESSLSocketInfo * info = (nsWINCESSLSocketInfo*) fd->secret;
|
|
if (info == NULL)
|
|
return PR_OUT_OF_MEMORY_ERROR;
|
|
|
|
*out_flags = PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
|
|
return 1;
|
|
}
|
|
|
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS1(nsWINCESSLSocketProvider, nsISocketProvider)
|
|
|
|
NS_IMETHODIMP
|
|
nsWINCESSLSocketProvider::NewSocket(PRInt32 family,
|
|
const char *host,
|
|
PRInt32 port,
|
|
const char *proxyHost,
|
|
PRInt32 proxyPort,
|
|
PRUint32 flags,
|
|
PRFileDesc **result,
|
|
nsISupports **socksInfo)
|
|
{
|
|
static PRBool firstTime = PR_TRUE;
|
|
if (firstTime) {
|
|
|
|
nsWINCESSLIOLayerIdentity = PR_GetUniqueIdentity("WINCESSL layer");
|
|
nsWINCESSLIOLayerMethods = *PR_GetDefaultIOMethods();
|
|
|
|
nsWINCESSLIOLayerMethods.connect = nsWINCESSLIOLayerConnect;
|
|
nsWINCESSLIOLayerMethods.close = nsWINCESSLIOLayerClose;
|
|
nsWINCESSLIOLayerMethods.available = nsWINCESSLIOLayerAvailable;
|
|
nsWINCESSLIOLayerMethods.write = nsWINCESSLIOLayerWrite;
|
|
nsWINCESSLIOLayerMethods.read = nsWINCESSLIOLayerRead;
|
|
nsWINCESSLIOLayerMethods.poll = nsWINCESSLIOLayerPoll;
|
|
|
|
firstTime = PR_FALSE;
|
|
}
|
|
|
|
PRFileDesc *fd = PR_OpenTCPSocket(family);
|
|
if (!fd)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
PRFileDesc * layer;
|
|
PRStatus rv;
|
|
|
|
layer = PR_CreateIOLayerStub(nsWINCESSLIOLayerIdentity, &nsWINCESSLIOLayerMethods);
|
|
if (! layer)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
nsWINCESSLSocketInfo * infoObject = new nsWINCESSLSocketInfo();
|
|
if (!infoObject)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ADDREF(infoObject);
|
|
|
|
layer->secret = (PRFilePrivate*) infoObject;
|
|
rv = PR_PushIOLayer(fd, PR_GetLayersIdentity(fd), layer);
|
|
if (NS_FAILED(rv))
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
|
|
nsresult initrv = infoObject->Init(host, proxyHost, proxyPort);
|
|
if (NS_FAILED(initrv))
|
|
{
|
|
MessageBox(0, "Can not create ssl socket.", "Can not create ssl socket.", MB_APPLMODAL | MB_TOPMOST | MB_SETFOREGROUND);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
*result = fd;
|
|
*socksInfo = (nsISupports*) (nsITransportSecurityInfo*) infoObject;
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWINCESSLSocketProvider::AddToSocket(PRInt32 family,
|
|
const char *host,
|
|
PRInt32 port,
|
|
const char *proxyHost,
|
|
PRInt32 proxyPort,
|
|
PRUint32 flags,
|
|
PRFileDesc *sock,
|
|
nsISupports **socksInfo)
|
|
{
|
|
return NS_ERROR_SOCKET_CREATE_FAILED;
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// XPCOM REGISTRATION BELOW
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
#define NS_WINCESSLSOCKETPROVIDER_CID \
|
|
{ /* 40f0fb5a-9819-4a48-bca9-da8170fd8b58 */ \
|
|
0x40f0fb5a, \
|
|
0x9819, \
|
|
0x4a48, \
|
|
{0xbc, 0xa9, 0xda, 0x81, 0x70, 0xfd, 0x8b, 0x58} \
|
|
}
|
|
|
|
|
|
#define NS_WINCENATIVESDR_CID \
|
|
{ 0xcba2f351, \
|
|
0x1993, \
|
|
0x4b27, \
|
|
{0xbf, 0x60, 0x4e, 0xbf, 0xce, 0x9a, 0xa3, 0x58} \
|
|
}
|
|
|
|
|
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsWINCESSLSocketProvider)
|
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSecureBrowserUIImpl)
|
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsNativeSDR)
|
|
|
|
static const nsModuleComponentInfo components[] =
|
|
{
|
|
{ "nsWINCESSLSocketProvider",
|
|
NS_WINCESSLSOCKETPROVIDER_CID,
|
|
NS_SSLSOCKETPROVIDER_CONTRACTID,
|
|
nsWINCESSLSocketProviderConstructor
|
|
},
|
|
|
|
{
|
|
NS_SECURE_BROWSER_UI_CLASSNAME,
|
|
NS_SECURE_BROWSER_UI_CID,
|
|
NS_SECURE_BROWSER_UI_CONTRACTID,
|
|
nsSecureBrowserUIImplConstructor
|
|
},
|
|
|
|
{
|
|
"Native SDR",
|
|
NS_WINCENATIVESDR_CID,
|
|
"@mozilla.org/security/sdr;1",
|
|
nsNativeSDRConstructor
|
|
},
|
|
|
|
};
|
|
|
|
NS_IMPL_NSGETMODULE(nsNativeSSLModule, components)
|