Skip to content

Commit 4c2ebee

Browse files
davidmcgrewGitHub Enterprise
authored andcommitted
Merge pull request #283 from network-intelligence/dev
Merging dev into trunk
2 parents 971c3cb + ccdbed3 commit 4c2ebee

File tree

10 files changed

+266
-40
lines changed

10 files changed

+266
-40
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.5.30
1+
2.5.31

doc/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# CHANGELOG for Mercury
22

3+
## Version 2.5.31
4+
* Mercury now outputs `tls.client.certs` and `tls.undetermined.certs`
5+
as well as `tls.server.certs`, for TLS version 1.2 and earlier.
6+
Client and server certificate chains are distinguished by the
7+
handshake type of the key exchange data that follows the
8+
`Certificate` data. If no key exchange data is present, then the
9+
certificate is reported as `tls.undetermined.certs`.
10+
* Timestamp counter reading support for ARM added in
11+
[tsc_clock.hpp](src/libmerc/tsc_clock.hpp).
12+
* If a timestamp of `0` is passed to `libmerc`, a timestamp is
13+
computed in order to improve the reassembly of TCP messages, as needed.
14+
315
## Version 2.5.30
416
* Dramatic improvements to TCP reassembly for output, performance and TCP segments handling.
517
* Improved error handling for malformed UTF-8 strings encountered in protocol fields.

src/libmerc/bench.h

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,13 @@
55
#ifndef BENCH_H
66
#define BENCH_H
77

8-
// The cycle_counter class will compile anywhere, but will only work
9-
// correctly on platforms that provide a function to read the
10-
// timestamp counter. The following preprocessor conditionals
11-
// identify the appropriate function, if one is present, and set
12-
// benchmark_is_valid to true or false. That value can be accessed
13-
// through the constexpr static boolean benchmark::is_valid.
14-
//
15-
#ifdef HAVE_X86INTRIN_H
16-
#include <x86intrin.h>
17-
#define read_timestamp_counter() __rdtsc()
18-
#define benchmark_is_valid true
19-
#else
20-
#define read_timestamp_counter() 0
21-
#define benchmark_is_valid false
22-
#endif
23-
//
24-
// TODO: add the corresponding ARM equivalent function
25-
268
#include <cmath> // for sqrt()
9+
#include "tsc_clock.hpp"
2710

