Merge remote-tracking branch 'origin/topic/vladg/ssh'

I replaced a few strcmps with either calls to std::str.compare
or with the == operator of BroString.

Also changed two of the input framework tests that did not pass
anymore after the merge. The new SSH analyzer no longer loads the
scripts that let network time run, hence those tests failed because
updates were not propagated from the threads (that took a while
to find.)

* origin/topic/vladg/ssh: (25 commits)
  SSH: Register analyzer for 22/tcp.
  SSH: Add 22/tcp to likely_server_ports
  SSH: Ignore encrypted packets by default.
  SSH: Fix some edge-cases which created BinPAC exceptions
  SSH: Add memleak btest
  SSH: Update baselines
  SSH: Added some more events for SSH2
  SSH: Intel framework integration (PUBKEY_HASH)
  Update baselines for new SSH analyzer.
  Update SSH policy scripts with new events.
  SSH: Add documentation
  Refactoring ssh-protocol.pac:
  SSH: Use the compression_algorithms const in another place.
  Some cleanup and refactoring on SSH main.bro.
  SSH: A bit of code cleanup.
  Move SSH constants to consts.pac
  SSH: Cleanup code style.
  SSH: Fix some memleaks.
  Refactored the SSH analyzer. Added supported for algorithm detection and more key exchange message types.
  Add host key support for SSH1.
  Add support for SSH1
  Move SSH analyzer to new plugin architecture.
  ...

Conflicts:
  scripts/base/protocols/ssh/main.bro
  testing/btest/Baseline/core.print-bpf-filters/output2
  testing/btest/Baseline/plugins.hooks/output

BIT-1344: #merged
This commit is contained in:
Johanna Amann 2015-03-25 10:58:13 -07:00
commit f79b5adc08
38 changed files with 1494 additions and 324 deletions

View file

