From 621d9e5c413e561293d7484b93882d985b3fe15f Mon Sep 17 00:00:00 2001 From: Endi Sukma Dewata Date: Sat, 24 Mar 2012 02:27:47 -0500 Subject: Removed unnecessary pki folder. Previously the source code was located inside a pki folder. This folder was created during svn migration and is no longer needed. This folder has now been removed and the contents have been moved up one level. Ticket #131 --- base/tps/src/httpClient/engine.cpp | 775 +++++++++++++++++++++++++++++++++++++ 1 file changed, 775 insertions(+) create mode 100644 base/tps/src/httpClient/engine.cpp (limited to 'base/tps/src/httpClient/engine.cpp') diff --git a/base/tps/src/httpClient/engine.cpp b/base/tps/src/httpClient/engine.cpp new file mode 100644 index 000000000..621a37244 --- /dev/null +++ b/base/tps/src/httpClient/engine.cpp @@ -0,0 +1,775 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/** BEGIN COPYRIGHT BLOCK + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Copyright (C) 2007 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "nspr.h" +#include "sslproto.h" +#include "prerror.h" + +#include "ssl.h" +#include "nss.h" +#include "pk11func.h" +#include "cert.h" +#include "certt.h" +#include "sslerr.h" +#include "secerr.h" + +#include "httpClient/httpc/engine.h" +#include "httpClient/httpc/http.h" +#include "httpClient/httpc/PSPRUtil.h" +#include "httpClient/httpc/Defines.h" +//-- #include "httpClient/httpc/DebugLogger.h" +#include "engine/RA.h" +#include "main/Memory.h" + +char* certName = NULL; +char* password = NULL; +int ciphers[32]; +int cipherCount = 0; +int _doVerifyServerCert = 1; + +//-- static const char *DEBUG_MODULE = "httpclient"; +//-- static const char *DEBUG_CLASS_NAME = "HttpEngine"; + +PRIntervalTime Engine::globaltimeout = PR_TicksPerSecond()*30; + +static char * ownPasswd( PK11SlotInfo *slot, PRBool retry, void *arg) { + if (!retry) { + if( password != NULL ) { + return PL_strdup(password); + } else { + return PL_strdup( "httptest" ); + } + } else { + return NULL; + } +} + +/** + * Function: SECStatus myBadCertHandler() + *
+ * Purpose: This callback is called when the incoming certificate is not + * valid. We define a certain set of parameters that still cause the + * certificate to be "valid" for this session, and return SECSuccess to cause + * the server to continue processing the request when any of these conditions + * are met. Otherwise, SECFailure is return and the server rejects the + * request. + */ +SECStatus myBadCertHandler( void *arg, PRFileDesc *socket ) { + + SECStatus secStatus = SECFailure; + PRErrorCode err; + + /* log invalid cert here */ + + if ( !arg ) { + return secStatus; + } + + *(PRErrorCode *)arg = err = PORT_GetError(); + + /* If any of the cases in the switch are met, then we will proceed */ + /* with the processing of the request anyway. Otherwise, the default */ + /* case will be reached and we will reject the request. */ + + switch (err) { + case SEC_ERROR_INVALID_AVA: + case SEC_ERROR_INVALID_TIME: + case SEC_ERROR_BAD_SIGNATURE: + case SEC_ERROR_EXPIRED_CERTIFICATE: + case SEC_ERROR_UNKNOWN_ISSUER: + case SEC_ERROR_UNTRUSTED_CERT: + case SEC_ERROR_CERT_VALID: + case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: + case SEC_ERROR_CRL_EXPIRED: + case SEC_ERROR_CRL_BAD_SIGNATURE: + case SEC_ERROR_EXTENSION_VALUE_INVALID: + case SEC_ERROR_CA_CERT_INVALID: + case SEC_ERROR_CERT_USAGES_INVALID: + case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION: + case SEC_ERROR_EXTENSION_NOT_FOUND: // Added by Rob 5/21/2002 + secStatus = SECSuccess; + break; + default: + secStatus = SECFailure; + break; + } + + return secStatus; +} + + +PRBool __EXPORT InitSecurity(char* certDir, char* certname, char* certpassword, char *prefix,int verify ) { + if (certpassword) { + password = PL_strdup(certpassword); + } else { + password = PL_strdup( "httptest" ); + } + if (certname) { + certName = PL_strdup(certname); + } + + SECStatus stat; + PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 0 ); + if (!NSS_IsInitialized()) { + stat = NSS_Initialize( certDir, prefix, prefix,"secmod.db", + NSS_INIT_READONLY); + } else { + stat = SECSuccess; + RA::Debug( LL_PER_PDU, + "initSecurity: ", + "NSS Already initialized" ); + + } + + if (SECSuccess != stat) { + // int err = PR_GetError(); + return PR_FAILURE; + } + PK11_SetPasswordFunc(ownPasswd); + + stat = NSS_SetDomesticPolicy(); + SSL_CipherPrefSetDefault( SSL_RSA_WITH_NULL_MD5, PR_TRUE ); + + _doVerifyServerCert = verify; + + + return PR_TRUE; +} + + +int ssl2Suites[] = { + SSL_EN_RC4_128_WITH_MD5, /* A */ + SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */ + SSL_EN_RC2_128_CBC_WITH_MD5, /* C */ + SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */ + SSL_EN_DES_64_CBC_WITH_MD5, /* E */ + SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */ + 0 +}; + +int ssl3Suites[] = { + SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, /* a */ + SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, /* b */ + SSL_RSA_WITH_RC4_128_MD5, /* c */ + SSL_RSA_WITH_3DES_EDE_CBC_SHA, /* d */ + SSL_RSA_WITH_DES_CBC_SHA, /* e */ + SSL_RSA_EXPORT_WITH_RC4_40_MD5, /* f */ + SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */ + SSL_FORTEZZA_DMS_WITH_NULL_SHA, /* h */ + SSL_RSA_WITH_NULL_MD5, /* i */ + SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */ + SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */ + TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */ + TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */ + 0 +}; + +int tlsSuites[] = { +// TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, +// TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, +// TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, +// TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, +// TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, +// TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA +}; + +void disableAllCiphersOnSocket(PRFileDesc* sock) { + int i; + int numsuites = SSL_NumImplementedCiphers; + + /* disable all the cipher suites for that socket */ + for (i = 0; i 0; ) { + /* do nothing */; + } + ciphers[cipherCount++] = cipher; + } + + return PR_TRUE; +} + +SECStatus certcallback ( + void *arg, + PRFileDesc *fd, + PRBool checksig, + PRBool isServer) { + return SECSuccess; // always succeed +} + +/** + * Function: SECStatus myAuthCertificate() + *
+ * Purpose: This function is our custom certificate authentication handler. + *
+ * Note: This implementation is essentially the same as the default + * SSL_AuthCertificate(). + */ +extern "C" { + +static SECStatus myAuthCertificate( void *arg, + PRFileDesc *socket, + PRBool checksig, + PRBool isServer ) { + + SECCertUsage certUsage; + CERTCertificate * cert; + void * pinArg; + char * hostName = NULL; + SECStatus secStatus = SECSuccess; +//-- static const char *DEBUG_METHOD_NAME = "myAuthCertificate"; +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( "httpclient"); + + if ( !arg || !socket ) { + return SECFailure; + } + + /* Define how the cert is being used based upon the isServer flag. */ + + certUsage = isServer ? certUsageSSLClient : certUsageSSLServer; + + cert = SSL_PeerCertificate( socket ); + + pinArg = SSL_RevealPinArg( socket ); + + // Skip the server cert verification fconditionally, because our test + // servers do not have a valid root CA cert. + if ( _doVerifyServerCert ) { + + PRLock *verify_lock = RA::GetVerifyLock(); + if (verify_lock == NULL) { + return SECFailure; + } + PR_Lock(verify_lock); + /* This function is not thread-safe. So we need to use a global lock */ + secStatus = CERT_VerifyCertNow( (CERTCertDBHandle *)arg, + cert, + checksig, + certUsage, + pinArg); + PR_Unlock(verify_lock); + + if( SECSuccess != secStatus ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + if (cert == NULL) { + RA::Debug( LL_PER_PDU, + "myAuthCertificate: ", + "Server Certificate Not Found" ); + } else { + if (cert->subjectName == NULL) { + RA::Debug( LL_PER_PDU, + "myAuthCertificate: ", + "Untrusted server certificate" ); + } else { + RA::Debug( LL_PER_PDU, + "myAuthCertificate: ", + "Untrusted server certificate error=%d subject='%s'", PORT_GetError(), cert->subjectName ); + } + } + } + } + + /* If this is a server, we're finished. */ + if (isServer || secStatus != SECSuccess) { + return secStatus; + } + + /* Certificate is OK. Since this is the client side of an SSL + * connection, we need to verify that the name field in the cert + * matches the desired hostname. This is our defense against + * man-in-the-middle attacks. + */ + + /* SSL_RevealURL returns a hostName, not an URL. */ + hostName = SSL_RevealURL( socket ); + + if (hostName && hostName[0]) { + secStatus = CERT_VerifyCertName( cert, hostName ); + if( SECSuccess != secStatus ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "myAuthCertificate: ", + "Server name does not match that in certificate" ); + } + } else { + secStatus = SECFailure; +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "myAuthCertificate: ", + "server name has been specified" ); + } + + if( hostName != NULL ) { + PR_Free( hostName ); + hostName = NULL; + } + + return secStatus; +} + + +/* Function: SECStatus ownGetClientAuthData() + * + * Purpose: This callback is used by SSL to pull client certificate + * information upon server request. + */ +static SECStatus ownGetClientAuthData(void *arg, PRFileDesc *socket, + CERTDistNames *caNames, + CERTCertificate **pRetCert,/*return */ + SECKEYPrivateKey **pRetKey) { + CERTCertificate * cert = NULL; + SECKEYPrivateKey * privKey = NULL; + void * proto_win = NULL; + SECStatus rv = SECFailure; + char * localNickName = (char *)arg; + + proto_win = SSL_RevealPinArg(socket); + + if (localNickName) { + RA::Debug( LL_PER_PDU, + "ownGetClientAuthData: ", + "ownGetClientAuthData looking for nickname=%s", + localNickName ); + cert = PK11_FindCertFromNickname(localNickName, proto_win); + if (cert) { + RA::Debug( LL_PER_PDU, + "ownGetClientAuthData: ", + "ownGetClientAuthData found cert" ); + privKey = PK11_FindKeyByAnyCert(cert, proto_win); + if (privKey) { + RA::Debug( LL_PER_PDU, + "ownGetClientAuthData: ", + "ownGetClientAuthData found priv key for cert" ); + rv = SECSuccess; + } else { + if( cert != NULL ) { + CERT_DestroyCertificate( cert ); + cert = NULL; + } + } + } + else { + RA::Debug( LL_PER_PDU, + "ownGetClientAuthData: ", + "ownGetClientAuthData did NOT find cert" ); + } + + if (rv == SECSuccess) { + *pRetCert = cert; + *pRetKey = privKey; + } + + // if( localNickName != NULL ) { + // free( localNickName ); + // localNickName = NULL; + // } + return rv; + } + else { + RA::Debug( LL_PER_PDU, + "ownGetClientAuthData: ", + "ownGetClientAuthData does not have nickname" ); + } + + char* chosenNickName = certName ? (char *)PL_strdup(certName) : NULL; + if (chosenNickName) { + cert = PK11_FindCertFromNickname(chosenNickName, proto_win); + if (cert) { + privKey = PK11_FindKeyByAnyCert(cert, proto_win); + if (privKey) { + rv = SECSuccess; + } else { + if( cert != NULL ) { + CERT_DestroyCertificate( cert ); + cert = NULL; + } + } + } + } else { + /* no nickname given, automatically find the right cert */ + CERTCertNicknames * names; + int i; + + names = CERT_GetCertNicknames( CERT_GetDefaultCertDB(), + SEC_CERT_NICKNAMES_USER, + proto_win); + + if (names != NULL) { + for( i=0; i < names->numnicknames; i++ ) { + cert = PK11_FindCertFromNickname(names->nicknames[i], + proto_win); + if (!cert) { + continue; + } + + /* Only check unexpired certs */ + if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE) != + secCertTimeValid) { + if( cert != NULL ) { + CERT_DestroyCertificate( cert ); + cert = NULL; + } + continue; + } + + rv = NSS_CmpCertChainWCANames(cert, caNames); + + if (rv == SECSuccess) { + privKey = PK11_FindKeyByAnyCert(cert, proto_win); + if (privKey) { + // got the key + break; + } + + // cert database password was probably wrong + rv = SECFailure; + break; + }; + } /* for loop */ + CERT_FreeNicknames(names); + } // names + } // no nickname chosen + + if (rv == SECSuccess) { + *pRetCert = cert; + *pRetKey = privKey; + } + + if( chosenNickName != NULL ) { + free( chosenNickName ); + chosenNickName = NULL; + } + + return rv; +} +} // extern "C" + +void nodelay(PRFileDesc* fd) { + PRSocketOptionData opt; + PRStatus rv; + + opt.option = PR_SockOpt_NoDelay; + opt.value.no_delay = PR_FALSE; + + rv = PR_GetSocketOption(fd, &opt); + if (rv == PR_FAILURE) { + return; + } + + opt.option = PR_SockOpt_NoDelay; + opt.value.no_delay = PR_TRUE; + rv = PR_SetSocketOption(fd, &opt); + if (rv == PR_FAILURE) { + return; + } + + return; +} + + +void __EXPORT setDefaultAllTLSCiphers() { + int i =0; + char alg[256]; + while (tlsSuites[i]) { + PR_snprintf((char *)alg, 256, "%x", tlsSuites[i]); + RA::Debug( LL_PER_PDU, + "setDefaultAllTLSCiphers", + alg); + SSL_CipherPrefSetDefault(tlsSuites[i++], PR_TRUE); + } +} + +/** + * Returns a file descriptor for I/O if the HTTP connection is successful + * @param addr PRnetAddr structure which points to the server to connect to + * @param SSLOn boo;elan to state if this is an SSL client + */ +PRFileDesc * Engine::_doConnect(PRNetAddr *addr, PRBool SSLOn, + const PRInt32* cipherSuite, + PRInt32 count, const char *nickName, + PRBool handshake, + /*const SecurityProtocols& secprots,*/ + const char *serverName, PRIntervalTime timeout) { +//-- static const char *DEBUG_METHOD_NAME = "doConnect"; +//-- DebugLogger *logger = DebugLogger::GetDebugLogger( "httpclient"); + PRFileDesc *tcpsock = NULL; + PRFileDesc *sock = NULL; + + SSL_CipherPrefSetDefault(0xC005 /* TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA */, PR_TRUE); + setDefaultAllTLSCiphers(); + + tcpsock = PR_OpenTCPSocket(addr->raw.family); + + if (nickName != NULL) + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "_doConnect has nickname=%s", + nickName ); + else + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "_doConnect has nickname=NULL" ); + + if (!tcpsock) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, +//XXXX log NSPR error code + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "PR_OpenTCPSocket returned NULL" ); + return NULL; + } + + nodelay(tcpsock); + + if (PR_TRUE == SSLOn) { + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "SSL is ON" ); + sock=SSL_ImportFD(NULL, tcpsock); + if (!sock) { + //xxx log + if( tcpsock != NULL ) { + PR_Close( tcpsock ); + tcpsock = NULL; + } + return NULL; + } + + int error = 0; + PRBool rv = SSL_OptionSet(sock, SSL_SECURITY, 1); + if ( SECSuccess == rv ) { + rv = SSL_OptionSet(sock, SSL_HANDSHAKE_AS_CLIENT, 1); + } + if ( SECSuccess == rv ) { + rv = SSL_OptionSet(sock, SSL_ENABLE_SSL3, PR_TRUE); + } + if ( SECSuccess == rv ) { + rv = SSL_OptionSet(sock, SSL_ENABLE_TLS, PR_TRUE); + } + if ( SECSuccess != rv ) { + error = PORT_GetError(); + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "SSL_OptionSet error: %d", + error ); + return NULL; + } + + rv = SSL_GetClientAuthDataHook( sock, + ownGetClientAuthData, + (void*)nickName); + if ( SECSuccess != rv ) { + error = PORT_GetError(); + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "SSL_GetClientAuthDataHook error: %d", + error ); + return NULL; + } + + rv = SSL_AuthCertificateHook(sock, + (SSLAuthCertificate)myAuthCertificate, + (void *)CERT_GetDefaultCertDB()); + + if (rv != SECSuccess ) { + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } + return NULL; + } + + PRErrorCode errCode = 0; + + rv = SSL_BadCertHook( sock, + (SSLBadCertHandler)myBadCertHandler, + &errCode ); + rv = SSL_SetURL( sock, serverName ); + + if (rv != SECSuccess ) { + error = PORT_GetError(); + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "SSL_SetURL error: %d", + error ); + return NULL; + } + + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "end SSL is ON" ); + //EnableAllTLSCiphers( sock); + //EnableAllSSL3Ciphers( sock); + } else { + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "SSL is OFF" ); + sock = tcpsock; + } + + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "about to call PR_Connect, timeout =%d", + timeout ); + + if ( PR_Connect(sock, addr, timeout) == PR_FAILURE ) { +//-- logger->Log( LOGLEVEL_SEVERE, DEBUG_CLASS_NAME, +//-- DEBUG_METHOD_NAME, + RA::Debug( LL_PER_PDU, + "Engine::_doConnect: ", + "PR_Connect error: %d Msg=%s", + PR_GetError(), + "XXX" ); + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } + return NULL; + } + + return (sock); +} + +/** + * Called from higher level to connect, sends a request + * and gets a response as an HttpResponse object + * + * @param request Contains the entire request url + headers etc + * @param server Has the host, port, protocol info + * @param timeout Time in seconds to wait for a response + * @return The response body and headers + */ +PSHttpResponse * HttpEngine::makeRequest( PSHttpRequest &request, + const PSHttpServer& server, + int timeout, PRBool expectChunked ) { + PRNetAddr addr; + PRFileDesc *sock = NULL; + PSHttpResponse *resp = NULL; + + PRBool response_code = 0; + + server.getAddr(&addr); + + char *nickName = request.getCertNickName(); + + char *serverName = (char *)server.getAddr(); + sock = _doConnect( &addr, request.isSSL(), 0, 0,nickName, 0, serverName ); + + if ( sock != NULL) { + PRBool status = request.send( sock ); + if ( status ) { + resp = new PSHttpResponse( sock, &request, timeout, expectChunked ); + response_code = resp->processResponse(); + + RA::Debug( LL_PER_PDU, + "HttpEngine::makeRequest: ", + "makeRequest response %d", + response_code ); + + if(!response_code) + { + RA::Debug( LL_PER_PDU, + "HttpEngine::makeRequest: ", + "Deleting response because of FALSE return, returning NULL." ); + if( resp != NULL ) { + delete resp; + resp = NULL; + } + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } + + return NULL; + + } + } + if( sock != NULL ) { + PR_Close( sock ); + sock = NULL; + } + } + + return resp; +} -- cgit