diff --git a/Makefile.in b/Makefile.in index 0ac8b05..435f604 100644 --- a/Makefile.in +++ b/Makefile.in @@ -59,6 +59,7 @@ COMMONDIR=common/ COMMON_LIB_SRCDIR=$(COMMONDIR)lib/ ANALYZE_NULL_SRCDIR=$(ROOT)null/ ANALYZE_SSL_SRCDIR=$(ROOT)ssl/ +PCAP_SRCDIR=$(ROOT)pcap/ ANALYZE_RECORD_SRCDIR=$(ROOT)@RECORD_MOD@/ include rules.mk @@ -66,9 +67,10 @@ include $(COMMON_LIB_SRCDIR)/targets.mk include $(ANALYZE_SRCDIR)targets.mk include $(ANALYZE_NULL_SRCDIR)targets.mk include $(ANALYZE_SSL_SRCDIR)targets.mk +include $(PCAP_SRCDIR)targets.mk include $(ANALYZE_RECORD_SRCDIR)targets.mk -INCLUDES += -I$(COMMONDIR)include/ -I$(ANALYZE_NULL_SRCDIR) -I$(ANALYZE_SSL_SRCDIR) +INCLUDES += -I$(COMMONDIR)include/ -I$(ANALYZE_NULL_SRCDIR) -I$(ANALYZE_SSL_SRCDIR) -I$(PCAP_SRCDIR) DEFINES += @DEFINES@ diff --git a/base/pcap-snoop.c b/base/pcap-snoop.c index e217189..682a3e6 100644 --- a/base/pcap-snoop.c +++ b/base/pcap-snoop.c @@ -76,6 +76,7 @@ static char *RCSSTRING="$Id: pcap-snoop.c,v 1.14 2002/09/09 21:02:58 ekr Exp $"; #ifdef ENABLE_RECORD #include "record_analyze.h" #endif +#include "pcap_logger.h" #ifndef ETHERTYPE_8021Q # define ETHERTYPE_8021Q 0x8100 @@ -97,6 +98,8 @@ int conn_ttl = 100; // TTL of inactive connections in connection pool struct timeval last_packet_seen_time = // Timestamp of the last packet of the (struct timeval) {0}; // last block of conn_freq packets seen +logger_mod *logger=NULL; + int err_exit(str,num) char *str; int num; @@ -107,7 +110,7 @@ int err_exit(str,num) int usage() { - fprintf(stderr,"Usage: ssldump [-r dumpfile] [-i interface] [-l sslkeylogfile] \n"); + fprintf(stderr,"Usage: ssldump [-r dumpfile] [-i interface] [-l sslkeylogfile] [-w outpcapfile]\n"); fprintf(stderr," [-k keyfile] [-p password] [-vtaTnsAxVNde]\n"); fprintf(stderr," [filter]\n"); exit(0); @@ -127,6 +130,7 @@ int print_version() RETSIGTYPE sig_handler() { fflush(stdout); + if (logger) logger->vtbl->deinit(); exit(0); } @@ -296,7 +300,7 @@ int main(argc,argv) signal(SIGINT,sig_handler); - while((c=getopt(argc,argv,"vr:F:f:S:yTt:ai:k:l:p:nsAxXhHVNdqem:P"))!=EOF){ + while((c=getopt(argc,argv,"vr:F:f:S:yTt:ai:k:l:w:p:nsAxXhHVNdqem:P"))!=EOF){ switch(c){ case 'v': print_version(); @@ -332,6 +336,14 @@ int main(argc,argv) case 'l': SSL_keylogfile=strdup(optarg); break; + case 'w': + logger=&pcap_mod; + if(logger->vtbl->init(optarg)!=0){ + fprintf(stderr,"Can not open/create out pcap %s\n", + optarg); + exit(1); + } + break; case 'p': SSL_password=strdup(optarg); break; @@ -475,6 +487,10 @@ int main(argc,argv) free(SSL_keylogfile); if(SSL_password) free(SSL_password); + if (logger) + { + logger->vtbl->deinit(); + } exit(0); } diff --git a/base/proto_mod.h b/base/proto_mod.h index 2ee27a1..8dddaaf 100644 --- a/base/proto_mod.h +++ b/base/proto_mod.h @@ -82,5 +82,27 @@ int create_proto_handler PROTO_LIST((proto_mod *mod,proto_ctx *ctx, tcp_conn *conn,struct timeval *first_packet)); int destroy_proto_handler PROTO_LIST((proto_handler **handlerp)); + +//add logger +struct logger_mod_vtbl_ { + int (*init) PROTO_LIST((void *data)); + //deinit must be async signal safe(!!!) + int (*deinit) PROTO_LIST(()); + int (*create) PROTO_LIST((proto_obj **objp, struct in_addr *i_addr,u_short i_port, + struct in_addr *r_addr,u_short r_port,struct timeval *time_base)); + int (*destroy) PROTO_LIST((proto_obj **objp)); + int (*data) PROTO_LIST((proto_obj *obj,unsigned char *data,unsigned int len,int direction)); + int (*close) PROTO_LIST((proto_obj *obj,unsigned char *data,unsigned int len,int direction)); +}; + +struct logger_mod_ { + char *name; + struct logger_mod_vtbl_ *vtbl; +}; + +typedef struct logger_mod_ logger_mod; + +extern logger_mod *logger; + #endif diff --git a/pcap/attrib.h b/pcap/attrib.h new file mode 100644 index 0000000..d2e8081 --- /dev/null +++ b/pcap/attrib.h @@ -0,0 +1,73 @@ +/*- + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2019, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ATTRIB_H +#define ATTRIB_H + +/* + * GCC attributes and built-ins for improved compile-time error checking + * and performance optimization. + * + * All of these are fully optional and are automatically disabled on non-GCC + * and non-LLVM/clang compilers. + */ + +/* + * Attributes. + * These serve to improve the compiler warnings or optimizations. + */ + +#if !defined(__GNUC__) && !defined(__clang__) +#define __attribute__(x) +#endif + +#define UNUSED __attribute__((unused)) +#define NORET __attribute__((noreturn)) +#define PRINTF(f,a) __attribute__((format(printf,(f),(a)))) +#define SCANF(f,a) __attribute__((format(scanf,(f),(a)))) +#define WUNRES __attribute__((warn_unused_result)) +#define MALLOC __attribute__((malloc)) WUNRES +#define NONNULL(...) __attribute__((nonnull(__VA_ARGS__))) +#define PURE __attribute__((pure)) + +/* + * Branch prediction macros. + * These serve to tell the compiler which of the branches is more likely. + */ + +#if !defined(__GNUC__) && !defined(__clang__) +#define likely(expr) (expr) +#define unlikely(expr) (expr) +#else +#define likely(expr) __builtin_expect((expr), 1) +#define unlikely(expr) __builtin_expect((expr), 0) +#endif + +#endif /* !ATTRIB_H */ + +/* vim: set noet ft=c: */ diff --git a/pcap/logpkt.c b/pcap/logpkt.c new file mode 100644 index 0000000..a8be86d --- /dev/null +++ b/pcap/logpkt.c @@ -0,0 +1,843 @@ +/*- + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2019, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "logpkt.h" + +#include "sys.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WITHOUT_MIRROR +#include +#endif /* !WITHOUT_MIRROR */ + +typedef struct __attribute__((packed)) { + uint32_t magic_number; /* magic number */ + uint16_t version_major; /* major version number */ + uint16_t version_minor; /* minor version number */ + uint32_t thiszone; /* GMT to local correction */ + uint32_t sigfigs; /* accuracy of timestamps */ + uint32_t snaplen; /* max length of captured packets, in octets */ + uint32_t network; /* data link type */ +} pcap_file_hdr_t; + +typedef struct __attribute__((packed)) { + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ +} pcap_rec_hdr_t; + +#define PCAP_MAGIC 0xa1b2c3d4 + +typedef struct __attribute__((packed)) { + uint8_t dst_mac[ETHER_ADDR_LEN]; + uint8_t src_mac[ETHER_ADDR_LEN]; + uint16_t ethertype; +} ether_hdr_t; + +#ifndef ETHERTYPE_IP +#define ETHERTYPE_IP 0x0800 +#endif +#ifndef ETHERTYPE_IPV6 +#define ETHERTYPE_IPV6 0x86dd +#endif + +typedef struct __attribute__((packed)) { + uint8_t version_ihl; + uint8_t dscp_ecn; + uint16_t len; + uint16_t id; + uint16_t frag; + uint8_t ttl; + uint8_t proto; + uint16_t chksum; + uint32_t src_addr; + uint32_t dst_addr; +} ip4_hdr_t; + +typedef struct __attribute__((packed)) { + uint32_t flags; + uint16_t len; + uint8_t next_hdr; + uint8_t hop_limit; + uint8_t src_addr[16]; + uint8_t dst_addr[16]; +} ip6_hdr_t; + +typedef struct __attribute__((packed)) { + uint16_t src_port; + uint16_t dst_port; + uint32_t seq; + uint32_t ack; + uint16_t flags; + uint16_t win; + uint16_t chksum; + uint16_t urgp; +} tcp_hdr_t; + +#ifndef TH_FIN +#define TH_FIN 0x01 +#endif +#ifndef TH_SYN +#define TH_SYN 0x02 +#endif +#ifndef TH_RST +#define TH_RST 0x04 +#endif +#ifndef TH_PUSH +#define TH_PUSH 0x08 +#endif +#ifndef TH_ACK +#define TH_ACK 0x10 +#endif + +/* + * *MTU* is the size of the largest layer 3 packet, including IP header. + * + * *MAX_PKTSZ* is the buffer size needed to construct a layer 2 frame + * containing the largest possible layer 3 packet allowed by MTU. + * + * *MSS_IP4* and *MSS_IP6* are the maximum TCP segment sizes that fit into a + * single IPv4 and IPv6 packet, respectively. + * + * The calculations assume no IPv4 options and no IPv6 option headers. + * + * These constants are only used for PCAP writing, not for mirroring. + */ +//#define MTU 1500 +#define MTU 65535//we add support for jumboframes and offload +#define MAX_PKTSZ (MTU + sizeof(ether_hdr_t)) +#define MSS_IP4 (MTU - sizeof(ip4_hdr_t) - sizeof(tcp_hdr_t)) +#define MSS_IP6 (MTU - sizeof(ip6_hdr_t) - sizeof(tcp_hdr_t)) + +/* + * IP/TCP checksumming operating on uint32_t intermediate state variable C. + */ +#define CHKSUM_INIT(C) \ + { \ + (C) = 0; \ + } +#define CHKSUM_ADD_RANGE(C,B,S) \ + { \ + uint16_t *p = (uint16_t *)(B); \ + size_t words = (S) >> 1; \ + while (words--) { \ + (C) += *p++; \ + } \ + if ((S) & 1) { \ + (C) += htons(*((char *)p) << 8); \ + } \ + } +#define CHKSUM_ADD_UINT32(C,U) \ + { \ + (C) += ((U) >> 16) + ((U) & 0xFFFF); \ + } +#define CHKSUM_ADD_UINT16(C,U) \ + { \ + (C) += (U); \ + } +#define CHKSUM_FINALIZE(C) \ + { \ + (C) = ((C) >> 16) + ((C) & 0xffff); \ + (C) += ((C) >> 16); \ + (C) = ~(C); \ + } + +/* Socket address typecasting shorthand notations. */ +#define CSA(X) ((const struct sockaddr *)(X)) +#define CSIN(X) ((const struct sockaddr_in *)(X)) +#define CSIN6(X) ((const struct sockaddr_in6 *)(X)) + +/* + * Write the PCAP file-level header to file descriptor *fd* open for writing, + * positioned at the beginning of an empty file. + * + * Returns 0 on success and -1 on failure. + */ +static int +logpkt_write_global_pcap_hdr(int fd) +{ + pcap_file_hdr_t hdr; + + memset(&hdr, 0x0, sizeof(hdr)); + hdr.magic_number = PCAP_MAGIC; + hdr.version_major = 2; + hdr.version_minor = 4; + hdr.snaplen = MAX_PKTSZ; + hdr.network = 1; + return write(fd, &hdr, sizeof(hdr)) != sizeof(hdr) ? -1 : 0; +} + +/* + * Called on a file descriptor open for reading and writing. + * If the fd points to an empty file, a pcap header is added and 0 is returned. + * If the fd points to a file with PCAP magic bytes, the file position is moved + * to the end of the file and 0 is returned. + * If the fd points to a file without PCAP magic bytes, the file is truncated + * to zero bytes and a new PCAP header is written. + * On a return value of 0, the caller can continue to write PCAP records to the + * file descriptor. On error, -1 is returned and the file descriptor is in an + * undefined but still open state. + */ +int +logpkt_pcap_open_fd(int fd) { + pcap_file_hdr_t hdr; + off_t sz; + ssize_t n; + + sz = lseek(fd, 0, SEEK_END); + if (sz == -1) + return -1; + + if (sz > 0) { + if (lseek(fd, 0, SEEK_SET) == -1) + return -1; + n = read(fd, &hdr, sizeof(pcap_file_hdr_t)); + if (n != sizeof(pcap_file_hdr_t)) + return -1; + if (hdr.magic_number == PCAP_MAGIC) + return lseek(fd, 0, SEEK_END) == -1 ? -1 : 0; + if (lseek(fd, 0, SEEK_SET) == -1) + return -1; + if (ftruncate(fd, 0) == -1) + return -1; + } + + return logpkt_write_global_pcap_hdr(fd); +} + +/* + * Initialize the per-connection packet crafting context. For mirroring, + * *libnet* must be an initialized libnet instance and *mtu* must be the + * target interface MTU greater than 0. For PCAP writing, *libnet* must be + * NULL and *mtu* must be 0. The ether and sockaddr addresses are used as the + * layer 2 and layer 3 addresses respectively. For mirroring, the ethers must + * match the actual link layer addresses to be used when sending traffic, not + * some emulated addresses. + */ +void +logpkt_ctx_init(logpkt_ctx_t *ctx, libnet_t *libnet, size_t mtu, + const uint8_t *src_ether, const uint8_t *dst_ether, + const struct sockaddr *src_addr, socklen_t src_addr_len, + const struct sockaddr *dst_addr, socklen_t dst_addr_len) +{ + ctx->libnet = libnet; + memcpy(ctx->src_ether, src_ether, ETHER_ADDR_LEN); + memcpy(ctx->dst_ether, dst_ether, ETHER_ADDR_LEN); + memcpy(&ctx->src_addr, src_addr, src_addr_len); + memcpy(&ctx->dst_addr, dst_addr, dst_addr_len); + ctx->src_seq = 0; + ctx->dst_seq = 0; + if (mtu) { + ctx->mss = mtu - sizeof(tcp_hdr_t) + - (dst_addr->sa_family == AF_INET + ? sizeof(ip4_hdr_t) + : sizeof(ip6_hdr_t)); + } else { + ctx->mss = dst_addr->sa_family == AF_INET ? MSS_IP4 : MSS_IP6; + } +} + +/* + * Write the layer 2 frame contained in *pkt* to file descriptor *fd* already + * open for writing. First writes a PCAP record header, then the actual frame. + */ +static int +logpkt_pcap_write(const uint8_t *pkt, size_t pktsz, int fd) +{ + pcap_rec_hdr_t rec_hdr; + struct timeval tv; + + gettimeofday(&tv, NULL); + rec_hdr.ts_sec = tv.tv_sec; + rec_hdr.ts_usec = tv.tv_usec; + rec_hdr.orig_len = rec_hdr.incl_len = pktsz; + + if (write(fd, &rec_hdr, sizeof(rec_hdr)) != sizeof(rec_hdr)) { + printf("Error writing pcap record hdr: %s\n", + strerror(errno)); + return -1; + } + if (write(fd, pkt, pktsz) != (ssize_t)pktsz) { + printf("Error writing pcap record: %s\n", + strerror(errno)); + return -1; + } + return 0; +} + +/* + * Build a frame from the given layer 2, layer 3 and layer 4 parameters plus + * payload, write the resulting bytes into buffer pointed to by *pkt*, and fix + * the checksums on all layers. The receiving buffer must be at least + * MAX_PKTSZ bytes large and payload must be a maximum of MSS_IP4 or MSS_IP6 + * respectively. Layer 2 is Ethernet II, layer 3 is IPv4 or IPv6 depending on + * the address family of *dst_addr*, and layer 4 is TCP. + * + * This function is stateless. For header fields that cannot be directly + * derived from the arguments, default values will be used. + */ +static size_t +logpkt_pcap_build(uint8_t *pkt, + uint8_t *src_ether, uint8_t *dst_ether, + const struct sockaddr *src_addr, + const struct sockaddr *dst_addr, + char flags, uint32_t seq, uint32_t ack, + const uint8_t *payload, size_t payloadlen) +{ + ether_hdr_t *ether_hdr; + ip4_hdr_t *ip4_hdr; + ip6_hdr_t *ip6_hdr; + tcp_hdr_t *tcp_hdr; + size_t sz; + uint32_t sum; + + ether_hdr = (ether_hdr_t *)pkt; + memcpy(ether_hdr->src_mac, src_ether, sizeof(ether_hdr->src_mac)); + memcpy(ether_hdr->dst_mac, dst_ether, sizeof(ether_hdr->dst_mac)); + sz = sizeof(ether_hdr_t); + + if (dst_addr->sa_family == AF_INET) { + ether_hdr->ethertype = htons(ETHERTYPE_IP); + ip4_hdr = (ip4_hdr_t *)(((uint8_t *)ether_hdr) + + sizeof(ether_hdr_t)); + ip4_hdr->version_ihl = 0x45; /* version 4, ihl 5 words */ + ip4_hdr->dscp_ecn = 0; + ip4_hdr->len = htons(sizeof(ip4_hdr_t) + + sizeof(tcp_hdr_t) + payloadlen); + ip4_hdr->id = sys_rand16(), + ip4_hdr->frag = 0; + ip4_hdr->ttl = 64; + ip4_hdr->proto = IPPROTO_TCP; + ip4_hdr->src_addr = CSIN(src_addr)->sin_addr.s_addr; + ip4_hdr->dst_addr = CSIN(dst_addr)->sin_addr.s_addr; + ip4_hdr->chksum = 0; + CHKSUM_INIT(sum); + CHKSUM_ADD_RANGE(sum, ip4_hdr, sizeof(ip4_hdr_t)); + CHKSUM_FINALIZE(sum); + ip4_hdr->chksum = sum; + sz += sizeof(ip4_hdr_t); + tcp_hdr = (tcp_hdr_t *)(((uint8_t *)ip4_hdr) + + sizeof(ip4_hdr_t)); + tcp_hdr->src_port = CSIN(src_addr)->sin_port; + tcp_hdr->dst_port = CSIN(dst_addr)->sin_port; + /* pseudo header */ + CHKSUM_INIT(sum); + CHKSUM_ADD_UINT32(sum, ip4_hdr->src_addr); + CHKSUM_ADD_UINT32(sum, ip4_hdr->dst_addr); + CHKSUM_ADD_UINT16(sum, htons(ip4_hdr->proto)); + CHKSUM_ADD_UINT16(sum, htons(sizeof(tcp_hdr_t) + payloadlen)); + } else { + ether_hdr->ethertype = htons(ETHERTYPE_IPV6); + ip6_hdr = (ip6_hdr_t *)(((uint8_t *)ether_hdr) + + sizeof(ether_hdr_t)); + ip6_hdr->flags = htonl(0x60000000UL); /* version 6 */ + ip6_hdr->len = htons(sizeof(tcp_hdr_t) + payloadlen); + ip6_hdr->next_hdr = IPPROTO_TCP; + ip6_hdr->hop_limit = 255; + memcpy(ip6_hdr->src_addr, CSIN6(src_addr)->sin6_addr.s6_addr, + sizeof(ip6_hdr->src_addr)); + memcpy(ip6_hdr->dst_addr, CSIN6(dst_addr)->sin6_addr.s6_addr, + sizeof(ip6_hdr->dst_addr)); + sz += sizeof(ip6_hdr_t); + tcp_hdr = (tcp_hdr_t *)(((uint8_t *)ip6_hdr) + + sizeof(ip6_hdr_t)); + tcp_hdr->src_port = CSIN6(src_addr)->sin6_port; + tcp_hdr->dst_port = CSIN6(dst_addr)->sin6_port; + /* pseudo header */ + CHKSUM_INIT(sum); + CHKSUM_ADD_RANGE(sum, ip6_hdr->src_addr, + sizeof(ip6_hdr->src_addr)); + CHKSUM_ADD_RANGE(sum, ip6_hdr->dst_addr, + sizeof(ip6_hdr->dst_addr)); + CHKSUM_ADD_UINT32(sum, ip6_hdr->len); + CHKSUM_ADD_UINT16(sum, htons(IPPROTO_TCP)); + } + tcp_hdr->seq = htonl(seq); + tcp_hdr->ack = htonl(ack); + tcp_hdr->flags = htons(0x5000|flags); /* data offset 5 words */ + tcp_hdr->win = htons(32767); + tcp_hdr->urgp = 0; + tcp_hdr->chksum = 0; + sz += sizeof(tcp_hdr_t); + memcpy(((uint8_t *)tcp_hdr) + sizeof(tcp_hdr_t), payload, payloadlen); + CHKSUM_ADD_RANGE(sum, tcp_hdr, sizeof(tcp_hdr_t) + payloadlen); + CHKSUM_FINALIZE(sum); + tcp_hdr->chksum = sum; + return sz + payloadlen; +} + +#ifndef WITHOUT_MIRROR +/* + * Build a packet using libnet intended for mirroring mode. The packet will + * be dynamically allocated on the heap by the libnet instance *libnet*. + */ +static int +logpkt_mirror_build(libnet_t *libnet, + uint8_t *src_ether, uint8_t *dst_ether, + const struct sockaddr *src_addr, + const struct sockaddr *dst_addr, + char flags, uint32_t seq, uint32_t ack, + const uint8_t *payload, size_t payloadlen) +{ + libnet_ptag_t ptag; + + ptag = libnet_build_tcp(htons(src_addr->sa_family == AF_INET + ? CSIN(src_addr)->sin_port + : CSIN6(src_addr)->sin6_port), + htons(dst_addr->sa_family == AF_INET + ? CSIN(dst_addr)->sin_port + : CSIN6(dst_addr)->sin6_port), + seq, + ack, + flags, + 32767, /* window size */ + 0, /* checksum */ + 0, /* urgent pointer */ + LIBNET_TCP_H + payloadlen, + (uint8_t *)payload, payloadlen, + libnet, 0); + if (ptag == -1) { + printf("Error building tcp header: %s", + libnet_geterror(libnet)); + return -1; + } + + if (dst_addr->sa_family == AF_INET) { + ptag = libnet_build_ipv4(LIBNET_IPV4_H + LIBNET_TCP_H + + payloadlen, + 0, /* TOS */ + (uint16_t) + sys_rand16(), /* id */ + 0x4000, /* frag */ + 64, /* TTL */ + IPPROTO_TCP, /* protocol */ + 0, /* checksum */ + CSIN(src_addr)->sin_addr.s_addr, + CSIN(dst_addr)->sin_addr.s_addr, + NULL, 0, + libnet, 0); + } else { + ptag = libnet_build_ipv6(0, /* traffic class */ + 0, /* flow label */ + LIBNET_IPV6_H + LIBNET_TCP_H + + payloadlen, + IPPROTO_TCP, + 255, /* hop limit */ + *(struct libnet_in6_addr *) + &CSIN6(src_addr)->sin6_addr, + *(struct libnet_in6_addr *) + &CSIN6(dst_addr)->sin6_addr, + NULL, 0, + libnet, 0); + } + if (ptag == -1) { + printf("Error building ip header: %s", + libnet_geterror(libnet)); + return -1; + } + + ptag = libnet_build_ethernet(dst_ether, + src_ether, + dst_addr->sa_family == AF_INET + ? ETHERTYPE_IP : ETHERTYPE_IPV6, + NULL, 0, + libnet, 0); + if (ptag == -1) { + printf("Error building ethernet header: %s", + libnet_geterror(libnet)); + return -1; + } + return 0; +} +#endif /* !WITHOUT_MIRROR */ + +/* + * Write a single packet to either PCAP (*fd* != -1) or a network interface + * (*fd* == -1). Caller must ensure that *ctx* was initialized accordingly. + * The packet will be in direction *direction*, use TCP flags *flags*, and + * transmit a payload *payload*. TCP sequence and acknowledgement numbers as + * well as source and destination identifiers are taken from *ctx*. + * + * Caller must ensure that *payload* fits into a frame depending on the MTU + * selected (interface in mirroring mode, MTU value in PCAP writing mode). + */ +static int +logpkt_write_packet(logpkt_ctx_t *ctx, int fd, int direction, char flags, + const uint8_t *payload, size_t payloadlen) +{ + int rv; + + if (fd != -1) { + uint8_t buf[MAX_PKTSZ]; + size_t sz; + if (direction == LOGPKT_REQUEST) { + sz = logpkt_pcap_build(buf, + ctx->src_ether, ctx->dst_ether, + CSA(&ctx->src_addr), + CSA(&ctx->dst_addr), + flags, + ctx->src_seq, ctx->dst_seq, + payload, payloadlen); + } else { + sz = logpkt_pcap_build(buf, + ctx->dst_ether, ctx->src_ether, + CSA(&ctx->dst_addr), + CSA(&ctx->src_addr), + flags, + ctx->dst_seq, ctx->src_seq, + payload, payloadlen); + } + rv = logpkt_pcap_write(buf, sz, fd); + if (rv == -1) { + printf("Error writing packet to PCAP file\n"); + return -1; + } + } else { +#ifndef WITHOUT_MIRROR + /* Source and destination ether are determined by the actual + * local MAC address and target MAC address for mirroring the + * packets to; use them as-is for both directions. */ + if (direction == LOGPKT_REQUEST) { + rv = logpkt_mirror_build(ctx->libnet, + ctx->src_ether, ctx->dst_ether, + CSA(&ctx->src_addr), + CSA(&ctx->dst_addr), + flags, + ctx->src_seq, ctx->dst_seq, + payload, payloadlen); + } else { + rv = logpkt_mirror_build(ctx->libnet, + ctx->src_ether, ctx->dst_ether, + CSA(&ctx->dst_addr), + CSA(&ctx->src_addr), + flags, + ctx->dst_seq, ctx->src_seq, + payload, payloadlen); + } + if (rv == -1) { + printf("Error building packet\n"); + return -1; + } + rv = libnet_write(ctx->libnet); + if (rv == -1) { + printf("Error writing packet: %s\n", + libnet_geterror(ctx->libnet)); + } + libnet_clear_packet(ctx->libnet); +#else /* WITHOUT_MIRROR */ + rv = -1; +#endif /* WITHOUT_MIRROR */ + } + return rv; +} + +/* + * Emulate the initial SYN handshake. + */ +static int +logpkt_write_syn_handshake(logpkt_ctx_t *ctx, int fd) +{ + ctx->src_seq = sys_rand32(); + if (logpkt_write_packet(ctx, fd, LOGPKT_REQUEST, + TH_SYN, NULL, 0) == -1) + return -1; + ctx->src_seq += 1; + ctx->dst_seq = sys_rand32(); + if (logpkt_write_packet(ctx, fd, LOGPKT_RESPONSE, + TH_SYN|TH_ACK, NULL, 0) == -1) + return -1; + ctx->dst_seq += 1; + if (logpkt_write_packet(ctx, fd, LOGPKT_REQUEST, + TH_ACK, NULL, 0) == -1) + return -1; + return 0; +} + +/* + * Emulate the necessary packets to write a single payload segment. If + * necessary, a SYN handshake will automatically be generated before emitting + * the packet carrying the payload plus a matching ACK. + */ +int +logpkt_write_payload(logpkt_ctx_t *ctx, int fd, int direction, + const uint8_t *payload, size_t payloadlen) +{ + int other_direction = (direction == LOGPKT_REQUEST) ? LOGPKT_RESPONSE + : LOGPKT_REQUEST; + + if (ctx->src_seq == 0) { + if (logpkt_write_syn_handshake(ctx, fd) == -1) + return -1; + } + + while (payloadlen > 0) { + size_t n = payloadlen > ctx->mss ? ctx->mss : payloadlen; + if (logpkt_write_packet(ctx, fd, direction, + TH_PUSH|TH_ACK, payload, n) == -1) { + printf("Warning: Failed to write to pcap log" + ": %s\n", strerror(errno)); + return -1; + } + if (direction == LOGPKT_REQUEST) { + ctx->src_seq += n; + } else { + ctx->dst_seq += n; + } + payload += n; + payloadlen -= n; + } + + if (logpkt_write_packet(ctx, fd, other_direction, + TH_ACK, NULL, 0) == -1) { + printf("Warning: Failed to write to pcap log: %s\n", + strerror(errno)); + return -1; + } + return 0; +} + +/* + * Emulate a connection close, emitting a FIN handshake in the correct + * direction. Does not close the file descriptor. + */ +int +logpkt_write_close(logpkt_ctx_t *ctx, int fd, int direction) { + int other_direction = (direction == LOGPKT_REQUEST) ? LOGPKT_RESPONSE + : LOGPKT_REQUEST; + + if (ctx->src_seq == 0) { + if (logpkt_write_syn_handshake(ctx, fd) == -1) + return -1; + } + + if (logpkt_write_packet(ctx, fd, direction, + TH_FIN|TH_ACK, NULL, 0) == -1) { + printf("Warning: Failed to write packet\n"); + return -1; + } + if (direction == LOGPKT_REQUEST) { + ctx->src_seq += 1; + } else { + ctx->dst_seq += 1; + } + + if (logpkt_write_packet(ctx, fd, other_direction, + TH_FIN|TH_ACK, NULL, 0) == -1) { + printf("Warning: Failed to write packet\n"); + return -1; + } + if (other_direction == LOGPKT_REQUEST) { + ctx->src_seq += 1; + } else { + ctx->dst_seq += 1; + } + + if (logpkt_write_packet(ctx, fd, direction, + TH_ACK, NULL, 0) == -1) { + printf("Warning: Failed to write packet\n"); + return -1; + } + + return 0; +} + +#ifndef WITHOUT_MIRROR +typedef struct { + uint32_t ip; + int result; + uint8_t ether[ETHER_ADDR_LEN]; +} logpkt_recv_arp_reply_ctx_t; + +/* + * Receive a single ARP reply and copy the resulting ether to ctx->ether. + */ +static void +logpkt_recv_arp_reply(uint8_t *user, + UNUSED const struct pcap_pkthdr *h, + const uint8_t *packet) +{ + logpkt_recv_arp_reply_ctx_t *ctx = (logpkt_recv_arp_reply_ctx_t*)user; + struct libnet_802_3_hdr *heth = (void*)packet; + struct libnet_arp_hdr *harp = (void*)((char*)heth + LIBNET_ETH_H); + + /* skip if wrong protocol */ + if (htons(harp->ar_op) != ARPOP_REPLY) + return; + if (htons(harp->ar_pro) != ETHERTYPE_IP) + return; + if (htons(harp->ar_hrd) != ARPHRD_ETHER) + return; + + /* skip if wrong target IP address */ + if (!!memcmp(&ctx->ip, (char*)harp + harp->ar_hln + LIBNET_ARP_H, 4)) + return; + + /* skip if source ether mismatch */ + if (!!memcmp((u_char*)harp + sizeof(struct libnet_arp_hdr), + heth->_802_3_shost, ETHER_ADDR_LEN)) + return; + + memcpy(ctx->ether, + (u_char*)harp + sizeof(struct libnet_arp_hdr), + ETHER_ADDR_LEN); + ctx->result = 0; +} + +/* + * Look up the appropriate source and destination ethernet addresses for + * mirroring packets to dst_ip_s on interface dst_if_s. + * Only IPv4 mirror targets are supported. + */ +int +logpkt_ether_lookup(libnet_t *libnet, + uint8_t *src_ether, uint8_t *dst_ether, + const char *dst_ip_s, const char *dst_if_s) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + uint8_t broadcast_ether[ETHER_ADDR_LEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + uint8_t zero_ether[ETHER_ADDR_LEN] = { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + struct libnet_ether_addr *src_ether_addr; + uint32_t src_ip; + struct bpf_program bp; + int count = 50; + logpkt_recv_arp_reply_ctx_t ctx; + + if (sys_get_af(dst_ip_s) != AF_INET) { + printf("Mirroring target must be an IPv4 address.\n"); + return -1; + } + + ctx.result = -1; + ctx.ip = libnet_name2addr4(libnet, (char *)dst_ip_s, + LIBNET_DONT_RESOLVE); + if (ctx.ip == (uint32_t)-1) { + printf("Error converting dst IP address: %s\n", + libnet_geterror(libnet)); + goto out; + } + src_ip = libnet_get_ipaddr4(libnet); + if (src_ip == (uint32_t)-1) { + printf("Error getting src IP address: %s\n", + libnet_geterror(libnet)); + goto out; + } + src_ether_addr = libnet_get_hwaddr(libnet); + if (src_ether_addr == NULL) { + printf("Error getting src ethernet address: %s\n", + libnet_geterror(libnet)); + goto out; + } + memcpy(src_ether, src_ether_addr->ether_addr_octet, ETHER_ADDR_LEN); + + if (libnet_autobuild_arp(ARPOP_REQUEST, + src_ether, + (uint8_t*)&src_ip, + zero_ether, + (uint8_t*)&ctx.ip, + libnet) == -1) { + printf("Error building arp header: %s\n", + libnet_geterror(libnet)); + goto out; + } + + if (libnet_autobuild_ethernet(broadcast_ether, + ETHERTYPE_ARP, + libnet) == -1) { + printf("Error building ethernet header: %s", + libnet_geterror(libnet)); + goto out; + } + + pcap_t *pcap = pcap_open_live(dst_if_s, 100, 0, 10, errbuf); + if (pcap == NULL) { + printf("Error in pcap_open_live(): %s\n", errbuf); + goto out; + } + + if (pcap_compile(pcap, &bp, "arp", 0, -1) == -1) { + printf("Error in pcap_compile(): %s\n", + pcap_geterr(pcap)); + goto out2; + } + if (pcap_setfilter(pcap, &bp) == -1) { + printf("Error in pcap_setfilter(): %s\n", + pcap_geterr(pcap)); + goto out3; + } + + do { + if (libnet_write(libnet) != -1) { + /* Limit # of packets to process, so we can loop to + * send arp requests on busy networks. */ + if (pcap_dispatch(pcap, 1000, + (pcap_handler)logpkt_recv_arp_reply, + (u_char*)&ctx) < 0) { + printf("Error in pcap_dispatch(): %s\n", + pcap_geterr(pcap)); + break; + } + } else { + printf("Error writing arp packet: %s", + libnet_geterror(libnet)); + break; + } + sleep(1); + } while (ctx.result == -1 && --count > 0); + + if (ctx.result == 0) { + memcpy(dst_ether, &ctx.ether, ETHER_ADDR_LEN); + //log_dbg_printf("Mirror target is up: " + // "%02x:%02x:%02x:%02x:%02x:%02x\n", + // dst_ether[0], dst_ether[1], dst_ether[2], + // dst_ether[3], dst_ether[4], dst_ether[5]); + } + +out3: + pcap_freecode(&bp); +out2: + pcap_close(pcap); +out: + libnet_clear_packet(libnet); + return ctx.result; +} +#endif /* !WITHOUT_MIRROR */ + +/* vim: set noet ft=c: */ diff --git a/pcap/logpkt.h b/pcap/logpkt.h new file mode 100644 index 0000000..7545c2b --- /dev/null +++ b/pcap/logpkt.h @@ -0,0 +1,71 @@ +/*- + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2019, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LOGPKT_H +#define LOGPKT_H + +#include "attrib.h" + +#include +#include +#include + + +#ifndef WITHOUT_MIRROR +#include +#else /* WITHOUT_MIRROR */ +#define libnet_t void +#define ETHER_ADDR_LEN 6 +#endif /* WITHOUT_MIRROR */ + +typedef struct { + libnet_t *libnet; + uint8_t src_ether[ETHER_ADDR_LEN]; + uint8_t dst_ether[ETHER_ADDR_LEN]; + struct sockaddr_storage src_addr; + struct sockaddr_storage dst_addr; + uint32_t src_seq; + uint32_t dst_seq; + size_t mss; +} logpkt_ctx_t; + +#define LOGPKT_REQUEST 0 +#define LOGPKT_RESPONSE 1 + +int logpkt_pcap_open_fd(int fd) WUNRES; +void logpkt_ctx_init(logpkt_ctx_t *, libnet_t *, size_t, + const uint8_t *, const uint8_t *, + const struct sockaddr *, socklen_t, + const struct sockaddr *, socklen_t); +int logpkt_write_payload(logpkt_ctx_t *, int, int, + const unsigned char *, size_t) WUNRES; +int logpkt_write_close(logpkt_ctx_t *, int, int); +int logpkt_ether_lookup(libnet_t *, uint8_t *, uint8_t *, + const char *, const char *) WUNRES; + +#endif /* !LOGPKT_H */ diff --git a/pcap/pcap_logger.c b/pcap/pcap_logger.c new file mode 100644 index 0000000..7649c07 --- /dev/null +++ b/pcap/pcap_logger.c @@ -0,0 +1,155 @@ + +#include +#include +#include +#include +#include +#include + +#include "network.h" +#include "proto_mod.h" +#include "debug.h" + +#include "pcap_logger.h" +#include "logpkt.h" + +#define DFLT_FILEMODE 0666 + +static int init_pcap_logger PROTO_LIST((void * data)); +static int deinit_pcap_logger PROTO_LIST(()); +static int create_pcap_logger PROTO_LIST((proto_obj **objp, struct in_addr *i_addr, + u_short i_port,struct in_addr *r_addr, u_short r_port, struct timeval *base_time)); +static int destroy_pcap_logger PROTO_LIST((proto_obj **objp)); +static int data_pcap_logger PROTO_LIST((proto_obj *_obj, unsigned char *data,unsigned int len, int dir)); +static int close_pcap_logger PROTO_LIST((proto_obj *_obj, unsigned char *data,unsigned int len, int dir)); + +int pcap_fd = -1; +static uint8_t content_pcap_src_ether[ETHER_ADDR_LEN] = {0x02, 0x00, 0x00, 0x11, 0x11, 0x11}; +static uint8_t content_pcap_dst_ether[ETHER_ADDR_LEN] = {0x02, 0x00, 0x00, 0x22, 0x22, 0x22}; + +static int init_pcap_logger(data) + void *data; + { + char *pcap_outfile = (char *) data; + pcap_fd = open(pcap_outfile, O_RDWR|O_CREAT, DFLT_FILEMODE); + if (pcap_fd == -1) { + //printf("Failed to open pcap '%s' for writing\n", pcap_outfile); + return -1; + } + if (logpkt_pcap_open_fd(pcap_fd) == -1) { + //printf("Failed to prepare '%s' for PCAP writing\n", pcap_outfile); + close(pcap_fd); + pcap_fd = -1; + return -1; + } + return 0; + } + +static int deinit_pcap_logger() + { + fdatasync(pcap_fd); + close(pcap_fd); + return 0; + } + +static int create_pcap_logger(objp,i_addr,i_port,r_addr,r_port,base_time) + proto_obj **objp; + struct in_addr *i_addr; + u_short i_port; + struct in_addr *r_addr; + u_short r_port; + struct timeval *base_time; + { + int r,_status; + logpkt_ctx_t *pcap_obj=0; + struct sockaddr_in src_addr, dst_addr; + + if(!(pcap_obj=(logpkt_ctx_t *)calloc(1,sizeof(logpkt_ctx_t)))) + ABORT(R_NO_MEMORY); + + src_addr.sin_family = AF_INET; + src_addr.sin_port = htons(i_port); + src_addr.sin_addr = *i_addr; + + dst_addr.sin_family = AF_INET; + dst_addr.sin_port = htons(r_port); + dst_addr.sin_addr = *r_addr; + + logpkt_ctx_init(pcap_obj,NULL,0,content_pcap_src_ether, content_pcap_dst_ether, + (const struct sockaddr*)&src_addr, sizeof(src_addr), + (const struct sockaddr*)&dst_addr, sizeof(dst_addr)); + *objp=(proto_obj *)pcap_obj; + _status=0; + abort: + if(_status){ + destroy_pcap_logger((proto_obj **)&pcap_obj); + } + return(_status); + } + +static int destroy_pcap_logger(objp) + proto_obj **objp; + { + logpkt_ctx_t *pcap_obj; + + if(!objp || !*objp) + return(0); + + pcap_obj=(logpkt_ctx_t *)*objp; + + free(pcap_obj); + *objp=0; + + return(0); + } + +static int data_pcap_logger(_obj,data,len,dir) + proto_obj *_obj; + unsigned char *data; + unsigned int len; + int dir; + { + logpkt_ctx_t *pcap_obj = (logpkt_ctx_t *)_obj; + int direction; + int status; + + if (dir == DIR_I2R ) direction = LOGPKT_REQUEST; + else direction = LOGPKT_RESPONSE; + + status = logpkt_write_payload(pcap_obj,pcap_fd,direction,data,len); + + return status; + } + +int close_pcap_logger(_obj,data,len,dir) + proto_obj *_obj; + unsigned char *data; + unsigned int len; + int dir; + { + logpkt_ctx_t *pcap_obj = (logpkt_ctx_t *)_obj; + int direction; + int status; + + if (dir == DIR_I2R ) direction = LOGPKT_REQUEST; + else direction = LOGPKT_RESPONSE; + + status = logpkt_write_close(pcap_obj, pcap_fd, direction); + + return status; + } + +static struct logger_mod_vtbl_ pcap_vtbl ={ + init_pcap_logger, + deinit_pcap_logger, + create_pcap_logger, + destroy_pcap_logger, + data_pcap_logger, + close_pcap_logger, +}; + +struct logger_mod_ pcap_mod = { + "PCAP", + &pcap_vtbl +}; + diff --git a/pcap/pcap_logger.h b/pcap/pcap_logger.h new file mode 100644 index 0000000..8490ea3 --- /dev/null +++ b/pcap/pcap_logger.h @@ -0,0 +1,7 @@ +#ifndef _pcap_logger_h +#define _pcap_logger_h + +extern logger_mod pcap_mod; + +#endif + diff --git a/pcap/sys.c b/pcap/sys.c new file mode 100644 index 0000000..e5a9efb --- /dev/null +++ b/pcap/sys.c @@ -0,0 +1,98 @@ +/*- + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2019, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "sys.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Determine address family of addr + */ +int +sys_get_af(const char *addr) +{ + if (strstr(addr, ":")) + return AF_INET6; + else if (!strpbrk(addr, "abcdefghijklmnopqrstu" + "vwxyzABCDEFGHIJKLMNOP" + "QRSTUVWXYZ-")) + return AF_INET; + else + return AF_UNSPEC; +} + + +static int sys_rand_seeded = 0; + +static void +sys_rand_seed(void) { + struct timeval seed; + + if (gettimeofday(&seed, NULL) == -1) { + srandom((unsigned)time(NULL)); + } else { + srandom((unsigned)(seed.tv_sec ^ seed.tv_usec)); + } + sys_rand_seeded = 1; +} + +uint16_t +sys_rand16(void) { + if (unlikely(!sys_rand_seeded)) + sys_rand_seed(); + return random(); +} + +uint32_t +sys_rand32(void) { + if (unlikely(!sys_rand_seeded)) + sys_rand_seed(); + return random(); +} + +/* vim: set noet ft=c: */ + diff --git a/pcap/sys.h b/pcap/sys.h new file mode 100644 index 0000000..e21f54c --- /dev/null +++ b/pcap/sys.h @@ -0,0 +1,45 @@ +/*- + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2019, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SYS_H +#define SYS_H + +#include "attrib.h" + +#include +#include +#include + +int sys_get_af(const char *); + +uint16_t sys_rand16(void); +uint32_t sys_rand32(void); + +#endif /* !SYS_H */ + +/* vim: set noet ft=c: */ diff --git a/pcap/targets.mk b/pcap/targets.mk new file mode 100644 index 0000000..534d19f --- /dev/null +++ b/pcap/targets.mk @@ -0,0 +1,256 @@ +# +# targets.mk +# +# + + + +# +# CONFIGURE USER-DEFINED MAKE ENVIRONMENT +# +# These fields are specified by the user. The remainder of +# this file is generated from this user-specified information. +# +# PCAP_DEFINES: +# cpp defines, with the -D flag preceeding each +# +# PCAP_INCLUDES: +# cpp include directories, with the -I flag preceeding each +# +# PCAP_INTERNAL: +# headers files which are local to a specific module directory, +# and should not be used by other parts of the toolkit or by +# the user +# +# PCAP_LIBNAME: +# the library associated with this module directory, used in +# most cases for debugging purposes +# +# PCAP_LIBPATHS: +# link-time directories to search for libraries, with the -L flag +# preceeding each +# +# PCAP_LIBRARIES: +# link-time libraries, with the -l flag preceeding each +# +# PCAP_LOCALFLAGS: +# compile-time flags specific to compiling only the files in +# this module directory--this variable should only be set in +# extremely exceptional cases +# +# PCAP_MAKEFILES: +# the makefiles +# +# PCAP_PREFIX: +# defines the module name, which also serves as the +# prefix for all the variable names defined in this file +# +# PCAP_PRIVATE: +# the private, for-toolkit-use-only API header files +# +# PCAP_NULL_PROGRAMS: +# programs to build +# +# PCAP_PUBLIC: +# the header files that define the public API for the toolkit +# and any other 'public' files that should be copied to +# the build directory +# +# PCAP_SOURCES: +# the source files to compile to object +# +PCAP_DEFINES = -DWITHOUT_MIRROR +PCAP_INCLUDES = -I$(PCAP_SRCDIR) +PCAP_INTERNAL = +PCAP_LIBNAME = +PCAP_LIBPATHS = +PCAP_LIBRARIES = +PCAP_LOCALFLAGS = +PCAP_MAKEFILES = targets.mk +PCAP_PREFIX = PCAP +PCAP_PRIVATE = pcap_logger.h +PCAP_PROGRAMS = +PCAP_PUBLIC = +PCAP_SOURCES = sys.c logpkt.c pcap_logger.h + + + +# +# CONFIGURE AUTOMATICALLY-GENERATED MAKE ENVIRONMENT +# +# PCAP_OBJECTS: +# object files to build +# +# PCAP_UNUSED: +# obsolete files in the module directory that are not +# used during the build process +# +# PCAP_USED: +# all files in the module directory that are used +# during the build process +# +PCAP_OBJECTS = sys.$(OBJSUFFIX) logpkt.$(OBJSUFFIX) pcap_logger.$(OBJSUFFIX) +PCAP_UNUSED = +PCAP_USED = $(PCAP_INTERNAL:%=$(PCAP_SRCDIR)%) \ + $(PCAP_MAKEFILES:%=$(PCAP_SRCDIR)%) \ + $(PCAP_PRIVATE:%=$(PCAP_SRCDIR)%) \ + $(PCAP_PUBLIC:%=$(PCAP_SRCDIR)%) \ + $(PCAP_SOURCES:%=$(PCAP_SRCDIR)%) + + + +# +# NOTES +# +# The following variables may be used during the build process, +# but are not defined in this file. If they are to be set +# to something other than the default blank, then they must +# be set by the calling make system. +# +# PCAP_SRCDIR: +# if the build target directory is different from the +# module directory (the source directory), then this +# variable contains the relative or full path of +# the module directory +# +# LIBARS: +# the library archive files (with fully-specified paths) that +# executables built from this module directory depend upon +# +# LIBPATHS: +# the paths to search for library archives (specified with +# the -L) +# +# LIBRARIES: +# the libraries to use while building executables from +# this module directory (specified with the -l) +# + + + +# +# GLOBAL ENVIRONMENT +# +DEFINES += $(PCAP_DEFINES) +INCLUDES += $(PCAP_INCLUDES) +LIBPATHS += $(PCAP_LIBPATHS) +LIBRARIES += $(PCAP_LIBRARIES) +OBJECTS += $(PCAP_OBJECTS) +PUBLIC += $(PCAP_PUBLIC) + + + +# +# GENERIC DEPENDENCIES +# +# default: +# default dependency, must be the first dependency in this makefile +# +# all: +# build everything in this module directory +# +# build: +# make only the toolkit build files of this module directory +# +# ci: +# perform an RCS check-in of this module directory +# +# clean: +# remove the compiled files +# +# clean_public: +# remove the public header files that have been copied +# to a public build directory +# +# objects: +# build the object files (this dependency is used for +# building the toolkit library) +# +# private: +# build only the private API header files +# +# public: +# build only the public API header files +# +default: $(PCAP_LIBNAME) +default: $(PCAP_PROGRAMS) + +all: $(PCAP_PUBLIC) +all: $(PCAP_OBJECTS) +all: $(PCAP_LIBNAME) +all: $(PCAP_PROGRAMS) +build: $(PCAP_PUBLIC) +build: $(PCAP_OBJECTS) +ci: pcap_ci +clean: pcap_clean +clean_public: pcap_clean_public +objects: $(PCAP_OBJECTS) +private: $(PCAP_PRIVATE) +public: $(PCAP_PUBLIC) + + + +# +# LOCAL UTILITY DEPENDENCIES +# +# utility dependencies are necessary because of some +# make-isms having to do with dependencies +# + +pcap_ci: + $(CI) $(CIFLAGS) $(PCAP_USED) + +pcap_clean: + $(RM) $(RMFLAGS) $(PCAP_OBJECTS) $(PCAP_LIBNAME) $(PCAP_PROGRAMS) + +pcap_clean_public: + $(RM) $(RMFLAGS) $(PCAP_PUBLIC) + +pcap_objects: $(PCAP_OBJECTS) + +pcap_programs: $(PCAP_PROGRAMS) + +pcap_public: $(PCAP_PUBLIC) + + + +# +# BUILD DEPENDENCIES +# +# build dependencies invoke the rule used to build each +# class of file +# + +$(PCAP_LIBNAME): + $(AR) $(ARFLAGS) $@ $? + $(RANLIB) $@ + +$(PCAP_OBJECTS): + $(COMPILE.c) $(PCAP_SRCDIR)$(@:%.o=%.c) $(DEFINES) $(INCLUDES) $(PCAP_LOCALFLAGS) + +$(PCAP_PUBLIC): + $(CP) $(CPFLAGS) $(PCAP_SRCDIR)$@ $@ + +$(PCAP_PROGRAMS): + $(LINK.c) $@.$(OBJSUFFIX) $(LDLIBS) $(LIBS) $(LIBRARIES) $(LIBPATHS) +#LIBS above is obsolete (use LIBARARIES instead) + + + +# +# FILE DEPENDENCIES +# +# file dependencies state, for each file that is built, +# which file(s) it depends upon +# + +logpkt.$(OBJSUFFIX): $(PCAP_SRCDIR)logpkt.h +logpkt.$(OBJSUFFIX): $(PCAP_SRCDIR)logpkt.c + +sys.$(OBJSUFFIX): $(PCAP_SRCDIR)sys.h +sys.$(OBJSUFFIX): $(PCAP_SRCDIR)sys.c + +pcap_logger.$(OBJSUFFIX): $(PCAP_SRCDIR)pcap_logger.h +pcap_logger.$(OBJSUFFIX): $(PCAP_SRCDIR)pcap_logger.c + +$(PCAP_LIBNAME): $(PCAP_OBJECTS) diff --git a/ssl/ssl_analyze.c b/ssl/ssl_analyze.c index 5a6b7ea..4e2c9dc 100644 --- a/ssl/ssl_analyze.c +++ b/ssl/ssl_analyze.c @@ -273,6 +273,10 @@ static int create_ssl_analyzer(handle,ctx,conn,objp,i_addr,i_port,r_addr,r_port, *objp=(proto_obj *)obj; _status=0; + + //check logger... + if (logger) _status=logger->vtbl->create(&obj->logger_obj,i_addr,i_port,r_addr,r_port,base_time); + abort: if(_status){ destroy_ssl_analyzer((proto_obj **)&obj); @@ -291,6 +295,9 @@ static int destroy_ssl_analyzer(objp) obj=(ssl_obj *)*objp; DBG((0,"Destroying SSL analyzer")); + //check logger... + if (logger) logger->vtbl->destroy(&obj->logger_obj); + free_r_queue(obj->i2r_queue); free_r_queue(obj->r2i_queue); ssl_decoder_destroy(&obj->decoder); @@ -605,6 +612,9 @@ int close_ssl_analyzer(_obj,p,dir) else what="FIN"; + //check logger... + if (logger) logger->vtbl->close(ssl->logger_obj,NULL,0,dir); + explain(ssl,"%d ",ssl->conn->conn_number); ssl_print_timestamp(ssl,&p->ts); ssl_print_direction_indicator(ssl,dir); diff --git a/ssl/ssl_h.h b/ssl/ssl_h.h index 4a64ede..b3b5b0c 100644 --- a/ssl/ssl_h.h +++ b/ssl/ssl_h.h @@ -74,6 +74,7 @@ typedef struct ssl_extensions_ { typedef struct ssl_obj_ { tcp_conn *conn; + proto_obj *logger_obj; int r_state; int i_state; int version; diff --git a/ssl/sslprint.c b/ssl/sslprint.c index badb60c..11a4735 100644 --- a/ssl/sslprint.c +++ b/ssl/sslprint.c @@ -78,6 +78,8 @@ int process_beginning_plaintext(ssl,seg,direction) if(d.data[0]==0x16) return(SSL_BAD_CONTENT_TYPE); + if (logger) logger->vtbl->data(ssl->logger_obj,d.data,d.len,direction); + P_(P_AD){ ssl_print_timestamp(ssl,&seg->p->ts); @@ -274,6 +276,10 @@ int ssl_expand_record(ssl,q,direction,data,len) printf("\n"); } else{ + //try to save unencrypted data to logger + //we must save record with type "application_data" (this is unencrypted data) + 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)) { printf(" unknown record type: %d\n", ct); ERETURN(r);