@ -1,105 +1,148 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "config.h"
#include <ctype.h>
#include "NetVar.h"
#include "SSH.h"
#include "Event.h"
#include "analyzer/protocol/tcp/ContentLine.h"
#include "analyzer/protocol/tcp/TCP_Reassembler.h"
#include "Reporter.h"
#include "types.bif.h"
#include "events.bif.h"
using namespace analyzer::ssh;
using namespace analyzer::SSH;
SSH_Analyzer::SSH_Analyzer(Connection* c)
: tcp::TCP_ApplicationAnalyzer("SSH", c)
: tcp::TCP_ApplicationAnalyzer("SSH", c)
{
orig = new tcp::ContentLine_Analyzer(c, true);
orig->SetSkipPartial(true);
orig->SetCRLFAsEOL(LF_as_EOL);
AddSupportAnalyzer(orig);
resp = new tcp::ContentLine_Analyzer(c, false);
resp->SetSkipPartial(true);
resp->SetCRLFAsEOL(LF_as_EOL);
AddSupportAnalyzer(resp);
interp = new binpac::SSH::SSH_Conn(this);
had_gap = false;
auth_decision_made = false;
skipped_banner = false;
service_accept_size = 0;
userauth_failure_size = 0;
}
void SSH_Analyzer::DeliverStream(int length, const u_char* data, bool is_orig)
SSH_Analyzer::~SSH_Analyzer()
{
tcp::TCP_ApplicationAnalyzer::DeliverStream(length, data, is_orig);
delete interp;
}
// We're all done processing this endpoint - flag it as such,
// before we even determine whether we have any event generation
// work to do, to make sure we don't do any further work on it.
if ( is_orig )
orig->SetSkipDeliveries(true);
else
resp->SetSkipDeliveries(true);
void SSH_Analyzer::Done()
{
tcp::TCP_ApplicationAnalyzer::Done();
if ( TCP() )
interp->FlowEOF(true);
interp->FlowEOF(false);
}
void SSH_Analyzer::EndpointEOF(bool is_orig)
{
tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
interp->FlowEOF(is_orig);
}
void SSH_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
{
tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
assert(TCP());
if ( TCP()->IsPartial() )
return;
if ( had_gap )
// If only one side had a content gap, we could still try to
// deliver data to the other side if the script layer can handle this.
return;
if ( interp->get_state(orig) == binpac::SSH::ENCRYPTED )
{
// Don't try to parse version if there has already been a gap.
tcp::TCP_Endpoint* endp = is_orig ? TCP()->Orig() : TCP()->Resp();
if ( endp->HadGap() )
if ( ssh_encrypted_packet )
BifEvent::generate_ssh_encrypted_packet(interp->bro_analyzer(), interp->bro_analyzer()->Conn(),
orig, len);
if ( ! auth_decision_made )
ProcessEncrypted(len, orig);
return;
}
try
{
interp->NewData(orig, data, data + len);
}
catch ( const binpac::Exception& e )
{
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
}
}
void SSH_Analyzer::Undelivered(uint64 seq, int len, bool orig)
{
tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
had_gap = true;
interp->NewGap(orig, len);
}
void SSH_Analyzer::ProcessEncrypted(int len, bool orig)
{
// We're interested in messages from the server for SSH2
if ( ! orig && (interp->get_version() == binpac::SSH::SSH2) )
{
// The first thing we see and want to know is the length of
// SSH_MSG_SERVICE_REQUEST, which has a fixed (decrypted) size
// of 24 bytes (17 for content pad-aligned to 8-byte
// boundaries)
if ( ! service_accept_size )
{
service_accept_size = len;
return;
}
const char* line = (const char*) data;
// The SSH identification looks like this:
//
// SSH-<protocolmajor>.<protocolminor>-<version>\n
//
// We're interested in the "version" part here.
if ( length < 4 || memcmp(line, "SSH-", 4) != 0 )
{
Weird("malformed_ssh_identification");
ProtocolViolation("malformed ssh identification", line, length);
return;
}
int i;
for ( i = 4; i < length && line[i] != '-'; ++i )
;
if ( TCP() )
{
if ( length >= i )
{
IPAddr dst;
if ( is_orig )
dst = TCP()->Orig()->dst_addr;
else
dst = TCP()->Resp()->dst_addr;
if ( Conn()->VersionFoundEvent(dst, line + i,
length - i) )
ProtocolConfirmation();
else
ProtocolViolation("malformed ssh version",
line, length);
}
else
// If our user can authenticate via the "none" method, this
// packet will be a SSH_MSG_USERAUTH_SUCCESS, which has a
// fixed (decrypted) size of 8 bytes (1 for content
// pad-aligned to 8-byte boundaries). relative_len would be
// -16.
if ( ! userauth_failure_size && (len + 16 == service_accept_size) )
{
Weird("malformed_ssh_version");
ProtocolViolation("malformed ssh version", line, length);
auth_decision_made = true;
if ( ssh_auth_successful )
BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), true);
return;
}
// Normally, this packet would be a SSH_MSG_USERAUTH_FAILURE
// message, with a variable length, depending on the
// authentication methods the server supports. If it's too
// big, it might contain a pre-auth MOTD/banner, so we'll just
// skip it.
if ( ! userauth_failure_size )
{
if ( ! skipped_banner && (len - service_accept_size) > 256 )
{
skipped_banner = true;
return;
}
userauth_failure_size = len;
return;
}
// If we've already seen a failure, let's see if this is
// another packet of the same size.
if ( len == userauth_failure_size )
{
if ( ssh_auth_failed )
BifEvent::generate_ssh_auth_failed(interp->bro_analyzer(), interp->bro_analyzer()->Conn());
return;
}
// ...or a success packet.
if ( len - service_accept_size == -16 )
{
auth_decision_made = true;
if ( ssh_auth_successful )
BifEvent::generate_ssh_auth_successful(interp->bro_analyzer(), interp->bro_analyzer()->Conn(), false);
return;
}
}
// Generate SSH events.
EventHandlerPtr event = is_orig ?
ssh_client_version : ssh_server_version;
if ( ! event )
return;
val_list* vl = new val_list;
vl->append(BuildConnVal());
vl->append(new StringVal(length, line));
ConnectionEvent(event, vl);
}