mirror of
https://github.com/zeek/zeek.git
synced 2025-10-10 10:38:20 +00:00

* remotes/origin/topic/robin/work: *Now* this passes the test suite. Fixes to SSL/TLS analyzer Added new TLS ciphers Removing some apparently unnecessary lines. A few smaller tweaks. Prepared the old analyzer for extracting SSL extensions. Fixed bug in do_split implementation. Removed an accidental debugging printf. Readded the other changes to remove CheckString calls from strings.bif. Fixed the problem with do_split function which caused it to bail 1 separator early. Modification from rmkml to support SSL extensions. Updated SSL analyzer and Bro script with lots of new ciphers.
1471 lines
40 KiB
C++
1471 lines
40 KiB
C++
// $Id: SSLv3.cc 5988 2008-07-19 07:02:12Z vern $
|
||
|
||
#include "SSLv3.h"
|
||
#include "SSLCiphers.h"
|
||
|
||
// --- Initalization of static variables --------------------------------------
|
||
|
||
bool SSLv3_Interpreter::bInited = false;
|
||
|
||
uint SSLv3_Interpreter::totalConnections = 0;
|
||
uint SSLv3_Interpreter::openedConnections = 0;
|
||
uint SSLv3_Interpreter::totalRecords = 0;
|
||
uint SSLv3_Interpreter::handshakeRecords = 0;
|
||
uint SSLv3_Interpreter::clientHelloRecords = 0;
|
||
uint SSLv3_Interpreter::serverHelloRecords = 0;
|
||
uint SSLv3_Interpreter::alertRecords = 0;
|
||
uint SSLv3_Interpreter::changeCipherRecords = 0;
|
||
|
||
|
||
// ---SSLv3_Interpreter--------------------------------------------------------
|
||
|
||
// Initialize static:
|
||
SSLv3_Automaton SSLv3_Interpreter::sslAutomaton(SSL3_1_NUM_STATES,
|
||
SSL3_1_NUM_TRANS, SSL3_1_STATE_ERROR);
|
||
|
||
SSLv3_Interpreter::SSLv3_Interpreter(SSLProxy_Analyzer* proxy)
|
||
: SSL_Interpreter(proxy)
|
||
{
|
||
pCipherSuite = 0;
|
||
cipherSuiteIdentifier = 0;
|
||
pClientCipherSpecs = 0;
|
||
clientSessionID = 0;
|
||
serverSessionID = 0;
|
||
clientRandom = 0;
|
||
serverRandom = 0;
|
||
serverRSApars = 0;
|
||
serverDHPars = 0;
|
||
encryptedPreSecret = 0;
|
||
clientDHpublic = 0;
|
||
// keyXAlgorithm = SSL_KEY_EXCHANGE_NULL;
|
||
change_cipher_client_seen = false;
|
||
change_cipher_server_seen = false;
|
||
fin_client_seen = false;
|
||
fin_server_seen = false;
|
||
helloRequestValid = true;
|
||
|
||
if ( ! bInited )
|
||
{
|
||
BuildAutomaton();
|
||
// BuildCipherDict();
|
||
bInited = true;
|
||
}
|
||
|
||
currentState = SSL3_1_STATE_INIT;
|
||
++totalConnections;
|
||
}
|
||
|
||
SSLv3_Interpreter::~SSLv3_Interpreter()
|
||
{
|
||
delete pClientCipherSpecs;
|
||
delete clientSessionID;
|
||
delete serverSessionID;
|
||
|
||
if ( ssl_store_key_material )
|
||
{
|
||
if ( clientRandom )
|
||
delete clientRandom->random_bytes;
|
||
delete clientRandom;
|
||
if ( serverRandom )
|
||
delete serverRandom->random_bytes;
|
||
delete serverRandom;
|
||
delete serverRSApars;
|
||
delete serverDHPars;
|
||
delete encryptedPreSecret;
|
||
delete clientDHpublic;
|
||
}
|
||
}
|
||
|
||
void SSLv3_Interpreter::BuildInterpreterEndpoints()
|
||
{
|
||
orig = new SSLv3_Endpoint(this, 1);
|
||
resp = new SSLv3_Endpoint(this, 0);
|
||
}
|
||
|
||
void SSLv3_Interpreter::BuildAutomaton()
|
||
{
|
||
sslAutomaton.addTrans(SSL3_1_STATE_INIT, SSL3_1_TRANS_SERVER_HELLO_REQ,
|
||
SSL3_1_STATE_SERVER_HELLO_REQ_SENT);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_HELLO_REQ_SENT,
|
||
SSL3_1_TRANS_CLIENT_HELLO, SSL3_1_STATE_CLIENT_HELLO_SENT);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_INIT, SSL3_1_TRANS_CLIENT_HELLO,
|
||
SSL3_1_STATE_CLIENT_HELLO_SENT);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_CLIENT_HELLO_SENT,
|
||
SSL3_1_TRANS_SERVER_HELLO, SSL3_1_STATE_SERVER_HELLO_SENT);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_HELLO_SENT,
|
||
SSL3_1_TRANS_SERVER_CERT, SSL3_1_STATE_SERVER_CERT_SENT);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_HELLO_SENT,
|
||
SSL3_1_TRANS_SERVER_KEY_EXCHANGE,
|
||
SSL3_1_STATE_SERVER_KEY_EXCHANGE_SENT);
|
||
|
||
// Server key-exchange and/or server requests cert from client.
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_CERT_SENT,
|
||
SSL3_1_TRANS_SERVER_KEY_EXCHANGE,
|
||
SSL3_1_STATE_SERVER_KEY_EXCHANGE_SENT);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_KEY_EXCHANGE_SENT,
|
||
SSL3_1_TRANS_SERVER_HELLO_DONE,
|
||
SSL3_1_STATE_SERVER_HELLO_DONE_SENT_A);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_KEY_EXCHANGE_SENT,
|
||
SSL3_1_TRANS_SERVER_CERT_REQ,
|
||
SSL3_1_STATE_SERVER_CERT_REQ_SENT);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_CERT_SENT,
|
||
SSL3_1_TRANS_SERVER_CERT_REQ,
|
||
SSL3_1_STATE_SERVER_CERT_REQ_SENT);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_CERT_REQ_SENT,
|
||
SSL3_1_TRANS_SERVER_HELLO_DONE,
|
||
SSL3_1_STATE_SERVER_HELLO_DONE_SENT_B);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_HELLO_DONE_SENT_B,
|
||
SSL3_1_TRANS_CLIENT_CERT, SSL3_1_STATE_CLIENT_CERT_SENT);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_CLIENT_CERT_SENT,
|
||
SSL3_1_TRANS_CLIENT_KEY_EXCHANGE,
|
||
SSL3_1_STATE_CLIENT_KEY_EXCHANGE_SENT_B);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_CLIENT_KEY_EXCHANGE_SENT_B,
|
||
SSL3_1_TRANS_CLIENT_CERT_VERIFY,
|
||
SSL3_1_STATE_CLIENT_CERT_VERIFY_SENT);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_CLIENT_KEY_EXCHANGE_SENT_B,
|
||
SSL3_1_TRANS_CLIENT_FIN, SSL3_1_STATE_CLIENT_FIN_SENT_A);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_CLIENT_CERT_VERIFY_SENT,
|
||
SSL3_1_TRANS_CLIENT_FIN, SSL3_1_STATE_CLIENT_FIN_SENT_A);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_CLIENT_FIN_SENT_A,
|
||
SSL3_1_TRANS_SERVER_FIN, SSL3_1_STATE_HS_FIN_A);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_CLIENT_KEY_EXCHANGE_SENT_B,
|
||
SSL3_1_TRANS_SERVER_FIN, SSL3_1_STATE_SERVER_FIN_SENT_A);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_CLIENT_CERT_VERIFY_SENT,
|
||
SSL3_1_TRANS_SERVER_FIN, SSL3_1_STATE_SERVER_FIN_SENT_A);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_FIN_SENT_A,
|
||
SSL3_1_TRANS_CLIENT_FIN, SSL3_1_STATE_HS_FIN_A);
|
||
|
||
// Server hello done after server cert sent.
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_CERT_SENT,
|
||
SSL3_1_TRANS_SERVER_HELLO_DONE,
|
||
SSL3_1_STATE_SERVER_HELLO_DONE_SENT_A);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_HELLO_DONE_SENT_A,
|
||
SSL3_1_TRANS_CLIENT_KEY_EXCHANGE,
|
||
SSL3_1_STATE_CLIENT_KEY_EXCHANGE_SENT_A);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_CLIENT_KEY_EXCHANGE_SENT_A,
|
||
SSL3_1_TRANS_CLIENT_FIN, SSL3_1_STATE_CLIENT_FIN_SENT_A);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_CLIENT_KEY_EXCHANGE_SENT_A,
|
||
SSL3_1_TRANS_SERVER_FIN, SSL3_1_STATE_SERVER_FIN_SENT_A);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_CLIENT_FIN_SENT_A,
|
||
SSL3_1_TRANS_SERVER_FIN, SSL3_1_STATE_HS_FIN_A);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_FIN_SENT_A,
|
||
SSL3_1_TRANS_CLIENT_FIN, SSL3_1_STATE_HS_FIN_A);
|
||
|
||
// When reestablishing a session:
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_HELLO_SENT,
|
||
SSL3_1_TRANS_CLIENT_FIN, SSL3_1_STATE_CLIENT_FIN_SENT_B);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_HELLO_SENT,
|
||
SSL3_1_TRANS_SERVER_FIN, SSL3_1_STATE_SERVER_FIN_SENT_B);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_CLIENT_FIN_SENT_B,
|
||
SSL3_1_TRANS_SERVER_FIN, SSL3_1_STATE_HS_FIN_B);
|
||
|
||
sslAutomaton.addTrans(SSL3_1_STATE_SERVER_FIN_SENT_B,
|
||
SSL3_1_TRANS_CLIENT_FIN, SSL3_1_STATE_HS_FIN_B);
|
||
|
||
sslAutomaton.setStartState(SSL3_1_STATE_INIT);
|
||
}
|
||
|
||
void SSLv3_Interpreter::printStats()
|
||
{
|
||
printf( "SSLv3x:\n" );
|
||
printf( "Note: Because handshake messages may be coalesced into a \n");
|
||
printf( " single SSLv3x record, the number of total messages for SSLv3x plus \n");
|
||
printf( " the number of total records seen for SSLv3 won't match \n");
|
||
printf( " SSLProxy_Analyzer::totalRecords! \n");
|
||
printf( "total connections = %u\n", totalConnections );
|
||
printf( "opened connections (complete handshake) = %u\n", openedConnections );
|
||
|
||
printf( "total messages seen = %u\n", totalRecords );
|
||
printf( "handshake messages seen = %u\n", handshakeRecords );
|
||
printf( "alert records seen = %u\n", alertRecords );
|
||
printf( "change cipher records seen = %u\n", changeCipherRecords );
|
||
printf( "client hello messages seen = %u\n", clientHelloRecords );
|
||
printf( "server hello messages seen = %u\n", serverHelloRecords );
|
||
}
|
||
|
||
int SSLv3_Interpreter::HandshakeType2Trans(int type)
|
||
{
|
||
switch ( SSL3_1_HandshakeType(type) ) {
|
||
case SSL3_1_HELLO_REQUEST: return SSL3_1_TRANS_SERVER_HELLO_REQ;
|
||
case SSL3_1_CLIENT_HELLO: return SSL3_1_TRANS_CLIENT_HELLO;
|
||
case SSL3_1_SERVER_HELLO: return SSL3_1_TRANS_SERVER_HELLO;
|
||
|
||
case SSL3_1_CERTIFICATE:
|
||
// Client- and server certificate handshake records lead
|
||
// to the same transition in the SSL automaton
|
||
// (see SSLDefines.h)
|
||
return SSL3_1_TRANS_SERVER_CERT;
|
||
|
||
case SSL3_1_SERVER_KEY_EXCHANGE: return SSL3_1_TRANS_SERVER_KEY_EXCHANGE;
|
||
case SSL3_1_CERTIFICATE_REQUEST: return SSL3_1_TRANS_SERVER_CERT_REQ;
|
||
case SSL3_1_SERVER_HELLO_DONE: return SSL3_1_TRANS_SERVER_HELLO_DONE;
|
||
case SSL3_1_CERTIFICATE_VERIFY: return SSL3_1_TRANS_CLIENT_CERT_VERIFY;
|
||
case SSL3_1_CLIENT_KEY_EXCHANGE: return SSL3_1_TRANS_CLIENT_KEY_EXCHANGE;
|
||
|
||
case SSL3_1_FINISHED:
|
||
// Client- and server certificate handshake records lead
|
||
// to the same transition in the SSL automaton
|
||
// (see SSLDefines.h)
|
||
return SSL3_1_TRANS_CLIENT_FIN;
|
||
default:
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
void SSLv3_Interpreter::DeliverSSLv3_Record(SSLv3_HandshakeRecord* rec)
|
||
{
|
||
++SSLv3_Interpreter::totalRecords;
|
||
++SSLv3_Interpreter::handshakeRecords;
|
||
|
||
TableVal* currentCipherSuites = 0;
|
||
|
||
// First: consistency checks.
|
||
// Special treatment for finished messages, because they are
|
||
// already encrypted (encrypted handshake message).
|
||
if ( (change_cipher_client_seen && (rec->endp)->IsOrig() &&
|
||
! fin_client_seen) ||
|
||
(change_cipher_server_seen && ! rec->endp->IsOrig() &&
|
||
! fin_server_seen) )
|
||
{
|
||
// no checks can be performed due encryption...
|
||
}
|
||
else
|
||
{
|
||
SSL3_1_HandshakeType ht = SSL3_1_HandshakeType(rec->type);
|
||
switch ( ht ) {
|
||
case SSL3_1_HELLO_REQUEST:
|
||
if ( rec->length != 0 )
|
||
Weird("SSLv3x: Hello request too long!");
|
||
if ( ! helloRequestValid )
|
||
Weird("SSLv3x: Received hello request during handshake!");
|
||
// There should only be sent one hello request at a
|
||
// time.
|
||
helloRequestValid = false;
|
||
break;
|
||
|
||
case SSL3_1_CLIENT_HELLO:
|
||
{
|
||
++SSLv3_Interpreter::clientHelloRecords;
|
||
|
||
// During the handshaking phase, we don't want any
|
||
// more hello requests.
|
||
helloRequestValid = false;
|
||
|
||
if ( rec->checkClientHello() == 0 )
|
||
return;
|
||
|
||
const u_char* pTemp = rec->data;
|
||
uint8 sessionIDLength = uint8(pTemp[38]);
|
||
clientSessionID =
|
||
new SSL_DataBlock((pTemp + 39), sessionIDLength);
|
||
uint16 cipherSuiteLength =
|
||
uint16(pTemp[39 + sessionIDLength] << 8 ) |
|
||
pTemp[40 + sessionIDLength];
|
||
|
||
currentCipherSuites =
|
||
analyzeCiphers(rec->endp, cipherSuiteLength,
|
||
rec->data + 41 + sessionIDLength,
|
||
rec->sslVersion);
|
||
|
||
if ( ssl_store_key_material )
|
||
{
|
||
clientRandom = new SSLv3x_Random();
|
||
clientRandom->random_bytes = 0;
|
||
clientRandom->gmt_unix_time =
|
||
uint32(((pTemp[6] << 24) |
|
||
pTemp[7] << 16) |
|
||
pTemp[8] << 8) | pTemp[9];
|
||
|
||
clientRandom->random_bytes =
|
||
new SSL_DataBlock(pTemp + 10, 28);
|
||
}
|
||
break;
|
||
}
|
||
|
||
case SSL3_1_SERVER_HELLO:
|
||
{
|
||
++SSLv3_Interpreter::serverHelloRecords;
|
||
if ( rec->checkServerHello() == 0)
|
||
return;
|
||
|
||
const u_char* pTemp = rec->data;
|
||
uint8 sessionIDLength = uint8(pTemp[38]);
|
||
serverSessionID =
|
||
new SSL_DataBlock(pTemp + 39, sessionIDLength);
|
||
currentCipherSuites =
|
||
analyzeCiphers(rec->endp, 2,
|
||
rec->data + 39 + sessionIDLength,
|
||
rec->sslVersion);
|
||
|
||
// Check whether the cipher suite the server choose
|
||
// was included in the cipher suites the client
|
||
// anounced.
|
||
if ( pClientCipherSpecs && pCipherSuite )
|
||
{
|
||
bool bFound = false;
|
||
uint16 tempClientCipher;
|
||
for ( int i = 0; i < pClientCipherSpecs->len;
|
||
i += 2 )
|
||
{
|
||
tempClientCipher =
|
||
(pClientCipherSpecs->data[i] << 8) |
|
||
pClientCipherSpecs->data[i+1];
|
||
|
||
if ( tempClientCipher ==
|
||
pCipherSuite->identifier )
|
||
{
|
||
bFound = true;
|
||
i = pClientCipherSpecs->len;
|
||
}
|
||
}
|
||
|
||
if ( ! bFound )
|
||
Weird("SSLv3x: Server choosed cipher spec that client didn't anounce!");
|
||
|
||
delete pClientCipherSpecs;
|
||
pClientCipherSpecs = 0;
|
||
}
|
||
|
||
if ( ssl_store_key_material )
|
||
{
|
||
serverRandom = new SSLv3x_Random();
|
||
serverRandom->gmt_unix_time =
|
||
uint32(((pTemp[6] << 8) |
|
||
pTemp[7] << 8) |
|
||
pTemp[8] << 8) | pTemp[9];
|
||
serverRandom->random_bytes =
|
||
new SSL_DataBlock(pTemp + 10, 28);
|
||
}
|
||
|
||
// Insert session injection into here.
|
||
|
||
if ( ! ssl_session_insertion )
|
||
break; // in place of below
|
||
|
||
TableVal* sessionIDTable =
|
||
serverSessionID ?
|
||
MakeSessionID(serverSessionID->data,
|
||
serverSessionID->len) :
|
||
MakeSessionID(0, 0);
|
||
|
||
val_list* vl = new val_list;
|
||
vl->append(proxy->BuildConnVal());
|
||
vl->append(sessionIDTable);
|
||
|
||
proxy->ConnectionEvent(ssl_session_insertion, vl);
|
||
break;
|
||
}
|
||
|
||
case SSL3_1_CERTIFICATE:
|
||
{
|
||
const u_char* pData = rec->data;
|
||
uint32 certListLength =
|
||
uint32((pData[4] << 16) |
|
||
pData[5] << 8) | pData[6];
|
||
|
||
// Sum of all cert sizes has to match
|
||
// certListLength.
|
||
uint tempLength = 0;
|
||
uint certCount = 0;
|
||
while ( tempLength < certListLength )
|
||
{
|
||
if ( tempLength + 3 <= certListLength )
|
||
{
|
||
++certCount;
|
||
uint32 certLength =
|
||
uint32((pData[tempLength + 7] << 16) | pData[tempLength + 8] << 8) | pData[tempLength + 9];
|
||
tempLength += certLength + 3;
|
||
}
|
||
else
|
||
{
|
||
Weird("SSLv3x: Corrupt length field in certificate list!");
|
||
return;
|
||
}
|
||
}
|
||
|
||
if ( tempLength > certListLength )
|
||
{
|
||
Weird("SSLv3x: sum of size of certificates doesn't match size of certificate chain");
|
||
return;
|
||
}
|
||
|
||
SSL_InterpreterEndpoint* pEp =
|
||
(SSL_InterpreterEndpoint*) rec->endp;
|
||
|
||
if ( certCount == 0 )
|
||
{
|
||
// we don't have a certificate, but this is valid
|
||
// according to RFC2246
|
||
if ( rec->endp->IsOrig() )
|
||
{
|
||
Weird("SSLv3x: Client certificate is missing!");
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
Weird("SSLv3x: Server certificate is missing!");
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( certCount > 1 )
|
||
{ // we have a chain
|
||
analyzeCertificate(pEp,
|
||
rec->data + 7,
|
||
certListLength, 1, true);
|
||
}
|
||
else
|
||
{
|
||
// We have a single certificate.
|
||
// FIXME.
|
||
analyzeCertificate(pEp,
|
||
rec->data + 10,
|
||
certListLength-3, 1, false);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case SSL3_1_SERVER_KEY_EXCHANGE:
|
||
{
|
||
/*
|
||
switch (cipherSuite)
|
||
{
|
||
// It would be necessary to have the RSA key length
|
||
// out of the server's certificate. If the cipher suite
|
||
// is EXPORT, than a RSA key length larger than 512 bits
|
||
// is not allowed for encryption and thus, the server needs
|
||
// to send a key-exchange-message in order to negotiate the
|
||
// pre-master secret (see rfc 2246 page 39)
|
||
case TLS_RSA_WITH_NULL_MD5:
|
||
case TLS_RSA_WITH_NULL_SHA:
|
||
// case TLS_RSA_EXPORT_WITH_RC4_40_MD5: //see comment above
|
||
case TLS_RSA_WITH_RC4_128_MD5:
|
||
case TLS_RSA_WITH_RC4_128_SHA:
|
||
// case TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5: //see comment above
|
||
case TLS_RSA_WITH_IDEA_CBC_SHA:
|
||
// case TLS_RSA_EXPORT_WITH_DES40_CBC_SHA: //see comment above
|
||
case TLS_RSA_WITH_DES_CBC_SHA:
|
||
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
|
||
case TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA:
|
||
case TLS_DH_DSS_WITH_DES_CBC_SHA:
|
||
case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
|
||
case TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA:
|
||
case TLS_DH_RSA_WITH_DES_CBC_SHA:
|
||
case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
|
||
{
|
||
Weird("SSLv3x: Sending server-key-exchange not allowed for this cipher suite!");
|
||
return;
|
||
break;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
*/
|
||
|
||
if ( ! pCipherSuite )
|
||
// If we have an unknown CIPHER-SPEC,
|
||
// we can't do our weird checks.
|
||
break;
|
||
|
||
SSL_KeyExchangeAlgorithm keyXAlgorithm =
|
||
pCipherSuite->keyExchangeAlgorithm;
|
||
|
||
if ( keyXAlgorithm == SSL_KEY_EXCHANGE_RSA ||
|
||
keyXAlgorithm == SSL_KEY_EXCHANGE_DH_DSS ||
|
||
keyXAlgorithm == SSL_KEY_EXCHANGE_DH_RSA )
|
||
{
|
||
Weird("SSLv3x: Sending server-key-exchange not allowed for this cipher suite!");
|
||
return;
|
||
|
||
}
|
||
// FIXME: check where DHE_RSA etc. belongs to
|
||
const u_char* pTemp = rec->data;
|
||
if ( ssl_store_key_material )
|
||
{
|
||
if ( keyXAlgorithm == SSL_KEY_EXCHANGE_RSA ||
|
||
keyXAlgorithm == SSL_KEY_EXCHANGE_RSA ||
|
||
keyXAlgorithm == SSL_KEY_EXCHANGE_RSA_EXPORT1024 )
|
||
{ // some weird checks
|
||
if ( rec->length < 2 )
|
||
{
|
||
Weird("SSLv3x: server-key-exchange empty!");
|
||
return;
|
||
}
|
||
|
||
uint16 modulusLength = uint16(pTemp[4] << 8 ) | pTemp[5];
|
||
if ( modulusLength + 4 > rec->length )
|
||
{
|
||
Weird("SSLv3x: Corrupt length fields in server-key-exchange!");
|
||
break;
|
||
}
|
||
|
||
uint16 exponentLength = uint16(pTemp[6 + modulusLength] << 8 ) | pTemp[7 + modulusLength];
|
||
if ( modulusLength + exponentLength + 4 > rec->length )
|
||
{
|
||
Weird("SSLv3x: Corrupt length fields in server-key-exchange!");
|
||
return;
|
||
}
|
||
|
||
serverRSApars =
|
||
new SSLv3x_ServerRSAParams;
|
||
serverRSApars->rsa_modulus =
|
||
new SSL_DataBlock(pTemp + 6, modulusLength);
|
||
serverRSApars->rsa_exponent =
|
||
new SSL_DataBlock( pTemp + 8 + modulusLength, exponentLength);
|
||
}
|
||
else
|
||
{
|
||
if ( keyXAlgorithm == SSL_KEY_EXCHANGE_DH || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_DSS || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_DSS_EXPORT || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_RSA || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_RSA_EXPORT || keyXAlgorithm == SSL_KEY_EXCHANGE_DHE_DSS || keyXAlgorithm == SSL_KEY_EXCHANGE_DHE_DSS_EXPORT || keyXAlgorithm == SSL_KEY_EXCHANGE_DHE_RSA || keyXAlgorithm == SSL_KEY_EXCHANGE_DHE_RSA_EXPORT || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_anon || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_anon_EXPORT || keyXAlgorithm == SSL_KEY_EXCHANGE_DHE_DSS_EXPORT1024 )
|
||
{
|
||
if ( rec->length < 2 )
|
||
{
|
||
Weird("SSLv3x: server-key-exchange empty!");
|
||
return;
|
||
}
|
||
|
||
uint16 dh_pLength = (uint16) (pTemp[4] << 8 ) | pTemp[5];
|
||
if ( dh_pLength + 4 > rec->length )
|
||
{
|
||
Weird("SSLv3x: Corrupt length fields in server-key-exchange!");
|
||
break;
|
||
}
|
||
|
||
uint16 dh_gLength = uint16(pTemp[6 + dh_pLength] << 8 ) | pTemp[7 + dh_pLength];
|
||
uint16 dh_YsLength = uint16(pTemp[8 + dh_pLength + dh_gLength] << 8 ) | pTemp[9 + dh_pLength + dh_gLength];
|
||
if ( dh_pLength + dh_gLength + dh_YsLength + 6 > rec->length )
|
||
{
|
||
Weird("SSLv3x: Corrupt length fields in server-key-exchange!");
|
||
printf("xxx %u > %u \n", (dh_pLength + dh_gLength + dh_YsLength + 6), rec->length);
|
||
return;
|
||
}
|
||
|
||
serverDHPars = new SSLv3x_ServerDHParams;
|
||
serverDHPars->dh_p = new SSL_DataBlock(pTemp + 6 , dh_pLength);
|
||
serverDHPars->dh_g = new SSL_DataBlock(pTemp + 8 + dh_pLength, dh_gLength);
|
||
serverDHPars->dh_Ys = new SSL_DataBlock(pTemp + 10 + dh_pLength + dh_gLength, dh_YsLength);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
case SSL3_1_CERTIFICATE_REQUEST:
|
||
{
|
||
// Only if server not anonymous
|
||
/*
|
||
switch (cipherSuite)
|
||
{
|
||
case TLS_NULL_WITH_NULL_NULL:
|
||
case TLS_DH_anon_EXPORT_WITH_RC4_40_MD5:
|
||
case TLS_DH_anon_WITH_RC4_128_MD5:
|
||
case TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA:
|
||
case TLS_DH_anon_WITH_DES_CBC_SHA:
|
||
case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
|
||
{
|
||
Weird("SSLv3x: Sending certificate-request not allowed for anonymous servers!");
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
*/
|
||
|
||
if ( ! pCipherSuite )
|
||
{
|
||
// if we have an unknown CIPHER-SPEC,
|
||
// we can't do our weird checks.
|
||
break;
|
||
}
|
||
|
||
if ( pCipherSuite->keyExchangeAlgorithm == SSL_KEY_EXCHANGE_DH_anon || pCipherSuite->keyExchangeAlgorithm == SSL_KEY_EXCHANGE_DH_anon_EXPORT )
|
||
Weird("SSLv3x: Sending certificate-request not allowed for anonymous servers!");
|
||
|
||
// FIXME: Insert weird checks!
|
||
break;
|
||
}
|
||
|
||
case SSL3_1_SERVER_HELLO_DONE:
|
||
{
|
||
if ( rec->length != 0 )
|
||
Weird("SSLv3x: Server hello too long!");
|
||
break;
|
||
}
|
||
|
||
case SSL3_1_CLIENT_KEY_EXCHANGE:
|
||
{
|
||
if ( ! pCipherSuite )
|
||
// if we have an unknown CIPHER-SPEC,
|
||
// we can't do our weird checks
|
||
break;
|
||
|
||
const u_char* pTemp = rec->data;
|
||
if ( ssl_store_key_material )
|
||
{
|
||
SSL_KeyExchangeAlgorithm keyXAlgorithm =
|
||
pCipherSuite->keyExchangeAlgorithm;
|
||
|
||
if ( keyXAlgorithm == SSL_KEY_EXCHANGE_RSA || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_DSS || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_RSA )
|
||
{
|
||
encryptedPreSecret =
|
||
new SSLv3x_EncryptedPremasterSecret;
|
||
encryptedPreSecret->encryptedSecret =
|
||
new SSL_DataBlock( pTemp + 4, rec->length);
|
||
}
|
||
else
|
||
{
|
||
if ( keyXAlgorithm == SSL_KEY_EXCHANGE_DH || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_DSS || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_DSS_EXPORT || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_RSA || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_RSA_EXPORT || keyXAlgorithm == SSL_KEY_EXCHANGE_DHE_DSS || keyXAlgorithm == SSL_KEY_EXCHANGE_DHE_DSS_EXPORT || keyXAlgorithm == SSL_KEY_EXCHANGE_DHE_RSA || keyXAlgorithm == SSL_KEY_EXCHANGE_DHE_RSA_EXPORT || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_anon || keyXAlgorithm == SSL_KEY_EXCHANGE_DH_anon_EXPORT || keyXAlgorithm == SSL_KEY_EXCHANGE_DHE_DSS_EXPORT1024 )
|
||
{
|
||
if ( rec->length < 2 )
|
||
{
|
||
// This can happen (see RFC 2246, p. 45).
|
||
return;
|
||
}
|
||
|
||
uint16 DHpublicLength =
|
||
uint16(pTemp[4] << 8) | pTemp[5];
|
||
if ( DHpublicLength + 2 < rec->length )
|
||
{
|
||
Weird("SSLv3x:<3A>Corrupt length fields in client-key-exchange!");
|
||
return;
|
||
}
|
||
|
||
clientDHpublic = new SSLv3x_ClientDHPublic;
|
||
clientDHpublic->dh_Yc = new SSL_DataBlock(pTemp + 6, DHpublicLength);
|
||
}
|
||
}
|
||
|
||
}
|
||
break;
|
||
}
|
||
|
||
case SSL3_1_CERTIFICATE_VERIFY:
|
||
{
|
||
// FIXME: Insert Weird checks!
|
||
break;
|
||
}
|
||
|
||
case SSL3_1_FINISHED:
|
||
{
|
||
// We won't get here, because finished messages
|
||
// are already encrypted, so we can't get
|
||
// the content type of this handshake-message...
|
||
break;
|
||
}
|
||
|
||
default:
|
||
{
|
||
if ( currentState == SSL3_1_STATE_SERVER_FIN_SENT_A ||
|
||
currentState == SSL3_1_STATE_CLIENT_FIN_SENT_B )
|
||
{
|
||
Weird("SSLv3x: Handshake message (unknown type) after finished message!");
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
Weird("SSLv3x: Invalid HandshakeType! Maybe finished message without predecessing change-cipher-message!");
|
||
return; }
|
||
}
|
||
}
|
||
}
|
||
|
||
int oldState = currentState;
|
||
bool alreadySwitchedState = false;
|
||
|
||
// First: Special handling of finished messages. They must be
|
||
// sent immediately after a change cipher message - already encrypted.
|
||
// from client?
|
||
if ( rec->endp->IsOrig() && change_cipher_client_seen )
|
||
{
|
||
if ( ! fin_client_seen )
|
||
{
|
||
// This must be a (valid) client finished.
|
||
// We assume it to be one, because the predecessing
|
||
// message was a change cipher.
|
||
fin_client_seen = true;
|
||
change_cipher_client_seen = false;
|
||
alreadySwitchedState = true;
|
||
currentState = sslAutomaton.getNextState(currentState,
|
||
SSL3_1_TRANS_CLIENT_FIN);
|
||
}
|
||
else
|
||
{
|
||
// We already saw a client finished (should not be
|
||
// possible).
|
||
Weird("SSLv3x: Already received client finished message!");
|
||
currentState = sslAutomaton.getNextState(currentState,
|
||
SSL3_1_TRANS_CLIENT_FIN);
|
||
fin_client_seen = true;
|
||
change_cipher_client_seen = false;
|
||
alreadySwitchedState = true;
|
||
}
|
||
}
|
||
|
||
// from server
|
||
else if ( ! rec->endp->IsOrig() && change_cipher_server_seen )
|
||
{
|
||
if ( ! fin_server_seen )
|
||
{
|
||
// This must be a (valid) server finished.
|
||
// We assume it to be one, because the predecessing
|
||
// message was a change cipher.
|
||
fin_server_seen = true;
|
||
change_cipher_server_seen = false;
|
||
alreadySwitchedState = true;
|
||
currentState = sslAutomaton.getNextState(currentState,
|
||
SSL3_1_TRANS_SERVER_FIN);
|
||
}
|
||
else
|
||
{
|
||
// We already saw a server-finished (should not be
|
||
// possible).
|
||
Weird("SSLv3x: Already received server finished message!");
|
||
currentState = sslAutomaton.getNextState(currentState,
|
||
SSL3_1_TRANS_SERVER_FIN);
|
||
alreadySwitchedState = true;
|
||
fin_server_seen = true;
|
||
change_cipher_server_seen = false;
|
||
}
|
||
}
|
||
|
||
if ( ! alreadySwitchedState )
|
||
{
|
||
// Check whether we are already finished with the
|
||
// handshaking process...
|
||
switch ( currentState ) {
|
||
case SSL3_1_STATE_HS_FIN_A:
|
||
case SSL3_1_STATE_HS_FIN_B:
|
||
Weird("SSLv3x: Received handshake message after finishing handshake!");
|
||
break;
|
||
|
||
default:
|
||
// It's a "normal" handshake message...
|
||
currentState = sslAutomaton.getNextState(currentState,
|
||
HandshakeType2Trans(rec->type));
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( currentState == SSL3_1_STATE_ERROR )
|
||
{
|
||
// proxy->SetSkip(1);
|
||
}
|
||
|
||
// Only if we changed the currentState, we need to call GenerateEvents
|
||
// because event generation in GenerateEvents() is based on
|
||
// currentState.
|
||
if ( oldState != currentState )
|
||
GenerateEvents(rec, currentCipherSuites);
|
||
else
|
||
Unref(currentCipherSuites);
|
||
}
|
||
|
||
void SSLv3_Interpreter::DeliverSSLv3_Record(SSLv3_AlertRecord* rec)
|
||
{
|
||
++SSLv3_Interpreter::totalRecords;
|
||
++SSLv3_Interpreter::alertRecords;
|
||
|
||
// First: consistency-checks.
|
||
// Only if handshake not already finished.
|
||
// Otherwise alerts may be encrypted, so we could do nothing...
|
||
if ( currentState == SSL3_1_STATE_SERVER_FIN_SENT_A ||
|
||
currentState == SSL3_1_STATE_CLIENT_FIN_SENT_B ||
|
||
currentState == SSL3_1_STATE_CLIENT_FIN_SENT_A ||
|
||
currentState == SSL3_1_STATE_SERVER_FIN_SENT_B ||
|
||
currentState == SSL3_1_STATE_HS_FIN_A ||
|
||
currentState == SSL3_1_STATE_HS_FIN_B ||
|
||
change_cipher_client_seen || change_cipher_server_seen )
|
||
return;
|
||
|
||
if ( rec->level != SSL3x_ALERT_LEVEL_WARNING &&
|
||
rec->level != SSL3x_ALERT_LEVEL_FATAL )
|
||
Weird("SSLv3x: Unknown ssl alert level");
|
||
|
||
SSL3_1_AlertDescription ad = SSL3_1_AlertDescription(rec->description);
|
||
switch ( ad ) {
|
||
case SSL3_1_CLOSE_NOTIFY:
|
||
case SSL3_1_UNEXPECTED_MESSAGE:
|
||
case SSL3_1_BAD_RECORD_MAC:
|
||
case SSL3_1_DECRYPTION_FAILED:
|
||
case SSL3_1_RECORD_OVERFLOW:
|
||
case SSL3_1_DECOMPRESSION_FAILURE:
|
||
case SSL3_1_HANDSHAKE_FAILURE:
|
||
break;
|
||
|
||
case SSL3_0_NO_CERTIFICATE:
|
||
// This may happen ONLY in SSLv3.0 when the server sends
|
||
// a certificate request but the client has none.
|
||
if ( rec->sslVersion == SSLProxy_Analyzer::SSLv30 )
|
||
currentState = SSL3_1_STATE_SERVER_HELLO_DONE_SENT_A;
|
||
else
|
||
Weird("SSLv3x: No certificate alert not defined for SSL 3.1!");
|
||
break;
|
||
|
||
case SSL3_1_BAD_CERTIFICATE:
|
||
case SSL3_1_UNSUPPORTED_CERTIFICATE:
|
||
case SSL3_1_CERTIFICATE_REVOKED:
|
||
case SSL3_1_CERTIFICATE_EXPIRED:
|
||
case SSL3_1_CERTIFICATE_UNKNOWN:
|
||
case SSL3_1_ILLEGAL_PARAMETER:
|
||
case SSL3_1_UNKNOWN_CA:
|
||
case SSL3_1_ACCESS_DENIED:
|
||
case SSL3_1_DECODE_ERROR:
|
||
case SSL3_1_DECRYPT_ERROR:
|
||
case SSL3_1_EXPORT_RESTRICTION:
|
||
case SSL3_1_PROTOCOL_VERSION:
|
||
case SSL3_1_INSUFFICIENT_SECURITY:
|
||
case SSL3_1_INTERNAL_ERROR:
|
||
case SSL3_1_USER_CANCELED:
|
||
case SSL3_1_NO_RENEGOTIATION:
|
||
break;
|
||
|
||
default:
|
||
Weird(" SSLv3x: Unknown ssl alert description!" );
|
||
break;
|
||
}
|
||
|
||
if ( rec->level == 2 )
|
||
// Fatal alert!
|
||
currentState = SSL3_1_STATE_INIT;
|
||
|
||
if ( rec->level == 1 && ad == SSL3_1_CLOSE_NOTIFY )
|
||
currentState = SSL3_1_STATE_INIT;
|
||
|
||
fire_ssl_conn_alert(rec->sslVersion, rec->level, rec->description);
|
||
}
|
||
|
||
void SSLv3_Interpreter::DeliverSSLv3_Record(SSLv3_ChangeCipherRecord* rec)
|
||
{
|
||
++SSLv3_Interpreter::totalRecords;
|
||
++SSLv3_Interpreter::changeCipherRecords;
|
||
|
||
if ( rec->type != 1 )
|
||
Weird("SSLv3x: Unknown change cipher type!");
|
||
if ( rec->recordLength != 1 )
|
||
Weird("SSLv3x: Change cipher message too long!");
|
||
|
||
// After receiving a change cipher spec message, the next message sent
|
||
// MUST be a finished message. So we set the appropriate flag:
|
||
// change_cipher_client/server_seen.
|
||
if ( rec->endp->IsOrig())
|
||
{
|
||
if ( change_cipher_client_seen )
|
||
Weird("SSLv3x: Received multiple change cipher message from client!");
|
||
change_cipher_client_seen = true;
|
||
fin_client_seen = false;
|
||
}
|
||
else
|
||
{
|
||
if ( change_cipher_server_seen )
|
||
Weird("SSLv3x: Received multiple change cipher message from server!");
|
||
change_cipher_server_seen = true;
|
||
fin_server_seen = false;
|
||
}
|
||
|
||
if ( currentState == SSL3_1_STATE_ERROR )
|
||
{
|
||
// proxy->SetSkip(1);
|
||
}
|
||
|
||
// We don't need a GenerateEvents here, because we didn't change
|
||
// the currentState of the SSL automaton. (Event generation
|
||
// in GenerateEvents() is done based on currentState.)
|
||
// GenerateEvents(rec);
|
||
}
|
||
|
||
void SSLv3_Interpreter::DeliverSSLv3_Record(SSLv3_ApplicationRecord* rec)
|
||
{
|
||
++SSLv3_Interpreter::totalRecords;
|
||
|
||
if ( currentState == SSL3_1_STATE_HS_FIN_A ||
|
||
currentState == SSL3_1_STATE_HS_FIN_B )
|
||
// O.K., sending application data is valid
|
||
// this was the last record we analyzed...
|
||
proxy->SetSkip(1);
|
||
else
|
||
{
|
||
// Sending application data now is not valid, so the SSL
|
||
// connection is probably already established and we
|
||
// didn't get the handshake.
|
||
Weird("SSLv3_data_without_full_handshake");
|
||
currentState = SSL3_1_STATE_ERROR;
|
||
GenerateEvents(rec, 0);
|
||
}
|
||
}
|
||
|
||
TableVal* SSLv3_Interpreter::analyzeCiphers(const SSLv3_Endpoint* s, int length,
|
||
const u_char* data, uint16 version)
|
||
{
|
||
int is_orig = (SSL_InterpreterEndpoint*) s == orig;
|
||
|
||
const u_char* pCipher = data;
|
||
SSL_CipherSpec* pCipherSuiteTemp = 0;
|
||
uint16 cipherSuite;
|
||
for ( int i = 0; i < length; i += 2 )
|
||
{
|
||
cipherSuite = uint16(pCipher[0+i] << 8) | pCipher[1+i];
|
||
HashKey h(static_cast<bro_uint_t>(cipherSuite));
|
||
|
||
pCipherSuiteTemp =
|
||
(SSL_CipherSpec*) SSL_CipherSpecDict.Lookup(&h);
|
||
if ( ! pCipherSuiteTemp )
|
||
{
|
||
if ( is_orig )
|
||
proxy->Weird("SSLv3x: Unknown CIPHER-SPEC in CLIENT-HELLO");
|
||
else
|
||
proxy->Weird("SSLv3x: Unknown CIPHER-SPEC in SERVER-HELLO");
|
||
}
|
||
}
|
||
|
||
// Store server's cipher specs.
|
||
if ( ! is_orig )
|
||
{
|
||
pCipherSuite = pCipherSuiteTemp;
|
||
if ( ! pCipherSuite )
|
||
{
|
||
// Special case: we store the identifier directly
|
||
// for unknown cipher-specs.
|
||
cipherSuiteIdentifier =
|
||
uint16(pCipher[0] << 8) | pCipher[1];
|
||
}
|
||
}
|
||
|
||
if ( ssl_compare_cipherspecs && length <= ssl_max_cipherspec_size )
|
||
{
|
||
// Store cipher specs for analysis: was the choosen
|
||
// server cipher suite announced by the client?
|
||
if ( is_orig )
|
||
{
|
||
pClientCipherSpecs =
|
||
new SSL_DataBlock(data, length);
|
||
}
|
||
}
|
||
|
||
if ( (! is_orig && ssl_conn_server_reply) ||
|
||
(is_orig && ssl_conn_attempt) )
|
||
{
|
||
TableVal* pCipherTable = new TableVal(cipher_suites_list);
|
||
for ( int i = 0; i < length; i += 2 )
|
||
{
|
||
uint32 cipherSpec = (pCipher[0] << 8) | pCipher[1];
|
||
Val* index = new Val(cipherSpec, TYPE_COUNT);
|
||
pCipherTable->Assign(index, 0);
|
||
Unref(index);
|
||
pCipher += 2;
|
||
}
|
||
|
||
return pCipherTable;
|
||
}
|
||
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
void SSLv3_Interpreter::GenerateEvents(SSLv3_Record* rec, TableVal* curCipherSuites)
|
||
{
|
||
if ( curCipherSuites &&
|
||
currentState != SSL3_1_STATE_CLIENT_HELLO_SENT &&
|
||
currentState != SSL3_1_STATE_SERVER_HELLO_SENT )
|
||
// Unref here, since the events won't do so in this case.
|
||
Unref(curCipherSuites);
|
||
|
||
switch ( currentState ) {
|
||
case SSL3_1_STATE_CLIENT_HELLO_SENT:
|
||
fire_ssl_conn_attempt(rec->sslVersion, curCipherSuites);
|
||
break;
|
||
|
||
case SSL3_1_STATE_SERVER_HELLO_SENT:
|
||
fire_ssl_conn_server_reply(rec->sslVersion, curCipherSuites);
|
||
break;
|
||
|
||
case SSL3_1_STATE_HS_FIN_A:
|
||
case SSL3_1_STATE_HS_FIN_B:
|
||
++SSLv3_Interpreter::openedConnections;
|
||
fire_ssl_conn_established(rec->sslVersion,
|
||
pCipherSuite ?
|
||
pCipherSuite->identifier : 0);
|
||
|
||
// We finished handshake. Skip all further data.
|
||
proxy->SetSkip(1);
|
||
helloRequestValid = true;
|
||
break;
|
||
|
||
case SSL3_1_STATE_SERVER_FIN_SENT_B:
|
||
// First, check for session-ID match.
|
||
if ( clientSessionID && serverSessionID &&
|
||
memcmp(clientSessionID->data, serverSessionID->data,
|
||
clientSessionID->len) != 0 )
|
||
Weird("SSLv3x: Reusing session but session ID mismatch!");
|
||
fire_ssl_conn_reused(serverSessionID);
|
||
break;
|
||
|
||
case SSL3_1_STATE_ERROR:
|
||
Weird("unexpected_SSLv3_record");
|
||
proxy->SetSkip(1);
|
||
}
|
||
}
|
||
|
||
void SSLv3_Interpreter::SetState(int i)
|
||
{
|
||
if ( i >= 0 && i < SSL3_1_NUM_STATES )
|
||
currentState = i;
|
||
}
|
||
|
||
// ---SSLv3_Endpoint--------------------------------------------------------------
|
||
|
||
SSLv3_Endpoint::SSLv3_Endpoint(SSL_Interpreter* interpreter, int is_orig)
|
||
: SSL_InterpreterEndpoint(interpreter, is_orig)
|
||
{
|
||
sslVersion = 0;
|
||
}
|
||
|
||
SSLv3_Endpoint::~SSLv3_Endpoint()
|
||
{
|
||
}
|
||
|
||
void SSLv3_Endpoint::Deliver(int len, const u_char* data)
|
||
{
|
||
if ( SSL3_1_LENGTHOFFSET + sizeof(uint16) <= unsigned(len) )
|
||
{
|
||
currentMessage_length =
|
||
uint16(data[SSL3_1_LENGTHOFFSET] << 8) |
|
||
data[SSL3_1_LENGTHOFFSET+1];
|
||
|
||
// ### where does this magic number come from?
|
||
if ( currentMessage_length > 18432 )
|
||
interpreter->Weird("SSLv3x: Message length too long!");
|
||
}
|
||
else
|
||
{
|
||
interpreter->Weird("SSLv3x: Could not determine message length!");
|
||
return;
|
||
}
|
||
|
||
if ( currentMessage_length + 2 + SSL3_1_LENGTHOFFSET != len )
|
||
{
|
||
// This should never happen; otherwise there is a bug in the
|
||
// SSL_RecordBuilder.
|
||
interpreter->Weird("SSLv3x: FATAL: recordLength doesn't match data block length!");
|
||
interpreter->Proxy()->SetSkip(1);
|
||
return;
|
||
}
|
||
|
||
ProcessMessage(data, len);
|
||
}
|
||
|
||
void SSLv3_Endpoint::ProcessMessage(const u_char* data, int len)
|
||
{
|
||
SSL3_1_ContentType ct = ExtractContentType(data, len);
|
||
if ( ! ExtractVersion(data, len) )
|
||
return;
|
||
|
||
switch ( ct ) {
|
||
case SSL3_1_TYPE_CHANGE_CIPHER_SPEC:
|
||
{
|
||
SSLv3_ChangeCipherRecord* rec = new
|
||
SSLv3_ChangeCipherRecord(data + SSL3_1_HEADERLENGTH,
|
||
len - SSL3_1_HEADERLENGTH, sslVersion, this);
|
||
|
||
// Multiple handshake messages may be coalesced into
|
||
// a single record.
|
||
rec->Deliver((SSLv3_Interpreter*) interpreter);
|
||
Unref(rec);
|
||
break;
|
||
}
|
||
|
||
case SSL3_1_TYPE_ALERT:
|
||
{
|
||
SSLv3_AlertRecord* rec = new
|
||
SSLv3_AlertRecord(data + SSL3_1_HEADERLENGTH,
|
||
len - SSL3_1_HEADERLENGTH, sslVersion, this);
|
||
rec->Deliver((SSLv3_Interpreter*) interpreter);
|
||
Unref(rec);
|
||
break;
|
||
}
|
||
|
||
case SSL3_1_TYPE_HANDSHAKE:
|
||
{
|
||
SSLv3_HandshakeRecord* rec =
|
||
new SSLv3_HandshakeRecord(data + SSL3_1_HEADERLENGTH,
|
||
len - SSL3_1_HEADERLENGTH, sslVersion, this);
|
||
rec->Deliver((SSLv3_Interpreter*) interpreter);
|
||
Unref(rec);
|
||
break;
|
||
}
|
||
|
||
case SSL3_1_TYPE_APPLICATION_DATA:
|
||
{
|
||
SSLv3_ApplicationRecord* rec =
|
||
new SSLv3_ApplicationRecord(data + SSL3_1_HEADERLENGTH,
|
||
len - SSL3_1_HEADERLENGTH, sslVersion, this);
|
||
rec->Deliver((SSLv3_Interpreter*) interpreter);
|
||
Unref(rec);
|
||
break;
|
||
}
|
||
|
||
default:
|
||
{
|
||
interpreter->Weird("SSLv3x: Could not determine content type!");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
SSL3_1_ContentType SSLv3_Endpoint::ExtractContentType(const u_char* data,
|
||
int len)
|
||
{
|
||
return SSL3_1_ContentType(uint8(*(data + SSL3_1_CONTENTTYPEOFFSET)));
|
||
}
|
||
|
||
int SSLv3_Endpoint::ExtractVersion(const u_char* data, int len)
|
||
{
|
||
sslVersion = uint16(data[SSL3_1_VERSIONTYPEOFFSET] << 8) |
|
||
data[SSL3_1_VERSIONTYPEOFFSET + 1];
|
||
|
||
if ( sslVersion != SSLProxy_Analyzer::SSLv30 &&
|
||
sslVersion != SSLProxy_Analyzer::SSLv31 )
|
||
{
|
||
interpreter->Weird("SSLv3x: Unsupported SSL-Version (not SSLv3x)!");
|
||
return 0;
|
||
}
|
||
else
|
||
return 1;
|
||
}
|
||
|
||
// ---SSLv3_Record----------------------------------------------------------------
|
||
|
||
SSLv3_Record::SSLv3_Record(const u_char* data, int len,
|
||
uint16 version, SSLv3_Endpoint const* e)
|
||
{
|
||
recordLength = len;
|
||
sslVersion = version;
|
||
endp = e;
|
||
this->data = data;
|
||
}
|
||
|
||
SSLv3_Record::~SSLv3_Record()
|
||
{
|
||
// The memory for data is deleted after processing the ssl record
|
||
// in the common ssl reassembler.
|
||
}
|
||
|
||
void SSLv3_Record::Describe(ODesc* d) const
|
||
{
|
||
d->Add("sslrecord");
|
||
}
|
||
|
||
SSLv3_Endpoint const* SSLv3_Record::GetEndpoint() const
|
||
{
|
||
return endp;
|
||
}
|
||
|
||
const u_char* SSLv3_Record::GetData() const
|
||
{
|
||
return data;
|
||
}
|
||
|
||
int SSLv3_Record::ExtractInt24(const u_char* data, int len, int offset)
|
||
{
|
||
if ( offset + int(sizeof(unsigned long)) - 1 > len)
|
||
return 0;
|
||
|
||
uint32 val;
|
||
|
||
val = 0;
|
||
val = uint32(*(data + offset + 2));
|
||
val |= uint32(*(data + offset + 1)) << 8;
|
||
val |= uint32(*(data + offset)) << 16;
|
||
|
||
return val;
|
||
}
|
||
|
||
int SSLv3_Record::GetRecordLength() const
|
||
{
|
||
return recordLength;
|
||
}
|
||
|
||
SSLv3_HandshakeRecord::SSLv3_HandshakeRecord(const u_char* data, int len,
|
||
uint16 version, SSLv3_Endpoint const* e)
|
||
: SSLv3_Record(data, len, version, e)
|
||
{
|
||
// Don't analyze encrypted client handshake messages.
|
||
if ( e->IsOrig() &&
|
||
((SSLv3_Interpreter*) e->Interpreter())->change_cipher_client_seen &&
|
||
! ((SSLv3_Interpreter*) e->Interpreter())->fin_client_seen )
|
||
{
|
||
type = 255;
|
||
length = 0;
|
||
next = 0;
|
||
return;
|
||
}
|
||
|
||
// Don't analyze encrypted server handshake messages.
|
||
if ( ! e->IsOrig() &&
|
||
((SSLv3_Interpreter*) e->Interpreter())->change_cipher_server_seen &&
|
||
! ((SSLv3_Interpreter*) e->Interpreter())->fin_server_seen )
|
||
{
|
||
type = 255;
|
||
length = 0;
|
||
next = 0;
|
||
return;
|
||
}
|
||
|
||
type = uint8(*(this->data));
|
||
length = ExtractInt24(data, len, 1);
|
||
|
||
if ( length == 0 ) // this is a special case to deal with 0 length certs
|
||
next = 0;
|
||
else if ( length + 4 < len )
|
||
next = new SSLv3_HandshakeRecord(data + length + 4,
|
||
len - (length + 4), version, e);
|
||
else if ( length + 4 > len )
|
||
{
|
||
e->Interpreter()->Weird("SSLv3x: Handshake-header-length inconsistent (too big)");
|
||
next = 0;
|
||
}
|
||
else
|
||
next = 0;
|
||
}
|
||
|
||
SSLv3_HandshakeRecord::~SSLv3_HandshakeRecord()
|
||
{
|
||
if ( next )
|
||
{
|
||
delete next;
|
||
}
|
||
}
|
||
|
||
void SSLv3_HandshakeRecord::Deliver(SSLv3_Interpreter* conn)
|
||
{
|
||
SSLv3_HandshakeRecord* it = this;
|
||
while ( it != 0)
|
||
{
|
||
conn->DeliverSSLv3_Record(it);
|
||
it = it->GetNext();
|
||
}
|
||
}
|
||
|
||
int SSLv3_HandshakeRecord::GetType() const
|
||
{
|
||
return type;
|
||
}
|
||
|
||
int SSLv3_HandshakeRecord::GetLength() const
|
||
{
|
||
return length;
|
||
}
|
||
|
||
SSLv3_HandshakeRecord* SSLv3_HandshakeRecord::GetNext()
|
||
{
|
||
return next;
|
||
}
|
||
|
||
int SSLv3_HandshakeRecord::checkClientHello()
|
||
{
|
||
if ( recordLength < 42 )
|
||
{
|
||
endp->Interpreter()->Weird("SSLv3x: Client hello too small!");
|
||
return 0;
|
||
}
|
||
|
||
uint16 version = uint16(data[4] << 8 ) | data[5];
|
||
if ( version != SSLProxy_Analyzer::SSLv30 &&
|
||
version != SSLProxy_Analyzer::SSLv31 )
|
||
endp->Interpreter()->Weird("SSLv3x: Corrupt version information in Client hello!");
|
||
|
||
uint16 offset = 38;
|
||
uint8 sessionIDLength = uint8(data[offset]);
|
||
offset += (1 + sessionIDLength);
|
||
if ( sessionIDLength > 32 )
|
||
{
|
||
endp->Interpreter()->Weird("SSLv3x: SessionID too long in Client hello!");
|
||
return 0;
|
||
}
|
||
|
||
uint16 cipherSuiteLength =
|
||
uint16(data[offset] << 8) | data[offset+1];
|
||
offset += (2 + cipherSuiteLength);
|
||
if ( cipherSuiteLength < 2 )
|
||
endp->Interpreter()->Weird("SSLv3x: CipherSuite length too small!");
|
||
|
||
if ( offset > recordLength )
|
||
{
|
||
endp->Interpreter()->Weird("SSLv3x: Client hello too small, corrupt length fields!");
|
||
return 0;
|
||
}
|
||
|
||
uint8 compressionMethodLength = uint8(data[offset]);
|
||
offset += (1 + compressionMethodLength);
|
||
if ( compressionMethodLength < 1 )
|
||
endp->Interpreter()->Weird("SSLv3x: CompressionMethod length too small!");
|
||
|
||
if ( offset < length )
|
||
{
|
||
uint16 sslExtensionsLength =
|
||
uint16(data[offset] << 8) | data[offset+1];
|
||
offset += 2;
|
||
if ( sslExtensionsLength < 4 )
|
||
endp->Interpreter()->Weird("SSLv3x: Extensions length too small!");
|
||
|
||
// TODO: extract SSL extensions here
|
||
|
||
offset += sslExtensionsLength;
|
||
if ( offset != length+4 )
|
||
{
|
||
endp->Interpreter()->Weird("SSLv3x: Corrupt length fields in Client hello!");
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
int SSLv3_HandshakeRecord::checkServerHello()
|
||
{
|
||
if ( recordLength < 42 )
|
||
{
|
||
endp->Interpreter()->Weird("SSLv3x: Server hello too small!");
|
||
return 0;
|
||
}
|
||
|
||
uint16 version = uint16(data[4] << 8) | data[5];
|
||
if ( version != SSLProxy_Analyzer::SSLv30 &&
|
||
version != SSLProxy_Analyzer::SSLv31 )
|
||
endp->Interpreter()->Weird("SSLv3x: Corrupt version information in Server hello!");
|
||
|
||
uint16 offset = 38;
|
||
uint8 sessionIDLength = uint8(data[offset]);
|
||
if ( sessionIDLength > 32 )
|
||
{
|
||
endp->Interpreter()->Weird("SSLv3x: SessionID too long in Server hello!");
|
||
return 0;
|
||
}
|
||
offset += (1 + sessionIDLength);
|
||
|
||
offset += 3; // account for cipher and compression method
|
||
if ( offset < length )
|
||
{
|
||
uint16 sslExtensionsLength =
|
||
uint16(data[offset] << 8) | data[offset+1];
|
||
offset += 2;
|
||
if ( sslExtensionsLength < 4 )
|
||
endp->Interpreter()->Weird("SSLv3x: Extensions length too small!");
|
||
|
||
// TODO: extract SSL extensions here
|
||
offset += sslExtensionsLength;
|
||
|
||
if ( offset != length+4 )
|
||
{
|
||
endp->Interpreter()->Weird("SSLv3x: Corrupt length fields in Server hello!");
|
||
return 0;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
SSLv3_AlertRecord::SSLv3_AlertRecord(const u_char* data, int len,
|
||
uint16 version, SSLv3_Endpoint const* e)
|
||
: SSLv3_Record(data, len, version, e)
|
||
{
|
||
if ( len < 2 )
|
||
{
|
||
e->Interpreter()->Weird("SSLv3x: Alert header length too small!");
|
||
level = 255;
|
||
description = 255;
|
||
}
|
||
|
||
// No further consistency-check, because alerts may be
|
||
// already encrypted.
|
||
level = uint8(*((this->data) + SSL3_1_ALERT_LEVEL_OFFSET));
|
||
description = uint8(*((this->data) + SSL3_1_ALERT_DESCRIPTION_OFFSET));
|
||
}
|
||
|
||
SSLv3_AlertRecord::~SSLv3_AlertRecord()
|
||
{
|
||
}
|
||
|
||
int SSLv3_AlertRecord::GetDescription() const
|
||
{
|
||
return description;
|
||
}
|
||
|
||
int SSLv3_AlertRecord::GetLevel() const
|
||
{
|
||
return level;
|
||
}
|
||
|
||
void SSLv3_AlertRecord::Deliver(SSLv3_Interpreter* conn)
|
||
{
|
||
conn->DeliverSSLv3_Record(this);
|
||
}
|
||
|
||
SSLv3_ChangeCipherRecord::SSLv3_ChangeCipherRecord(const u_char* data, int len,
|
||
uint16 version, SSLv3_Endpoint const* e)
|
||
: SSLv3_Record(data, len, version, e)
|
||
{
|
||
if ( len < 1 )
|
||
{
|
||
e->Interpreter()->Weird("SSLv3x: Change cipher header length too small!");
|
||
type = 255;
|
||
}
|
||
else
|
||
type = uint8(*((this->data) + SSL3_1_CHANGE_CIPHER_TYPE_OFFSET));
|
||
}
|
||
|
||
SSLv3_ChangeCipherRecord::~SSLv3_ChangeCipherRecord()
|
||
{
|
||
}
|
||
|
||
int SSLv3_ChangeCipherRecord::GetType() const
|
||
{
|
||
return type;
|
||
}
|
||
|
||
void SSLv3_ChangeCipherRecord::Deliver(SSLv3_Interpreter* conn)
|
||
{
|
||
conn->DeliverSSLv3_Record(this);
|
||
}
|
||
|
||
SSLv3_ApplicationRecord::SSLv3_ApplicationRecord(const u_char* data, int len, uint16 version, SSLv3_Endpoint const* e)
|
||
: SSLv3_Record(data, len, version, e)
|
||
{
|
||
}
|
||
|
||
SSLv3_ApplicationRecord::~SSLv3_ApplicationRecord()
|
||
{
|
||
}
|
||
|
||
void SSLv3_ApplicationRecord::Deliver(SSLv3_Interpreter* conn)
|
||
{
|
||
conn->DeliverSSLv3_Record(this);
|
||
}
|