2811
namespace benchmark {
2912

30-
static constexpr bool is_valid = benchmark_is_valid;
31-
32-
// An object of class cycle_counter counts the number of clock
33-
// cycles between its construction and the invocation of the
34-
// delta() function
35-
//
36-
class cycle_counter {
37-
uint64_t value;
38-
39-
public:
40-
41-
cycle_counter() : value{read_timestamp_counter()} { }
42-
43-
uint64_t delta() const { return read_timestamp_counter() - value; }
44-
45-
};
13+
static bool is_valid = tsc_clock::is_valid();
4614

47-
// An object of class count_and_mean maintains a count and mean of
4815
// all of the observed numbers. Each observation is reported with
4916
// the member function +=, e.g. 's += x' observes x.
5017
//

src/libmerc/libmerc.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "pkt_proc.h"
1515
#include "config_generator.h"
1616
#include "global_config.h"
17+
#include "tsc_clock.hpp"
1718

1819
#ifndef MERCURY_SEMANTIC_VERSION
1920
#warning MERCURY_SEMANTIC_VERSION is not defined
@@ -89,7 +90,9 @@ mercury_context mercury_init(const struct libmerc_config *vars, int verbosity) {
8990
// taking place
9091
//
9192
assert(printf_err(log_info, "libmerc is running assert() tests\n") != 0);
92-
93+
// Calculate the cpu ticks per sec during initialization time
94+
// to avoid delay during packet processing path
95+
tsc_clock::init();
9396
try {
9497
m = new mercury{vars, verbosity};
9598
return m;

src/libmerc/pkt_proc.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include "openvpn.h"
5454
#include "mysql.hpp"
5555
#include "geneve.hpp"
56+
#include "tsc_clock.hpp"
5657

5758
// double malware_prob_threshold = -1.0; // TODO: document hidden option
5859

@@ -217,9 +218,11 @@ void stateful_pkt_proc::set_tcp_protocol(protocol &x,
217218
break;
218219
}
219220
case tcp_msg_type_tls_server_hello:
220-
case tcp_msg_type_tls_certificate:
221221
x.emplace<tls_server_hello_and_certificate>(pkt, tcp_pkt);
222222
break;
223+
case tcp_msg_type_tls_certificate:
224+
x.emplace<tls_certificate>(pkt, tcp_pkt);
225+
break;
223226
case tcp_msg_type_ssh:
224227
x.emplace<ssh_init_packet>(pkt);
225228
break;
@@ -508,6 +511,11 @@ size_t stateful_pkt_proc::ip_write_json(void *buffer,
508511
}
509512
}
510513

514+
if (ts->tv_sec == 0) {
515+
tsc_clock time_now;
516+
ts->tv_sec = time_now.time_in_seconds();
517+
}
518+
511519
// process transport/application protocols
512520
//
513521
protocol x;
@@ -785,6 +793,12 @@ bool stateful_pkt_proc::analyze_ip_packet(const uint8_t *packet,
785793
if (reassembler) {
786794
reassembler->dump_pkt = false;
787795
}
796+
797+
if (ts->tv_sec == 0) {
798+
tsc_clock time_now;
799+
ts->tv_sec = time_now.time_in_seconds();
800+
}
801+
788802
if (transport_proto == ip::protocol::tcp) {
789803
tcp_packet tcp_pkt{pkt, &ip_pkt};
790804
if (!tcp_pkt.is_valid()) {

src/libmerc/pkt_proc_util.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct http_request; // start of tcp protocols
3838
struct http_response;
3939
struct tls_client_hello;
4040
class tls_server_hello_and_certificate;
41+
class tls_certificate;
4142
struct ssh_init_packet;
4243
struct ssh_kex_init;
4344
class smtp_client;
@@ -72,6 +73,7 @@ using protocol = std::variant<std::monostate,
7273
http_response,
7374
tls_client_hello,
7475
tls_server_hello_and_certificate,
76+
tls_certificate,
7577
ssh_init_packet,
7678
ssh_kex_init,
7779
smtp_client,
@@ -240,6 +242,10 @@ struct write_metadata {
240242
r.write_json(record, metadata_output_, certs_json_output_);
241243
}
242244

245+
void operator()(tls_certificate &r) {
246+
r.write_json(record, metadata_output_, certs_json_output_);
247+
}
248+
243249
void operator()(std::monostate &) { }
244250

245251
};

src/libmerc/tls.h

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,10 @@ enum class handshake_type : uint8_t {
203203
end_of_early_data = 5,
204204
encrypted_extensions = 8,
205205
certificate = 11,
206+
server_key_exchange = 12,
206207
certificate_request = 13,
207208
certificate_verify = 15,
209+
client_key_exchange = 16,
208210
finished = 20,
209211
key_update = 24,
210212
message_hash = 254
@@ -570,6 +572,107 @@ class tls_server_hello_and_certificate : public base_protocol {
570572

571573
};
572574

575+
576+
// ClientKeyExchange, following RFC 5246 Section 7.4.7
577+
//
578+
// struct {
579+
// select (KeyExchangeAlgorithm) {
580+
// case rsa:
581+
// EncryptedPreMasterSecret;
582+
// case dhe_dss:
583+
// case dhe_rsa:
584+
// case dh_dss:
585+
// case dh_rsa:
586+
// case dh_anon:
587+
// ClientDiffieHellmanPublic;
588+
// } exchange_keys;
589+
// } ClientKeyExchange;
590+
591+
// enum { dhe_dss, dhe_rsa, dh_anon, rsa, dh_dss, dh_rsa
592+
// /* may be extended, e.g., for ECDH -- see [TLSECC] */
593+
// } KeyExchangeAlgorithm;
594+
595+
596+
enum role {
597+
client,
598+
server,
599+
undetected
600+
};
601+
602+
class tls_certificate : public base_protocol {
603+
struct tls_server_certificate certificate;
604+
role entity;
605+
606+
public:
607+
608+
tls_certificate(struct datum &pkt, struct tcp_packet *tcp_pkt) : certificate{}, entity{undetected} {
609+
parse(pkt, tcp_pkt);
610+
}
611+
612+
void parse(struct datum &pkt, struct tcp_packet *tcp_pkt) {
613+
614+
// parse certificate
615+
//
616+
struct tls_record rec{pkt};
617+
struct tls_handshake handshake{rec.fragment};
618+
if (handshake.msg_type == handshake_type::certificate) {
619+
certificate.parse(handshake.body);
620+
621+
if (rec.fragment.is_not_empty()) {
622+
tls_handshake handshake{rec.fragment};
623+
if (handshake.msg_type == handshake_type::client_key_exchange) {
624+
entity = client;
625+
} else if (handshake.msg_type == handshake_type::server_key_exchange) {
626+
entity = server;
627+
}
628+
} else if (pkt.is_not_empty()) {
629+
tls_record rec2{pkt};
630+
tls_handshake handshake{rec2.fragment};
631+
if (handshake.msg_type == handshake_type::client_key_exchange) {
632+
entity = client;
633+
} else if (handshake.msg_type == handshake_type::server_key_exchange) {
634+
entity = server;
635+
}
636+
}
637+
638+
}
639+
if (tcp_pkt && certificate.additional_bytes_needed) {
640+
tcp_pkt->reassembly_needed(certificate.additional_bytes_needed);
641+
}
642+
}
643+
644+
bool is_not_empty() {
645+
return certificate.is_not_empty();
646+
}
647+
648+
void write_json(struct json_object &record, bool metadata_output, bool certs_json_output) {
649+
(void)metadata_output;
650+
651+
bool have_certificate = certificate.is_not_empty();
652+
if (have_certificate) {
653+
654+
// output certificate
655+
//
656+
const char *role = "undetermined";
657+
if (entity == client) {
658+
role = "client";
659+
} else if (entity == server) {
660+
role = "server";
661+
}
662+
struct json_object tls{record, "tls"};
663+
json_object client_or_server{tls, role};
664+
struct json_array certs{client_or_server, "certs"};
665+
certificate.write_json(certs, certs_json_output);
666+
certs.close();
667+
client_or_server.close();
668+
tls.close();
669+
670+
}
671+
}
672+
673+
};
674+
675+
573676
static uint16_t degrease_uint16(uint16_t x) {
574677
switch(x) {
575678
case 0x0a0a:

0 commit comments

Comments
 (0)