mirror of
https://github.com/zeek/zeek.git
synced 2025-10-13 03:58:20 +00:00
Merge remote-tracking branch 'origin/topic/johanna/imap-starttls'
BIT-1574 #merged * origin/topic/johanna/imap-starttls: IMAP: add c++11 header file that gcc complains about. IMAP: documentation and test updates also generate an event when starttls is encounterd for imap. Add support of getting server capabilities to IMAP parser. Basic IMAP StartTLS analyzer.
This commit is contained in:
commit
2eeddac401
25 changed files with 468 additions and 14 deletions
12
src/analyzer/protocol/imap/CMakeLists.txt
Normal file
12
src/analyzer/protocol/imap/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
include(BroPlugin)
|
||||
|
||||
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
bro_plugin_begin(Bro IMAP)
|
||||
bro_plugin_cc(Plugin.cc)
|
||||
bro_plugin_cc(IMAP.cc)
|
||||
bro_plugin_bif(events.bif)
|
||||
bro_plugin_pac(imap.pac imap-analyzer.pac imap-protocol.pac)
|
||||
bro_plugin_end()
|
||||
|
85
src/analyzer/protocol/imap/IMAP.cc
Normal file
85
src/analyzer/protocol/imap/IMAP.cc
Normal file
|
@ -0,0 +1,85 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include "IMAP.h"
|
||||
#include "analyzer/protocol/tcp/TCP_Reassembler.h"
|
||||
#include "analyzer/Manager.h"
|
||||
|
||||
using namespace analyzer::imap;
|
||||
|
||||
IMAP_Analyzer::IMAP_Analyzer(Connection* conn)
|
||||
: tcp::TCP_ApplicationAnalyzer("IMAP", conn)
|
||||
{
|
||||
interp = new binpac::IMAP::IMAP_Conn(this);
|
||||
had_gap = false;
|
||||
tls_active = false;
|
||||
}
|
||||
|
||||
IMAP_Analyzer::~IMAP_Analyzer()
|
||||
{
|
||||
delete interp;
|
||||
}
|
||||
|
||||
void IMAP_Analyzer::Done()
|
||||
{
|
||||
tcp::TCP_ApplicationAnalyzer::Done();
|
||||
|
||||
interp->FlowEOF(true);
|
||||
interp->FlowEOF(false);
|
||||
}
|
||||
|
||||
void IMAP_Analyzer::EndpointEOF(bool is_orig)
|
||||
{
|
||||
tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
|
||||
interp->FlowEOF(is_orig);
|
||||
}
|
||||
|
||||
void IMAP_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
|
||||
{
|
||||
tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
|
||||
|
||||
if ( tls_active )
|
||||
{
|
||||
// If TLS has been initiated, forward to child and abort further
|
||||
// processing
|
||||
ForwardStream(len, data, orig);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
try
|
||||
{
|
||||
interp->NewData(orig, data, data + len);
|
||||
}
|
||||
catch ( const binpac::Exception& e )
|
||||
{
|
||||
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
|
||||
}
|
||||
}
|
||||
|
||||
void IMAP_Analyzer::Undelivered(uint64 seq, int len, bool orig)
|
||||
{
|
||||
tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
|
||||
had_gap = true;
|
||||
interp->NewGap(orig, len);
|
||||
}
|
||||
|
||||
void IMAP_Analyzer::StartTLS()
|
||||
{
|
||||
// StartTLS was called. This means we saw a client starttls followed
|
||||
// by a server proceed. From here on, everything should be a binary
|
||||
// TLS datastream.
|
||||
tls_active = true;
|
||||
|
||||
Analyzer* ssl = analyzer_mgr->InstantiateAnalyzer("SSL", Conn());
|
||||
if ( ssl )
|
||||
AddChildAnalyzer(ssl);
|
||||
}
|
40
src/analyzer/protocol/imap/IMAP.h
Normal file
40
src/analyzer/protocol/imap/IMAP.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#ifndef ANALYZER_PROTOCOL_IMAP_IMAP_H
|
||||
#define ANALYZER_PROTOCOL_IMAP_IMAP_H
|
||||
|
||||
// for std::transform
|
||||
#include <algorithm>
|
||||
#include "analyzer/protocol/tcp/TCP.h"
|
||||
|
||||
#include "imap_pac.h"
|
||||
|
||||
namespace analyzer { namespace imap {
|
||||
|
||||
class IMAP_Analyzer : public tcp::TCP_ApplicationAnalyzer {
|
||||
public:
|
||||
IMAP_Analyzer(Connection* conn);
|
||||
virtual ~IMAP_Analyzer();
|
||||
|
||||
virtual void Done();
|
||||
virtual void DeliverStream(int len, const u_char* data, bool orig);
|
||||
virtual void Undelivered(uint64 seq, int len, bool orig);
|
||||
|
||||
// Overriden from tcp::TCP_ApplicationAnalyzer.
|
||||
virtual void EndpointEOF(bool is_orig);
|
||||
|
||||
void StartTLS();
|
||||
|
||||
static analyzer::Analyzer* Instantiate(Connection* conn)
|
||||
{ return new IMAP_Analyzer(conn); }
|
||||
|
||||
protected:
|
||||
binpac::IMAP::IMAP_Conn* interp;
|
||||
bool had_gap;
|
||||
|
||||
bool tls_active;
|
||||
};
|
||||
|
||||
} } // namespace analyzer::*
|
||||
|
||||
#endif /* ANALYZER_PROTOCOL_IMAP_IMAP_H */
|
22
src/analyzer/protocol/imap/Plugin.cc
Normal file
22
src/analyzer/protocol/imap/Plugin.cc
Normal file
|
@ -0,0 +1,22 @@
|
|||
// See the file in the main distribution directory for copyright.
|
||||
#include "plugin/Plugin.h"
|
||||
#include "IMAP.h"
|
||||
|
||||
namespace plugin {
|
||||
namespace Bro_IMAP {
|
||||
|
||||
class Plugin : public plugin::Plugin {
|
||||
public:
|
||||
plugin::Configuration Configure()
|
||||
{
|
||||
AddComponent(new ::analyzer::Component("IMAP", ::analyzer::imap::IMAP_Analyzer::Instantiate));
|
||||
|
||||
plugin::Configuration config;
|
||||
config.name = "Bro::IMAP";
|
||||
config.description = "IMAP analyzer (StartTLS only)";
|
||||
return config;
|
||||
}
|
||||
} plugin;
|
||||
|
||||
}
|
||||
}
|
13
src/analyzer/protocol/imap/events.bif
Normal file
13
src/analyzer/protocol/imap/events.bif
Normal file
|
@ -0,0 +1,13 @@
|
|||
## Generated when a server sends a capability list to the client,
|
||||
## after being queried using the CAPABILITY command.
|
||||
##
|
||||
## c: The connection.
|
||||
##
|
||||
## capabilities: The list of IMAP capabilities as sent by the server.
|
||||
event imap_capabilities%(c: connection, capabilities: string_vec%);
|
||||
|
||||
## Generated when a IMAP connection goes encrypted after a successful
|
||||
## StartTLS exchange between the client and the server.
|
||||
##
|
||||
## c: The connection.
|
||||
event imap_starttls%(c: connection%);
|
76
src/analyzer/protocol/imap/imap-analyzer.pac
Normal file
76
src/analyzer/protocol/imap/imap-analyzer.pac
Normal file
|
@ -0,0 +1,76 @@
|
|||
refine connection IMAP_Conn += {
|
||||
|
||||
%member{
|
||||
string client_starttls_id;
|
||||
%}
|
||||
|
||||
%init{
|
||||
%}
|
||||
|
||||
function proc_imap_token(is_orig: bool, tag: bytestring, command: bytestring): bool
|
||||
%{
|
||||
string commands = std_str(command);
|
||||
std::transform(commands.begin(), commands.end(), commands.begin(), ::tolower);
|
||||
|
||||
string tags = std_str(tag);
|
||||
|
||||
//printf("imap %s %s\n", commands.c_str(), tags.c_str());
|
||||
|
||||
if ( !is_orig && tags == "*" && commands == "ok" )
|
||||
bro_analyzer()->ProtocolConfirmation();
|
||||
|
||||
if ( is_orig && ( command == "capability" || commands == "starttls" ) )
|
||||
bro_analyzer()->ProtocolConfirmation();
|
||||
|
||||
if ( command == "authenticate" || command == "login" || command == "examine" || command == "create" || command == "list" || command == "fetch" )
|
||||
{
|
||||
bro_analyzer()->ProtocolConfirmation();
|
||||
// Handshake has passed the phase where we should see StartTLS. Simply skip from hereon...
|
||||
bro_analyzer()->SetSkip(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( is_orig && commands == "starttls" )
|
||||
{
|
||||
if ( !client_starttls_id.empty() )
|
||||
reporter->Weird(bro_analyzer()->Conn(), "IMAP: client sent duplicate StartTLS");
|
||||
|
||||
client_starttls_id = tags;
|
||||
}
|
||||
|
||||
if ( !is_orig && !client_starttls_id.empty() && tags == client_starttls_id )
|
||||
{
|
||||
if ( commands == "ok" )
|
||||
{
|
||||
bro_analyzer()->StartTLS();
|
||||
BifEvent::generate_imap_starttls(bro_analyzer(), bro_analyzer()->Conn());
|
||||
}
|
||||
else
|
||||
reporter->Weird(bro_analyzer()->Conn(), "IMAP: server refused StartTLS");
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_server_capability(capabilities: Capability[]): bool
|
||||
%{
|
||||
VectorVal* capv = new VectorVal(internal_type("string_vec")->AsVectorType());
|
||||
for ( unsigned int i = 0; i< capabilities->size(); i++ )
|
||||
{
|
||||
const bytestring& capability = (*capabilities)[i]->cap();
|
||||
capv->Assign(i, new StringVal(capability.length(), (const char*)capability.data()));
|
||||
}
|
||||
|
||||
BifEvent::generate_imap_capabilities(bro_analyzer(), bro_analyzer()->Conn(), capv);
|
||||
return true;
|
||||
%}
|
||||
|
||||
};
|
||||
|
||||
refine typeattr ImapToken += &let {
|
||||
proc: bool = $context.connection.proc_imap_token(is_orig, tag, command);
|
||||
};
|
||||
|
||||
refine typeattr ServerCapability += &let {
|
||||
proc: bool = $context.connection.proc_server_capability(capabilities);
|
||||
};
|
70
src/analyzer/protocol/imap/imap-protocol.pac
Normal file
70
src/analyzer/protocol/imap/imap-protocol.pac
Normal file
|
@ -0,0 +1,70 @@
|
|||
# commands that we support parsing. The numbers do not really mean anything
|
||||
# in this case
|
||||
enum ImapCommand {
|
||||
CMD_CAPABILITY,
|
||||
CMD_UNKNOWN
|
||||
}
|
||||
|
||||
type TAG = RE/[[:alnum:][:punct:]]+/;
|
||||
type CONTENT = RE/[^\r\n]*/;
|
||||
type SPACING = RE/[ ]+/;
|
||||
type OPTIONALSPACING = RE/[ ]*/;
|
||||
type NEWLINE = RE/[\r\n]+/;
|
||||
type OPTIONALNEWLINE = RE/[\r\n]*/;
|
||||
|
||||
type IMAP_PDU(is_orig: bool) = ImapToken(is_orig)[] &until($input.length() == 0);
|
||||
|
||||
type ImapToken(is_orig: bool) = record {
|
||||
tag : TAG;
|
||||
: SPACING;
|
||||
command: TAG;
|
||||
: OPTIONALSPACING;
|
||||
client_or_server: case is_orig of {
|
||||
true -> client: UnknownCommand(this) ;
|
||||
false -> server: ServerContentText(this);
|
||||
} &requires(pcommand) ;
|
||||
} &let {
|
||||
pcommand: int = $context.connection.determine_command(is_orig, tag, command);
|
||||
};
|
||||
|
||||
type ServerContentText(rec: ImapToken) = case rec.pcommand of {
|
||||
CMD_CAPABILITY -> capability: ServerCapability(rec);
|
||||
default -> unknown: UnknownCommand(rec);
|
||||
};
|
||||
|
||||
type Capability = record {
|
||||
cap: TAG;
|
||||
: OPTIONALSPACING;
|
||||
nl: OPTIONALNEWLINE;
|
||||
};
|
||||
|
||||
type ServerCapability(rec: ImapToken) = record {
|
||||
capabilities: Capability[] &until($context.connection.strlen($element.nl) > 0);
|
||||
};
|
||||
|
||||
type UnknownCommand(rec: ImapToken) = record {
|
||||
tagcontent: CONTENT;
|
||||
: NEWLINE;
|
||||
};
|
||||
|
||||
refine connection IMAP_Conn += {
|
||||
|
||||
function determine_command(is_orig: bool, tag: bytestring, command: bytestring): int
|
||||
%{
|
||||
string cmdstr = std_str(command);
|
||||
std::transform(cmdstr.begin(), cmdstr.end(), cmdstr.begin(), ::tolower);
|
||||
string tagstr = std_str(tag);
|
||||
|
||||
if ( !is_orig && cmdstr == "capability" && tag == "*" ) {
|
||||
return CMD_CAPABILITY;
|
||||
}
|
||||
|
||||
return CMD_UNKNOWN;
|
||||
%}
|
||||
|
||||
function strlen(str: bytestring): int
|
||||
%{
|
||||
return str.length();
|
||||
%}
|
||||
|
||||
};
|
37
src/analyzer/protocol/imap/imap.pac
Normal file
37
src/analyzer/protocol/imap/imap.pac
Normal file
|
@ -0,0 +1,37 @@
|
|||
# binpac file for the IMAP analyzer.
|
||||
# Note that we currently do not even try to parse the protocol
|
||||
# completely -- this is only supposed to be able to parse imap
|
||||
# till StartTLS does (or does not) kick in.
|
||||
|
||||
%include binpac.pac
|
||||
%include bro.pac
|
||||
|
||||
%extern{
|
||||
#include "events.bif.h"
|
||||
|
||||
namespace analyzer { namespace imap { class IMAP_Analyzer; } }
|
||||
namespace binpac { namespace IMAP { class IMAP_Conn; } }
|
||||
typedef analyzer::imap::IMAP_Analyzer* IMAPAnalyzer;
|
||||
|
||||
#include "IMAP.h"
|
||||
%}
|
||||
|
||||
extern type IMAPAnalyzer;
|
||||
|
||||
analyzer IMAP withcontext {
|
||||
connection: IMAP_Conn;
|
||||
flow: IMAP_Flow;
|
||||
};
|
||||
|
||||
connection IMAP_Conn(bro_analyzer: IMAPAnalyzer) {
|
||||
upflow = IMAP_Flow(true);
|
||||
downflow = IMAP_Flow(false);
|
||||
};
|
||||
|
||||
%include imap-protocol.pac
|
||||
|
||||
flow IMAP_Flow(is_orig: bool) {
|
||||
datagram = IMAP_PDU(is_orig) withcontext(connection, this);
|
||||
};
|
||||
|
||||
%include imap-analyzer.pac
|
Loading…
Add table
Add a link
Reference in a new issue