First import of the JSON output code (https://github.com/adulau/ssldump/issues/41)

This commit is contained in:
William Robinet 2021-01-06 17:52:45 +01:00
parent 28b4915099
commit d0ca1a8adc
15 changed files with 454 additions and 233 deletions

View file

@ -36,7 +36,7 @@ Additional back-end code available is in the [crl-monitor ](https://github.com/a
On Debian & Ubuntu:
```
apt install build-essential autoconf libssl-dev libpcap-dev libnet1-dev
apt install build-essential autoconf libssl-dev libpcap-dev libnet1-dev libjson-c-dev
./autogen.sh
./configure --prefix=/usr/local
make
@ -45,7 +45,7 @@ make
On Fedora, Centos & RHEL:
```
dnf install autoconf automake gcc make openssl-devel libpcap-devel libnet-devel
dnf install autoconf automake gcc make openssl-devel libpcap-devel libnet-devel json-c-devel
./autogen.sh
./configure --prefix=/usr/local
make

View file

@ -135,6 +135,7 @@ int network_process_packet(handler,timestamp,data,length)
p.len = ntohs(p.ip->ip_len);
if(p.len > length) {
if(!(NET_print_flags & NET_PRINT_JSON))
printf("Malformed packet, size from IP header is larger than size reported by libpcap, skipping ...\n");
return(0);
}

View file

@ -106,5 +106,6 @@ extern UINT4 NET_print_flags;
#define NET_PRINT_TYPESET 2
#define NET_PRINT_ACKS 4
#define NET_PRINT_NO_RESOLVE 8
#define NET_PRINT_JSON 16
#endif

View file

@ -135,7 +135,7 @@ void sig_handler(int sig)
logger->vtbl->deinit();
freed_conn = destroy_all_conn();
if(freed_conn)
if(freed_conn && !(NET_print_flags & NET_PRINT_JSON))
printf("Cleaned %d remaining connection(s) from connection pool\n", freed_conn);
if(p)
@ -258,7 +258,7 @@ void pcap_cb(ptr,hdr,data)
if(packet_cnt == conn_freq) {
packet_cnt = 0;
memcpy(&last_packet_seen_time,&hdr->ts,sizeof(struct timeval));
if((cleaned_conn = clean_old_conn()))
if((cleaned_conn = clean_old_conn()) && !(NET_print_flags & NET_PRINT_JSON))
printf("%d inactive connection(s) cleaned from connection pool\n", cleaned_conn);
} else {
packet_cnt++;
@ -310,7 +310,7 @@ int main(argc,argv)
signal(SIGINT,sig_handler);
while((c=getopt(argc,argv,"vr:F:f:S:yTt:ai:k:l:w:p:nsAxXhHVNdqem:P"))!=EOF){
while((c=getopt(argc,argv,"vr:F:f:S:jyTt:ai:k:l:w:p:nsAxXhHVNdqem:P"))!=EOF){
switch(c){
case 'v':
print_version();
@ -328,6 +328,10 @@ int main(argc,argv)
/*Kludge*/
SSL_print_flags |= SSL_PRINT_NROFF;
break;
case 'j':
NET_print_flags |= NET_PRINT_JSON;
SSL_print_flags |= SSL_PRINT_JSON;
break;
case 'a':
NET_print_flags |= NET_PRINT_ACKS;
break;
@ -472,6 +476,7 @@ int main(argc,argv)
pcap_if_type=pcap_datalink(p);
if(!(NET_print_flags & NET_PRINT_JSON))
if(NET_print_flags & NET_PRINT_TYPESET)
printf("\n.nf\n.ps -2\n");
@ -480,11 +485,12 @@ int main(argc,argv)
pcap_loop(p,-1,pcap_cb,(u_char *)n);
if(!(NET_print_flags & NET_PRINT_JSON))
if(NET_print_flags & NET_PRINT_TYPESET)
printf("\n.ps\n.fi\n");
freed_conn = destroy_all_conn();
if(freed_conn)
if(freed_conn && !(NET_print_flags & NET_PRINT_JSON))
printf("Cleaned %d remaining connection(s) from connection pool\n", freed_conn);
pcap_close(p);

View file

@ -74,13 +74,13 @@ int exdump(name,data)
for(i=0;i<data->len;i++){
if(!i && (data->len>8)) INDENT;
if((data->len>8) && i && !(i%12)){
printf("\n"); INDENT;
LF;INDENT;
}
printf("%.2x ",data->data[i]&255);
}
if(name) INDENT_POP;
if(data->len>8 && i%12)
printf("\n");
LF;
return(0);
}

View file

@ -130,6 +130,7 @@ int process_tcp_packet(handler,ctx,p)
conn->i2r.ack=ntohl(p->tcp->th_ack)+1;
lookuphostname(&conn->i_addr,&sn);
lookuphostname(&conn->r_addr,&dn);
if(!(NET_print_flags & NET_PRINT_JSON)) {
if(NET_print_flags & NET_PRINT_TYPESET)
printf("\\fC");
printf("New TCP connection #%d: %s(%d) <-> %s(%d)\n",
@ -138,7 +139,7 @@ int process_tcp_packet(handler,ctx,p)
dn,conn->r_port);
if(NET_print_flags & NET_PRINT_TYPESET)
printf("\\fR");
}
conn->state=TCP_STATE_ESTABLISHED;
free(sn);
free(dn);
@ -240,6 +241,7 @@ static int process_data_segment(conn,handler,p,stream,direction)
l=p->len - p->tcp->th_off * 4;
if(l < 0) {
if(!(NET_print_flags & NET_PRINT_JSON))
printf("Malformed packet, computed TCP segment size is negative, skipping ...\n");
return(0);
}
@ -400,6 +402,7 @@ static int print_tcp_packet(p)
lookuphostname(&p->ip->ip_src,&src);
lookuphostname(&p->ip->ip_dst,&dst);
if(!(NET_print_flags & NET_PRINT_JSON)) {
printf("TCP: %s(%d) -> %s(%d) ",
src,
ntohs(p->tcp->th_sport),
@ -425,7 +428,7 @@ static int print_tcp_packet(p)
printf("URG ");
printf("\n");
}
free(src);
free(dst);
return(0);

View file

@ -1,3 +1,4 @@
#include <json-c/json.h>
#include "network.h"
#include "ssl_h.h"
#include "sslprint.h"
@ -15,6 +16,10 @@ static int decode_ContentType_ChangeCipherSpec(ssl,dir,seg,data)
Data *data;
{
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "msg_type", json_object_new_string("ChangeCipherSpec"));
ssl_process_change_cipher_spec(ssl,ssl->decoder,dir);
@ -25,7 +30,7 @@ static int decode_ContentType_ChangeCipherSpec(ssl,dir,seg,data)
ssl->r_state=SSL_ST_SENT_CHANGE_CIPHER_SPEC;
}
printf("\n");
LF;
return(0);
}
@ -37,9 +42,10 @@ static int decode_ContentType_Alert(ssl,dir,seg,data)
{
int r;
struct json_object *jobj;
if(ssl->record_encryption==REC_CIPHERTEXT){
printf("\n");
LF;
return(0);
}
@ -49,17 +55,20 @@ static int decode_ContentType_Alert(ssl,dir,seg,data)
ERETURN(R_EOD);
}
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "msg_type", json_object_new_string("Alert"));
P_(P_HL){
printf("\n");
LF;
SSL_DECODE_ENUM(ssl,"level",1,AlertLevel_decoder,P_HL,data,0);
printf("\n");
LF;
SSL_DECODE_ENUM(ssl,"value",1,AlertDescription_decoder,P_HL,data,0);
printf("\n");
LF;
}
else {
SSL_DECODE_ENUM(ssl,0,1,AlertLevel_decoder,SSL_PRINT_ALL,data,0);
SSL_DECODE_ENUM(ssl,0,1,AlertDescription_decoder,SSL_PRINT_ALL,data,0);
printf("\n");
LF;
}
return(0);
@ -77,9 +86,10 @@ static int decode_ContentType_Handshake(ssl,dir,seg,data)
UINT4 t,l;
int rs=0;
Data d;
struct json_object *jobj;
if(ssl->record_encryption==REC_CIPHERTEXT){
printf("\n");
LF;
return(0);
}
@ -93,13 +103,16 @@ static int decode_ContentType_Handshake(ssl,dir,seg,data)
ERETURN(R_EOD);
}
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "msg_type", json_object_new_string("Handshake"));
d.data=data->data;
d.len=l;
data->len-=l;
data->data+=l;
P_(P_HL){
if(!rs){
printf("\n");
LF;
rs=1;
}
}
@ -118,6 +131,9 @@ static int decode_ContentType_application_data(ssl,dir,seg,data)
int r;
Data d;
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "msg_type", json_object_new_string("application data"));
SSL_DECODE_OPAQUE_ARRAY(ssl,"data",data->len,0,data,&d);
@ -125,7 +141,7 @@ static int decode_ContentType_application_data(ssl,dir,seg,data)
print_data(ssl,&d);
}
else {
printf("\n");
LF;
}
return(0);
@ -160,9 +176,12 @@ static int decode_HandshakeType_HelloRequest(ssl,dir,seg,data)
segment *seg;
Data *data;
{
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "handshake_type", json_object_new_string("HelloRequest"));
printf("\n");
LF;
return(0);
}
@ -173,6 +192,9 @@ static int decode_HandshakeType_ClientHello(ssl,dir,seg,data)
Data *data;
{
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "handshake_type", json_object_new_string("ClientHello"));
UINT4 vj,vn,cs,cslen,complen,comp,odd,exlen,ex;
Data session_id,random;
@ -182,13 +204,13 @@ static int decode_HandshakeType_ClientHello(ssl,dir,seg,data)
extern decoder compression_method_decoder[];
extern decoder extension_decoder[];
printf("\n");
LF;
ssl_update_handshake_messages(ssl,data);
SSL_DECODE_UINT8(ssl,0,0,data,&vj);
SSL_DECODE_UINT8(ssl,0,0,data,&vn);
P_(P_HL) {explain(ssl,"Version %d.%d ",vj,vn);
printf("\n");
LF;
}
SSL_DECODE_OPAQUE_ARRAY(ssl,"random",32,P_ND,data,&random);
@ -219,7 +241,7 @@ static int decode_HandshakeType_ClientHello(ssl,dir,seg,data)
ssl_decode_enum(ssl,0,2,cipher_suite_decoder,
0,data,&cs);
ssl_print_cipher_suite(ssl,(vj<<8)|vn,P_HL,cs);
printf("\n");
LF;
}
}
@ -228,7 +250,7 @@ static int decode_HandshakeType_ClientHello(ssl,dir,seg,data)
explain(ssl,"compression methods\n");
for(;complen;complen--){
SSL_DECODE_ENUM(ssl,0,1,compression_method_decoder,P_HL,data,&comp);
printf("\n");
LF;
}
}
@ -244,7 +266,7 @@ static int decode_HandshakeType_ClientHello(ssl,dir,seg,data)
}
continue;
}
printf("\n");
LF;
}
}
return(0);
@ -263,14 +285,18 @@ static int decode_HandshakeType_ServerHello(ssl,dir,seg,data)
extern decoder extension_decoder[];
printf("\n");
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "handshake_type", json_object_new_string("ServerHello"));
LF;
ssl_update_handshake_messages(ssl,data);
SSL_DECODE_UINT8(ssl,0,0,data,&vj);
SSL_DECODE_UINT8(ssl,0,0,data,&vn);
ssl->version=vj*256+vn;
P_(P_HL) {explain(ssl,"Version %d.%d ",vj,vn);
printf("\n");
LF;
}
@ -288,9 +314,9 @@ static int decode_HandshakeType_ServerHello(ssl,dir,seg,data)
ssl_process_server_session_id(ssl,ssl->decoder,session_id.data,
session_id.len);
P_(P_HL) printf("\n");
P_(P_HL) LF;
SSL_DECODE_ENUM(ssl,"compressionMethod",1,compression_method_decoder,P_HL,data,0);
P_(P_HL) printf("\n");
P_(P_HL) LF;
SSL_DECODE_UINT16(ssl,"extensions len",0,data,&exlen);
if (exlen) {
@ -304,7 +330,7 @@ static int decode_HandshakeType_ServerHello(ssl,dir,seg,data)
}
continue;
}
printf("\n");
LF;
}
}
@ -323,10 +349,16 @@ static int decode_HandshakeType_Certificate(ssl,dir,seg,data)
Data cert;
int r;
printf("\n");
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "handshake_type", json_object_new_string("Certificate"));
LF;
ssl_update_handshake_messages(ssl,data);
SSL_DECODE_UINT24(ssl,"certificates len",0,data,&len);
json_object_object_add(jobj, "cert_chain", json_object_new_array());
while(len){
SSL_DECODE_OPAQUE_ARRAY(ssl,"certificate",-((1<<23)-1),
0,data,&cert);
@ -347,7 +379,11 @@ static int decode_HandshakeType_ServerKeyExchange(ssl,dir,seg,data)
int r;
printf("\n");
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "handshake_type", json_object_new_string("ServerKeyExchange"));
LF;
ssl_update_handshake_messages(ssl,data);
if(ssl->cs){
P_(P_ND){
@ -385,14 +421,18 @@ static int decode_HandshakeType_CertificateRequest(ssl,dir,seg,data)
Data ca;
int r;
printf("\n");
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "handshake_type", json_object_new_string("CertificateRequest"));
LF;
ssl_update_handshake_messages(ssl,data);
SSL_DECODE_UINT8(ssl,"certificate_types len",0,data,&len);
for(;len;len--){
SSL_DECODE_ENUM(ssl,"certificate_types",1,
client_certificate_type_decoder, P_HL,data,0);
P_(P_HL){
printf("\n");
LF;
}
};
@ -416,8 +456,11 @@ static int decode_HandshakeType_ServerHelloDone(ssl,dir,seg,data)
Data *data;
{
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "handshake_type", json_object_new_string("ServerHelloDone"));
printf("\n");
LF;
ssl_update_handshake_messages(ssl,data);
return(0);
@ -431,7 +474,12 @@ static int decode_HandshakeType_CertificateVerify(ssl,dir,seg,data)
int r;
printf("\n");
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "handshake_type", json_object_new_string("CertificateVerify"));
LF;
ssl_update_handshake_messages(ssl,data);
SSL_DECODE_OPAQUE_ARRAY(ssl,"Signature",-((1<<15)-1),P_HL,data,0);
return(0);
@ -448,7 +496,11 @@ static int decode_HandshakeType_ClientKeyExchange(ssl,dir,seg,data)
int r;
Data pms;
printf("\n");
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "handshake_type", json_object_new_string("ClientKeyExchange"));
LF;
ssl_update_handshake_messages(ssl,data);
if(ssl->cs){
switch(ssl->cs->kex){
@ -486,7 +538,11 @@ static int decode_HandshakeType_Finished(ssl,dir,seg,data)
int r;
printf("\n");
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "handshake_type", json_object_new_string("Finished"));
LF;
switch(ssl->version){
case 0x300:
SSL_DECODE_OPAQUE_ARRAY(ssl,"md5_hash",16,P_ND,data,0);
@ -496,7 +552,7 @@ static int decode_HandshakeType_Finished(ssl,dir,seg,data)
case 0x301:
SSL_DECODE_OPAQUE_ARRAY(ssl,"verify_data",12,P_ND,data,0);
P_(P_ND)
printf("\n");
LF;
break;
}
@ -2124,6 +2180,9 @@ static int decode_AlertLevel_warning(ssl,dir,seg,data)
segment *seg;
Data *data;
{
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "alert_level", json_object_new_string("warning"));
return(0);
}
static int decode_AlertLevel_fatal(ssl,dir,seg,data)
@ -2132,6 +2191,9 @@ static int decode_AlertLevel_fatal(ssl,dir,seg,data)
segment *seg;
Data *data;
{
struct json_object *jobj;
jobj = ssl->cur_json_st;
json_object_object_add(jobj, "alert_level", json_object_new_string("fatal"));
return(0);
}
decoder AlertLevel_decoder[]={
@ -2530,7 +2592,7 @@ static int decode_extension_server_name(ssl,dir,seg,data)
if(dir==DIR_I2R){
SSL_DECODE_UINT16(ssl,"server name list length",0,data,&l);
printf("\n");
LF;
while(l) {
p=data->len;
SSL_DECODE_UINT8(ssl, "server name type", 0, data, &t);
@ -2896,6 +2958,7 @@ static int decode_server_name_type_host_name(ssl,dir,seg,data)
int r;
UINT4 l;
SSL_DECODE_UINT16(ssl,"server name length",0,data,&l);
if(!(NET_print_flags & NET_PRINT_JSON))
printf(": %.*s",l,data->data);
/* Possibly use data->data to set/modify ssl->server_name */

View file

@ -44,7 +44,8 @@
*/
#include <json-c/json.h>
#include <arpa/inet.h>
#include "network.h"
#include "debug.h"
#include "sslprint.h"
@ -246,8 +247,14 @@ static int create_ssl_analyzer(void *handle, proto_ctx *ctx, tcp_conn *conn,
ABORT(r);
lookuphostname(i_addr,&obj->client_name);
if(!(obj->client_ip=(char *)calloc(1,INET_ADDRSTRLEN)))
ABORT(R_NO_MEMORY);
inet_ntop(AF_INET, i_addr, obj->client_ip, INET_ADDRSTRLEN);
obj->client_port=i_port;
lookuphostname(r_addr,&obj->server_name);
if(!(obj->server_ip=(char *)calloc(1,INET_ADDRSTRLEN)))
ABORT(R_NO_MEMORY);
inet_ntop(AF_INET, r_addr, obj->server_ip, INET_ADDRSTRLEN);
obj->server_port=r_port;
obj->i_state=SSL_ST_SENT_NOTHING;
@ -574,6 +581,8 @@ static int print_ssl_record(obj,direction,q,data,len)
{
int r;
obj->cur_json_st = json_object_new_object();
if((r=print_ssl_header(obj,direction,q,data,len)))
ERETURN(r);
@ -583,9 +592,14 @@ static int print_ssl_record(obj,direction,q,data,len)
INIT_DATA(d,data,len);
exdump(obj,"Packet data",&d);
printf("\n\n");
LF;LF;
}
if(SSL_print_flags & SSL_PRINT_JSON)
printf("%s\n", json_object_to_json_string(obj->cur_json_st));
json_object_put(obj->cur_json_st);
obj->cur_json_st = NULL;
return(0);
}
@ -609,8 +623,7 @@ int close_ssl_analyzer(_obj,p,dir)
ssl_print_timestamp(ssl,&p->ts);
ssl_print_direction_indicator(ssl,dir);
explain(ssl," TCP %s",what);
printf("\n");
LF;
return(0);
}

View file

@ -62,6 +62,7 @@ extern proto_mod ssl_mod;
#define P_TSA (1<<12)
#define P_QT (1<<13)
#define P_HO (1<<14)
#define P_JS (1<<15)
#define SSL_PRINT_TIMESTAMP (1) /*Timestamp records*/
#define SSL_PRINT_HEXDUMP (1<<2) /*Print the whole record in hex*/
@ -77,6 +78,7 @@ extern proto_mod ssl_mod;
#define SSL_PRINT_TIMESTAMP_ABSOLUTE (P_TSA)
#define SSL_PRINT_QUIET (P_QT)
#define SSL_PRINT_HEX_ONLY (P_HO)
#define SSL_PRINT_JSON (P_JS)
#define SSL_PRINT_ALL 0xfffffff
extern UINT4 SSL_print_flags;

View file

@ -81,8 +81,10 @@ typedef struct ssl_obj_ {
UINT4 cipher_suite;
char *client_name;
char *client_ip;
int client_port;
char *server_name;
char *server_ip;
int server_port;
struct SSL_CipherSuite_ *cs;
@ -106,6 +108,7 @@ typedef struct ssl_obj_ {
int record_count;
int indent_depth;
int indent_name_len;
struct json_object *cur_json_st;
} ssl_obj;
typedef struct decoder_ {

View file

@ -47,8 +47,8 @@
#ifndef _ssldecode_h
#define _ssldecode_h
#define CRDUMP(a,b,c) P_(P_CR) {Data d; d.data=b; d.len=c; exdump(ssl,a,&d); printf("\n");}
#define CRDUMPD(a,b) P_(P_CR) {exdump(ssl,a,b);printf("\n");}
#define CRDUMP(a,b,c) P_(P_CR) {Data d; d.data=b; d.len=c; exdump(ssl,a,&d); LF;}
#define CRDUMPD(a,b) P_(P_CR) {exdump(ssl,a,b);LF;}
int ssl_decode_ctx_create PROTO_LIST((ssl_decode_ctx **ctx,
char *keyfile,char *password,char *keylogfile));

View file

@ -44,7 +44,7 @@
*/
#include <json-c/json.h>
#include <ctype.h>
#include <stdarg.h>
#include "network.h"
@ -83,7 +83,7 @@ int process_beginning_plaintext(ssl,seg,direction)
ssl_print_direction_indicator(ssl,direction);
print_data(ssl,&d);
printf("\n");
LF;
}
return(0);
@ -140,7 +140,7 @@ int process_v2_hello(ssl,seg)
P_(P_HL) {
explain(ssl,"Version %d.%d ",(ver>>8)&0xff,
ver&0xff);
printf("\n");
LF;
}
SSL_DECODE_UINT16(ssl,"cipher_spec_length",P_DC,&d,&cs_len);
SSL_DECODE_UINT16(ssl,"session_id_length",P_DC,&d,&sid_len);
@ -191,7 +191,7 @@ int process_v2_hello(ssl,seg)
INIT_DATA(d,seg->data,seg->len);
exdump(ssl,"Packet data",&d);
printf("\n\n");
LF;LF;
}
INDENT_POP;
@ -235,6 +235,11 @@ int ssl_expand_record(ssl,q,direction,data,len)
Data d;
UINT4 ct,vermaj,vermin,length;
int version;
char verstr[4];
char enumstr[20];
struct json_object *jobj;
jobj = ssl->cur_json_st;
d.data=data;
d.len=len;
@ -251,9 +256,11 @@ int ssl_expand_record(ssl,q,direction,data,len)
P_(P_RH){
explain(ssl," V%d.%d(%d)",vermaj,vermin,length);
json_object_object_add(jobj, "record_len", json_object_new_int(length));
snprintf(verstr,4,"%d.%d",vermaj,vermin);
json_object_object_add(jobj, "record_ver", json_object_new_string(verstr));
}
version=vermaj*256+vermin;
r=ssl_decode_record(ssl,ssl->decoder,direction,ct,version,&d);
@ -264,11 +271,16 @@ int ssl_expand_record(ssl,q,direction,data,len)
}
if(r){
if(!(SSL_print_flags & SSL_PRINT_JSON))
if((r=ssl_print_enum(ssl,0,ContentType_decoder,ct))) {
printf(" unknown record type: %d\n", ct);
ERETURN(r);
}
printf("\n");
ssl_get_enum_str(ssl,enumstr,ContentType_decoder,ct);
json_object_object_add(jobj, "msg_type", json_object_new_string(enumstr));
if(!(SSL_print_flags & SSL_PRINT_JSON))
LF;
}
else{
//try to save unencrypted data to logger
@ -276,6 +288,7 @@ int ssl_expand_record(ssl,q,direction,data,len)
if ((ct == 23) && (logger)) logger->vtbl->data(ssl->logger_obj,d.data,d.len,direction);
if((r=ssl_decode_switch(ssl,ContentType_decoder,data[0],direction,q, &d))) {
if(!(SSL_print_flags & SSL_PRINT_JSON))
printf(" unknown record type: %d\n", ct);
ERETURN(r);
}
@ -394,6 +407,7 @@ int ssl_decode_enum(ssl,name,size,dtable,p,data,x)
ERETURN(r);
P_(p){
if(!(SSL_print_flags & SSL_PRINT_JSON))
if((r=ssl_print_enum(ssl,name,dtable,*x)))
ERETURN(r);
}
@ -407,6 +421,7 @@ int ssl_print_enum(ssl,name,dtable,value)
decoder *dtable;
UINT4 value;
{
if(name) explain(ssl,"%s ",name);
INDENT;
@ -419,14 +434,30 @@ int ssl_print_enum(ssl,name,dtable,value)
}
dtable++;
}
printf("\n");
LF;
return(R_NOT_FOUND);
}
int ssl_get_enum_str(ssl,outstr,dtable,value)
ssl_obj *ssl;
char *outstr;
decoder *dtable;
UINT4 value;
{
while(dtable && dtable->type!=-1){
if(dtable->type == value){
strncpy(outstr, dtable->name, 20);
return(0);
}
dtable++;
}
return(R_NOT_FOUND);
}
int explain(ssl_obj *ssl,char *format,...)
{
va_list ap;
if(!(SSL_print_flags & SSL_PRINT_JSON)) {
va_start(ap,format);
P_(P_NR){
@ -439,6 +470,7 @@ int explain(ssl_obj *ssl,char *format,...)
vprintf(format,ap);
va_end(ap);
}
return(0);
}
@ -448,7 +480,7 @@ int exdump(ssl,name,data)
Data *data;
{
int i;
if(!(SSL_print_flags & SSL_PRINT_JSON)) {
if(name){
explain(ssl,"%s[%d]=\n",name,data->len);
INDENT_INCR;
@ -461,7 +493,7 @@ int exdump(ssl,name,data)
if(!i) INDENT;
if((data->len>8) && i && !(i%16)){
printf("\n"); INDENT;
LF; INDENT;
}
printf("%.2x ",data->data[i]&255);
}
@ -469,7 +501,28 @@ int exdump(ssl,name,data)
printf("\\fR");
}
if(name) INDENT_POP;
printf("\n");
LF;
}
return(0);
}
int exstr(ssl,outstr,data)
ssl_obj *ssl;
char *outstr;
Data *data;
{
int i;
char *ptr = outstr;
for(i=0;i<data->len;i++){
sprintf(ptr, "%.2x",data->data[i]&255);
ptr+=2;
if(i<data->len - 1) {
sprintf(ptr, ":");
++ptr;
}
}
return(0);
}
@ -518,7 +571,7 @@ int combodump(ssl,name,data)
else
printf(".");
}
printf("\n");
LF;
len-=bytes;
ptr+=bytes;
@ -536,7 +589,7 @@ int print_data(ssl,d)
{
int i,bit8=0;
printf("\n");
LF;
for(i=0;i<d->len;i++){
if(d->data[i] == 0 || (!isprint(d->data[i]) && !strchr("\r\n\t",d->data[i]))){
bit8=1;
@ -590,6 +643,7 @@ int ssl_print_direction_indicator(ssl,dir)
ssl_obj *ssl;
int dir;
{
struct json_object *jobj;
#if 0
if(dir==DIR_I2R){
explain(ssl,"%s(%d) > %s>%d",
@ -600,11 +654,30 @@ int ssl_print_direction_indicator(ssl,dir)
ssl->client_name,ssl->client_port,ssl->server_name,ssl->server_port);
}
#else
jobj = ssl->cur_json_st;
if(dir==DIR_I2R){
explain(ssl,"C>S");
if(jobj) {
json_object_object_add(jobj, "src_name", json_object_new_string(ssl->client_name));
json_object_object_add(jobj, "src_ip", json_object_new_string(ssl->client_ip));
json_object_object_add(jobj, "src_port", json_object_new_int(ssl->client_port));
json_object_object_add(jobj, "dst_name", json_object_new_string(ssl->server_name));
json_object_object_add(jobj, "dst_ip", json_object_new_string(ssl->server_ip));
json_object_object_add(jobj, "dst_port", json_object_new_int(ssl->server_port));
}
}
else{
explain(ssl,"S>C");
if(jobj) {
json_object_object_add(jobj, "src_name", json_object_new_string(ssl->server_name));
json_object_object_add(jobj, "src_ip", json_object_new_string(ssl->server_ip));
json_object_object_add(jobj, "src_port", json_object_new_int(ssl->server_port));
json_object_object_add(jobj, "dst_name", json_object_new_string(ssl->client_name));
json_object_object_add(jobj, "dst_ip", json_object_new_string(ssl->client_ip));
json_object_object_add(jobj, "dst_port", json_object_new_int(ssl->client_port));
}
}
#endif
@ -618,18 +691,30 @@ int ssl_print_timestamp(ssl,ts)
struct timeval dt;
int r;
char ts_str[16];
struct json_object *jobj;
jobj = ssl->cur_json_st;
if(jobj) {
snprintf(ts_str,16, "%d%c%4.4d",ts->tv_sec,'.',ts->tv_usec/100);
json_object *j_ts_str = json_object_new_string(ts_str);
json_object_object_add(jobj, "timestamp", j_ts_str);
}
if(SSL_print_flags & SSL_PRINT_TIMESTAMP_ABSOLUTE) {
if(!(SSL_print_flags & SSL_PRINT_JSON))
explain(ssl,"%d%c%4.4d ",ts->tv_sec,'.',ts->tv_usec/100);
}
else{
if((r=timestamp_diff(ts,&ssl->time_start,&dt)))
ERETURN(r);
if(!(SSL_print_flags & SSL_PRINT_JSON))
explain(ssl,"%d%c%4.4d ",dt.tv_sec,'.',dt.tv_usec/100);
}
if((r=timestamp_diff(ts,&ssl->time_last,&dt))){
ERETURN(r);
}
if(!(SSL_print_flags & SSL_PRINT_JSON))
explain(ssl,"(%d%c%4.4d) ",dt.tv_sec,'.',dt.tv_usec/100);
memcpy(&ssl->time_last,ts,sizeof(struct timeval));
@ -641,7 +726,11 @@ int ssl_print_timestamp(ssl,ts)
int ssl_print_record_num(ssl)
ssl_obj *ssl;
{
struct json_object *jobj;
jobj = ssl->cur_json_st;
ssl->record_count++;
if(!(SSL_print_flags & SSL_PRINT_JSON))
if(SSL_print_flags & SSL_PRINT_NROFF){
printf("\\fI%d %d\\fR %s",
ssl->conn->conn_number,
@ -652,6 +741,9 @@ int ssl_print_record_num(ssl)
ssl->record_count,ssl->record_count<10?" ":"");
}
json_object_object_add(jobj, "connection_number", json_object_new_int(ssl->conn->conn_number));
json_object_object_add(jobj, "record_count", json_object_new_int(ssl->record_count));
return(0);
}

View file

@ -65,6 +65,8 @@ int ssl_lookup_enum PROTO_LIST((ssl_obj *ssl,decoder *dtable,
UINT4 val,char **ptr));
int ssl_print_enum PROTO_LIST((ssl_obj *obj,char *name,
decoder *decode,UINT4 value));
int ssl_get_enum_str PROTO_LIST((ssl_obj *obj,char *outstr,
decoder *decode,UINT4 value));
int print_data PROTO_LIST((ssl_obj *ssl,Data *d));
int process_v2_hello PROTO_LIST((ssl_obj *ssl,segment *seg));
int process_beginning_plaintext PROTO_LIST((ssl_obj *ssl,
@ -77,6 +79,7 @@ int ssl_print_cipher_suite PROTO_LIST((ssl_obj *ssl,int version,int p,
int explain PROTO_LIST((ssl_obj *ssl,char *format,...));
int exdump PROTO_LIST((ssl_obj *ssl,char *name,Data *data));
int exstr PROTO_LIST((ssl_obj *ssl,char *name,Data *data));
#define SSL_DECODE_UINT8(a,n,b,c,d) if((r=ssl_decode_uintX(a,n,1,b,c,d))) ERETURN(r)
@ -87,13 +90,13 @@ int exdump PROTO_LIST((ssl_obj *ssl,char *name,Data *data));
#define SSL_DECODE_ENUM(a,b,c,d,e,f,g) if((r=ssl_decode_enum(a,b,c,d,e,f,g))) ERETURN(r)
#define P_(p) if((p==SSL_PRINT_ALL) || (p & SSL_print_flags))
#define INDENT do {int i; for(i=0;i<(ssl->indent_depth + ssl->indent_name_len);i++) printf("%s",SSL_print_flags & SSL_PRINT_NROFF?" ":" ");} while(0)
#define INDENT if(!(NET_print_flags & NET_PRINT_JSON)) do {int i; for(i=0;i<(ssl->indent_depth + ssl->indent_name_len);i++) printf("%s",SSL_print_flags & SSL_PRINT_NROFF?" ":" ");} while(0)
#define INDENT_INCR ssl->indent_depth+=2
#define INDENT_POP ssl->indent_depth-=2
#define INDENT_NAME(x) ssl->indent_name_len += strlen(x)
#define INDENT_NAME_POP ssl->indent_name_len=0
#define LINE_LEFT (80-(ssl->indent_name_len + ssl->indent_depth)
#define LF printf("\n")
#define LF if(!(NET_print_flags & NET_PRINT_JSON)) printf("\n")
#endif

View file

@ -44,7 +44,7 @@
*/
#include <json-c/json.h>
#include "network.h"
#include "ssl_h.h"
#include "sslprint.h"
@ -78,9 +78,25 @@ int sslx_print_certificate(ssl,data,pf)
P_(P_ASN){
char buf[BUFSIZE];
int ext;
char *b64_cert;
char *serial_str = NULL;
Data data_tmp;
struct json_object *jobj;
jobj = ssl->cur_json_st;
struct json_object *cert_obj;
cert_obj = json_object_new_object();
d=data->data;
if(!(b64_cert=(char *)calloc(1,sizeof(char) * ((((data->len) + 3 - 1)/3) * 4 + 1))))
ABORT(R_NO_MEMORY);
EVP_EncodeBlock((unsigned char *)b64_cert, d, data->len);
json_object_object_add(cert_obj, "cert_der", json_object_new_string(b64_cert));
if(!(x=d2i_X509(0,(const unsigned char **) &d,data->len))){
explain(ssl,"Bad certificate");
ABORT(R_BAD_DATA);
@ -89,16 +105,24 @@ int sslx_print_certificate(ssl,data,pf)
BUFSIZE);
explain(ssl,"Subject\n");
INDENT_INCR;
json_object_object_add(cert_obj, "cert_subject", json_object_new_string(buf));
sslx__print_dn(ssl,buf);
INDENT_POP;
X509_NAME_oneline(X509_get_issuer_name(x),buf,
BUFSIZE);
explain(ssl,"Issuer\n");
INDENT_INCR;
json_object_object_add(cert_obj, "cert_issuer", json_object_new_string(buf));
sslx__print_dn(ssl,buf);
INDENT_POP;
a=X509_get_serialNumber(x);
explain(ssl,"Serial ");
if(!(serial_str=(char *)calloc(1,sizeof(char) * (a->length * 3))))
ABORT(R_NO_MEMORY);
INIT_DATA(data_tmp,a->data,a->length);
exstr(ssl, serial_str, &data_tmp);
json_object_object_add(cert_obj, "cert_serial", json_object_new_string(serial_str));
sslx__print_serial(ssl,a);
ext=X509_get_ext_count(x);
@ -147,6 +171,10 @@ int sslx_print_certificate(ssl,data,pf)
}
#ifdef OPENSSL
}
struct json_object *certs_array;
json_object_object_get_ex(jobj, "cert_chain", &certs_array);
json_object_array_add(certs_array,cert_obj);
}
#endif

164
ssldump.1
View file

@ -61,8 +61,7 @@ ssldump \- dump SSL traffic on a network
.na
.B ssldump
[
.B \-vTshVq
.B \-aAdeHnNqTxXvy
.B \-aAdeFHjnNPqtTvxXy
] [
.B \-i
.I interface
@ -110,14 +109,14 @@ form to stdout. If provided with the appropriate keying material,
it will also decrypt the connections and display the application
data traffic.
.LP
ssldump has been tested on FreeBSD, Linux, Solaris, and HP/UX. Since
\fIssldump\fP has been tested on FreeBSD, Linux, Solaris, and HP/UX. Since
it's based on PCAP, it should work on most platforms. However, unlike
tcpdump, ssldump needs to be able to see both sides of the data
tcpdump, \fIssldump\fP needs to be able to see both sides of the data
transmission so you may have trouble using it with network taps such
as SunOS nit that don't permit you to see transmitted data.
.B Under SunOS with nit or bpf:
To run
.I tcpdump
.I ssldump
you must have read access to
.I /dev/nit
or
@ -135,46 +134,65 @@ You must be root or it must be installed setuid to root.
Once the super-user has enabled promiscuous-mode operation using
.IR pfconfig (8),
any user may run
.BR ssldump
.I ssldump
.B Under BSD:
You must have read access to
.IR /dev/bpf* .
.SH OPTIONS
.TP
.B \-a
Print bare TCP ACKs (useful for observing Nagle behavior)
Print bare TCP ACKs (useful for observing Nagle behavior).
.TP
.B \-A
Print all record fields (by default ssldump chooses
the most interesting fields)
Print all record fields (by default \fIssldump\fP chooses
the most interesting fields).
.TP
.B \-d
Display the application data traffic. This usually means
decrypting it, but when -d is used ssldump will also decode
decrypting it, but when -d is used \fIssldump\fP will also decode
application data traffic \fIbefore\fP the SSL session initiates.
This allows you to see HTTPS CONNECT behavior as well as
SMTP STARTTLS. As a side effect, since ssldump can't tell
SMTP STARTTLS. As a side effect, since \fIssldump\fP can't tell
whether plaintext is traffic before the initiation of an
SSL connection or just a regular TCP connection, this allows
you to use ssldump to sniff any TCP connection.
ssldump will automatically detect ASCII data and display it
you to use \fIssldump\fP to sniff any TCP connection.
\fIssldump\fP will automatically detect ASCII data and display it
directly to the screen. non-ASCII data is displayed as hex
dumps. See also -X.
.TP
.B \-e
Print absolute timestamps instead of relative timestamps
Print absolute timestamps instead of relative timestamps.
.TP
.B \-F
Specify the number of packets after which a connection pool cleaning is performed (in packets, default: 100).
.TP
.B \-H
Print the full SSL packet header.
.TP
.BI \-i " interface"
Use \fIinterface\fP as the network interface on which to sniff SSL/TLS
traffic.
.TP
.B \-j
Switch output format to JSON. Only stdout is affected by this toggle.
.TP
.BI \-k " keyfile"
Use \fIkeyfile\fP as the location of the SSL keyfile (OpenSSL format)
Previous versions of \fIssldump\fP automatically looked in ./server.pem.
Now you must specify your keyfile every time.
.TP
.BI \-l " sslkeylogfile"
Use \fIsslkeylogfile\fP as the location of the SSLKEYLOGFILE
(https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format).
.TP
.B \-n
Don't try to resolve host names from IP addresses
Don't try to resolve host names from IP addresses.
.TP
.B \-N
Attempt to parse ASN.1 when it appears, such as in
certificates and DNs.
.TP
.B \-p
.BI \-p " password"
Use \fIpassword\fP as the SSL keyfile password.
.TP
.B \-P
@ -183,55 +201,13 @@ Don't put the interface into promiscuous mode.
.B \-q
Don't decode any record fields beyond a single summary line. (quiet mode).
.TP
.B \-T
Print the TCP headers.
.TP
.B \-v
Display version and copyright information.
.TP
.B \-t
Specify the TTL for inactive connections referenced in the connection pool (in seconds, default: 100).
.TP
.B \-F
Specify the number of packets after which a connection pool cleaning is performed (in packets, default: 100).
.TP
.B \-x
Print each record in hex, as well as decoding it.
.TP
.B \-X
When the -d option is used, binary data is automatically printed
in two columns with a hex dump on the left and the printable characters
on the right. -X suppresses the display of the printable characters,
thus making it easier to cut and paste the hex data into some other
program.
.TP
.B \-y
Decorate the output for processing with nroff/troff. Not very
useful for the average user.
.TP
.BI \-i " interface"
Use \fIinterface\fP as the network interface on which to sniff SSL/TLS
traffic.
.TP
.BI \-k " keyfile"
Use \fIkeyfile\fP as the location of the SSL keyfile (OpenSSL format)
Previous versions of ssldump automatically looked in ./server.pem.
Now you must specify your keyfile every time.
.TP
.BI \-l " sslkeylogfile"
Use \fIsslkeylogfile\fP as the location of the SSLKEYLOGFILE
(https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format)
.TP
.BI \-p " password"
Use \fIpassword\fP as the SSL keyfile password.
.TP
.BI \-r " file"
Read data from \fIfile\fP instead of from the network.
The old -f option still works but is deprecated and will
probably be removed with the next version.
.TP
.BI \-S " [ " crypto " | " d " | " ht " | " H " ]"
Specify SSL flags to ssldump. These flags include:
Specify SSL flags to \fIssldump\fP. These flags include:
.RS
.TP
.I crypto
@ -247,17 +223,40 @@ Print the handshake type.
Print handshake type and highlights.
.RE
.TP
.B \-t
Specify the TTL for inactive connections referenced in the connection pool (in seconds, default: 100).
.TP
.B \-T
Print the TCP headers.
.TP
.B \-v
Display version and copyright information.
.TP
.BI \-w " outputpcap"
Use \fIoutputpcap\fP as the destination for decrypted packets.
.TP
.B \-x
Print each record in hex, as well as decoding it.
.TP
.B \-X
When the -d option is used, binary data is automatically printed
in two columns with a hex dump on the left and the printable characters
on the right. -X suppresses the display of the printable characters,
thus making it easier to cut and paste the hex data into some other
program.
.TP
.B \-y
Decorate the output for processing with nroff/troff. Not very
useful for the average user.
.TP
\fIexpression\fP
.RS
Selects what packets ssldump will examine. Technically speaking,
ssldump supports the full expression syntax from PCAP and tcpdump.
Selects what packets \fIssldump\fP will examine. Technically speaking,
\fIssldump\fP supports the full expression syntax from PCAP and tcpdump.
In fact, the description here is cribbed from the tcpdump man
page. However, since ssldump needs to examine full TCP streams,
page. However, since \fIssldump\fP needs to examine full TCP streams,
most of the tcpdump expressions will select traffic mixes
that ssldump will simply ignore. Only the expressions which
that \fIssldump\fP will simply ignore. Only the expressions which
don't result in incomplete TCP streams are listed here.
.LP
The \fIexpression\fP consists of one or more
@ -431,29 +430,36 @@ which should not be confused with
.fi
.in -.5i
.LP
Expression arguments can be passed to ssldump as either a single argument
Expression arguments can be passed to \fIssldump\fP as either a single argument
or as multiple arguments, whichever is more convenient.
Generally, if the expression contains Shell metacharacters, it is
easier to pass it as a single, quoted argument.
Multiple arguments are concatenated with spaces before being parsed.
.SH EXAMPLES
.LP
To listen to traffic on interface \fIle0\fP port \fI443\fP
To listen to traffic on interface \fIle0\fP port \fI443\fP:
.RS
.nf
\fBssldump -i le0 port 443\fP
.fi
.RE
.LP
To listen to traffic to the server \fIromeo\fP on port \fI443\fP.
To listen to traffic to the server \fIromeo\fP on port \fI443\fP:
.RS
.nf
\fBssldump -i le0 port 443 and host romeo\fP
\fBssldump -i le0 port 443 and host romeo\fP:
.fi
.RE
.LP
To switch output format to JSON:
.RS
.nf
\fBssldump -ANH -j -i le0 port 443 and host romeo\fP
.fi
.RE
.LP
To decrypt traffic to host \fIromeo\fR
\fIserver.pem\fR and the password \fIfoobar\fR
\fIserver.pem\fR and the password \fIfoobar\fR:
.RS
.nf
\fBssldump -Ad -k ~/server.pem -p foobar -i le0 host romeo
@ -463,7 +469,7 @@ To decrypt traffic to host \fIromeo\fR
.LP
All output is printed to standard out.
.LP
ssldump prints an indication of every new TCP connection using a line
\fIssldump\fP prints an indication of every new TCP connection using a line
like the following
.nf
.LP
@ -493,16 +499,16 @@ the time since the previous record. Both are in seconds.
The next field in the record line is the direction that the record
was going. \fIC>S\fR indicates records transmitted from client to
server and \fIS>C\fR indicates records transmitted from server to client.
ssldump assumes that the host to transmit the first SYN
\fIssldump\fP assumes that the host to transmit the first SYN
is the SSL client (this is nearly always correct).
.LP
The next field is the record type, one of \fIHandshake\fR, \fIIAlert\fR,
\fIChangeCipherSpec\fR, or \fIapplication_data\fR. Finally, ssldump
\fIChangeCipherSpec\fR, or \fIapplication_data\fR. Finally, \fIssldump\fP
may print record-specific data on the rest of the line. For \fIHandshake\fR
records, it prints the handshake message. Thus, this record is
a \fICertificate\fR message.
.LP
ssldump chooses certain record types for further decoding. These
\fIssldump\fP chooses certain record types for further decoding. These
are the ones that have proven to be most useful for debugging:
.LP
.nf
@ -525,16 +531,16 @@ flags.
.LP
.SH DECRYPTION
.LP
ssldump can decrypt traffic between two hosts if the following two
\fIssldump\fP can decrypt traffic between two hosts if the following two
conditions are met:
.RS
.nf
1. ssldump has the keys.
1. \fIssldump\fP has the keys.
2. Static RSA was used.
.fi
.RE
In any other case, once encryption starts,
ssldump will only be able to determine the
\fIssldump\fP will only be able to determine the
record type. Consider the following section of a trace.
.LP
.nf
@ -553,7 +559,7 @@ record type. Consider the following section of a trace.
Note that the \fIClientKeyExchange\fR message type is printed
but the rest of the \fIHandshake\fR messages do not have
types. These are the \fIFinished\fR messages, but because they
are encrypted ssldump only knows that they are of type \fIHandshake\fR.
are encrypted \fIssldump\fP only knows that they are of type \fIHandshake\fR.
Similarly, had the \fIAlert\fR in record 14 happened during the handshake,
it's type and level would have been printed. However, since it
is encrypted we can only tell that it is an alert.
@ -569,9 +575,9 @@ implemented. In practice, this turns out not to be much of a problem.
Support is provided for only for Ethernet and loopback interfaces
because that's all that I have. If you have another kind of network
you will need to modify pcap_cb in base/pcap-snoop.c. If you have
direct experience with ssldump on other networks, please send me patches.
direct experience with \fIssldump\fP on other networks, please send me patches.
.LP
ssldump doesn't implement session caching and therefore can't decrypt
\fIssldump\fP doesn't implement session caching and therefore can't decrypt
resumed sessions.
.LP
.SH SEE ALSO
@ -580,4 +586,4 @@ resumed sessions.
.LP
.SH AUTHOR
.LP
ssldump was written by Eric Rescorla <ekr@rtfm.com>.
\fIssldump\fP was written by Eric Rescorla <ekr@rtfm.com>.