odp_ctxt.brute_force_inprocess_threshold = brute_force_inprocess_threshold;
}
}
+ else if (!(strcasecmp(conf_key, "inspect_ooo_flows")))
+ {
+ if (!(strcasecmp(conf_val, "enabled")))
+ {
+ odp_ctxt.inspect_ooo_flows = true;
+ continue;
+ }
+ }
/* App Priority bit set*/
else if (!(strcasecmp(conf_key, "app_priority")))
{
APPID_LOG(nullptr, TRACE_INFO_LEVEL, "Appid Config: appid_cpu_profiler %s\n", (appid_cpu_profiler ? "True" : "False"));
APPID_LOG(nullptr, TRACE_INFO_LEVEL, "Appid Config: brute_force_inprocess_threshold %" PRId8" \n", brute_force_inprocess_threshold);
APPID_LOG(nullptr, TRACE_INFO_LEVEL, "Appid Config: failed_state_expiration_secs %" PRId32" \n", failed_state_expiration_secs);
+ APPID_LOG(nullptr, TRACE_INFO_LEVEL, "Appid Config: inspect_ooo_flows %s\n", inspect_ooo_flows ? "True" : "False");
}
bool OdpContext::is_appid_cpu_profiler_running()
bool recheck_for_portservice_appid = false;
bool eve_http_client = true;
bool appid_cpu_profiler = true;
+ bool inspect_ooo_flows = false;
uint8_t brute_force_inprocess_threshold = DEFAULT_BRUTE_FORCE_INPROCESS_STATE_THRESHOLD;
uint16_t max_packet_before_service_fail = DEFAULT_MAX_PKTS_BEFORE_SERVICE_FAIL;
uint16_t max_packet_service_fail_ignore_bytes = DEFAULT_MAX_PKT_BEFORE_SERVICE_FAIL_IGNORE_BYTES;
uint32_t host_port_app_cache_lookup_range = 100000;
uint64_t max_bytes_before_service_fail = DEFAULT_MAX_BYTES_BEFORE_SERVICE_FAIL;
FirstPktAppIdDiscovered first_pkt_appid_prefix = NO_APPID_FOUND;
+
OdpContext(const AppIdConfig&, snort::SnortConfig*);
void initialize(AppIdInspector& inspector);
// Shut off service/client discoveries, since they skip not-ok data packets and
// may keep failing on subsequent data packets causing performance degradation
- if (!asd->get_session_flags(APPID_SESSION_MID) or
- (p->ptrs.sp != 21 and p->ptrs.dp != 21)) // exception for ftp-control
+ if ((!asd->get_session_flags(APPID_SESSION_MID) or
+ (p->ptrs.sp != 21 and p->ptrs.dp != 21)) and !odp_ctxt.inspect_ooo_flows) // exception for ftp-control
{
asd->service_disco_state = APPID_DISCO_STATE_FINISHED;
if (asd->get_payload_id() == APP_ID_NONE and
}
}
// FIXIT-M - snort 2.x has added a check for midstream pickup to this, do we need that?
- else if (protocol != IpProtocol::TCP or (p->packet_flags & PKT_STREAM_ORDER_OK))
+ else if (protocol != IpProtocol::TCP or ((p->packet_flags & PKT_STREAM_ORDER_OK) || asd.get_session_flags(APPID_SESSION_OOO)))
{
if (asd.service_disco_state != APPID_DISCO_STATE_FINISHED)
is_discovery_done =
int certs_curr_len = 0; // Current amount of collected certificate data.
uint8_t* cached_data = nullptr;
uint16_t cached_len = 0;
+ bool cached_client_data = false;
};
#pragma pack(1)
ssl_cache_free(cached_data, cached_len);
}
-static void parse_client_initiation(const uint8_t* data, uint16_t size, ServiceSSLData* ss)
+static ParseCHResult parse_client_initiation(const uint8_t* data, uint16_t size, ServiceSSLData* ss)
{
const ServiceSSLV3Hdr* hdr3;
uint16_t ver;
/* Sanity check header stuff. */
if (size < sizeof(ServiceSSLV3Hdr))
- return;
+ return ParseCHResult::FAILED;
hdr3 = (const ServiceSSLV3Hdr*)data;
ver = ntohs(hdr3->version);
if (hdr3->type != SSL_HANDSHAKE || (ver != 0x0300 && ver != 0x0301 && ver != 0x0302 &&
ver != 0x0303))
{
- return;
+ return ParseCHResult::FAILED;
}
data += sizeof(ServiceSSLV3Hdr);
size -= sizeof(ServiceSSLV3Hdr);
- parse_client_hello_data(data, size, &ss->client_hello);
+ return parse_client_hello_data(data, size, &ss->client_hello);
}
static void save_ssl_cache(ServiceSSLData* ss, uint16_t size, const uint8_t* data)
uint16_t size = args.size;
uint8_t* reallocated_data = nullptr;
- if (ss->cached_len and ss->cached_data and (args.dir == APP_ID_FROM_RESPONDER))
+ if (!size)
+ goto inprocess;
+
+ ss = (ServiceSSLData*)data_get(args.asd);
+ if (!ss)
+ {
+ ss = (ServiceSSLData*)snort_calloc(sizeof(ServiceSSLData));
+ data_add(args.asd, ss);
+ ss->state = SSL_STATE_INITIATE;
+ ss->cached_data = nullptr;
+ ss->cached_len = 0;
+ }
+
+ if ( args.asd.get_session_flags(APPID_SESSION_OOO)
+ and ss->state == SSL_STATE_INITIATE
+ and args.dir == APP_ID_FROM_INITIATOR
+ and !(args.asd.scan_flags & SCAN_CERTVIZ_ENABLED_FLAG))
+ {
+ if (ss->cached_data)
+ {
+ reallocated_data = (uint8_t*)snort_calloc(ss->cached_len + size, sizeof(uint8_t));
+ memcpy(reallocated_data, args.data, args.size);
+ memcpy(reallocated_data + args.size, ss->cached_data, ss->cached_len);
+ size = ss->cached_len + args.size;
+ ssl_cache_free(ss->cached_data, ss->cached_len);
+ data = reallocated_data;
+ }
+ else
+ {
+ save_ssl_cache(ss, size, data);
+ ss->cached_client_data = true;
+ goto inprocess;
+ }
+ }
+
+ if (ss->cached_len and ss->cached_data)
{
- reallocated_data = (uint8_t*)snort_calloc(ss->cached_len + size, sizeof(uint8_t));
- memcpy(reallocated_data, ss->cached_data, ss->cached_len);
- memcpy(reallocated_data + ss->cached_len, args.data, args.size);
- size = ss->cached_len + args.size;
- ssl_cache_free(ss->cached_data, ss->cached_len);
- data = reallocated_data;
+ if ( (ss->cached_client_data and (args.dir == APP_ID_FROM_INITIATOR)) or (!ss->cached_client_data and (args.dir == APP_ID_FROM_RESPONDER)) )
+ {
+ reallocated_data = (uint8_t*)snort_calloc(ss->cached_len + size, sizeof(uint8_t));
+ memcpy(reallocated_data, ss->cached_data, ss->cached_len);
+ memcpy(reallocated_data + ss->cached_len, args.data, args.size);
+ size = ss->cached_len + args.size;
+ ssl_cache_free(ss->cached_data, ss->cached_len);
+ data = reallocated_data;
+ }
}
/* Start off with a Client Hello from client to server. */
if (ss->state == SSL_STATE_INITIATE)
if (!(args.asd.scan_flags & SCAN_CERTVIZ_ENABLED_FLAG) and
args.dir == APP_ID_FROM_INITIATOR)
{
- parse_client_initiation(data, size, ss);
- goto inprocess;
+ auto parse_status = parse_client_initiation(data, size, ss);
+ if (parse_status == ParseCHResult::FRAGMENTED_PACKET)
+ {
+ save_ssl_cache(ss, size, data);
+ ss->cached_client_data = true;
+ ss->state = SSL_STATE_INITIATE;
+ goto inprocess;
+ }
+ else if (parse_status == ParseCHResult::FAILED)
+ {
+ goto inprocess;
+ }
}
}
if (size < sizeof(ServiceSSLV3Hdr))
{
save_ssl_cache(ss, size, data);
+ ss->cached_client_data = false;
goto inprocess;
}
if (size < offsetof(ServiceSSLV3Record, version))
{
save_ssl_cache(ss, size, data);
+ ss->cached_client_data = false;
goto inprocess;
}
return APPID_NOMATCH;
success:
- if (reallocated_data)
- {
- snort_free(reallocated_data);
- reallocated_data = nullptr;
- }
-
if (ss->server_cert.certs_data && ss->server_cert.certs_len)
{
if (!(args.asd.scan_flags & SCAN_CERTVIZ_ENABLED_FLAG) and
}
}
+ if (reallocated_data)
+ {
+ snort_free(reallocated_data);
+ reallocated_data = nullptr;
+ }
+
args.asd.set_session_flags(APPID_SESSION_SSL_SESSION);
if (ss->client_hello.host_name || ss->server_cert.common_name || ss->server_cert.org_unit)
{
return false;
}
+
return false;
}
-void parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHelloData* client_hello_data)
+ParseCHResult parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHelloData* client_hello_data)
{
if (client_hello_data == nullptr)
- return;
+ return ParseCHResult::FAILED;
if (size < sizeof(ServiceSSLV3Record))
- return;
+ return ParseCHResult::FAILED;
const ServiceSSLV3Record* rec = (const ServiceSSLV3Record*)pkt;
uint16_t ver = ntohs(rec->version);
if (rec->type != SSLV3RecordType::CLIENT_HELLO || (ver != 0x0300 && ver != 0x0301 && ver != 0x0302 &&
ver != 0x0303) || rec->length_msb)
{
- return;
+ return ParseCHResult::FAILED;
}
unsigned length = ntohs(rec->length) + offsetof(ServiceSSLV3Record, version);
if (size < length)
- return;
+ return ParseCHResult::FRAGMENTED_PACKET;
pkt += sizeof(ServiceSSLV3Record);
size -= sizeof(ServiceSSLV3Record);
/* Session ID (1-byte length). */
if (size < 1)
- return;
+ return ParseCHResult::FAILED;
length = *((const uint8_t*)pkt);
pkt += length + 1;
if (size < (length + 1))
- return;
+ return ParseCHResult::FAILED;
size -= length + 1;
/* Cipher Suites (2-byte length). */
if (size < 2)
- return;
+ return ParseCHResult::FAILED;
length = ntohs(*((const uint16_t*)pkt));
pkt += length + 2;
if (size < (length + 2))
- return;
+ return ParseCHResult::FAILED;
size -= length + 2;
/* Compression Methods (1-byte length). */
if (size < 1)
- return;
+ return ParseCHResult::FAILED;
length = *((const uint8_t*)pkt);
pkt += length + 1;
if (size < (length + 1))
- return;
+ return ParseCHResult::FAILED;
size -= length + 1;
/* Extensions (2-byte length) */
if (size < 2)
- return;
+ return ParseCHResult::FAILED;
length = ntohs(*((const uint16_t*)pkt));
pkt += 2;
size -= 2;
if (size < length)
- return;
+ return ParseCHResult::FAILED;
/* We need at least type (2 bytes) and length (2 bytes) in the extension. */
while (length >= 4)
{
/* Found server host name. */
if (length < sizeof(ServiceSSLV3ExtensionServerName))
- return;
+ return ParseCHResult::FAILED;
unsigned len = ntohs(ext->string_length);
if ((length - sizeof(ServiceSSLV3ExtensionServerName)) < len)
- return;
+ return ParseCHResult::FAILED;
const uint8_t* str = pkt + offsetof(ServiceSSLV3ExtensionServerName, string_length) +
sizeof(ext->string_length);
client_hello_data->host_name = snort_strndup((const char*)str, len);
- return;
+ return ParseCHResult::SUCCESS;
}
unsigned len = ntohs(ext->length) + offsetof(ServiceSSLV3ExtensionServerName, list_length);
if (len > length)
- return;
+ return ParseCHResult::FAILED;
pkt += len;
length -= len;
}
+
+ return ParseCHResult::FAILED;
}
bool parse_server_certificates(SSLV3ServerCertData* server_cert_data)
namespace snort
{
+
+enum class ParseCHResult
+{
+ SUCCESS = 0,
+ FRAGMENTED_PACKET = 1,
+ FAILED = 2
+};
+
SO_PUBLIC uint32_t SSL_decode(
const uint8_t* pkt, int size, uint32_t pktflags, uint32_t prevflags,
uint8_t* alert_flags, uint16_t* partial_rec_len, int hblen, uint32_t* info_flags = nullptr,
SSLV3ClientHelloData* data = nullptr, SSLV3ServerCertData* server_cert_data = nullptr);
- void parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHelloData*);
+ ParseCHResult parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHelloData*);
bool parse_server_certificates(SSLV3ServerCertData* server_cert_data);
SO_PUBLIC bool IsTlsClientHello(const uint8_t* ptr, const uint8_t* end);