diff --git a/.github/workflows/clang.yml b/.github/workflows/clang.yml index 9ebe978..85b3183 100644 --- a/.github/workflows/clang.yml +++ b/.github/workflows/clang.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v2 - name: Install dependencies run: | - sudo apt install clang autoconf libpcap-dev libssl-dev + sudo apt install clang autoconf libpcap-dev libssl-dev libnet1-dev - name: autogen run: ./autogen.sh - name: configure diff --git a/.github/workflows/gcc.yml b/.github/workflows/gcc.yml index 44a6f3a..b441140 100644 --- a/.github/workflows/gcc.yml +++ b/.github/workflows/gcc.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v2 - name: Install dependencies run: | - sudo apt install autoconf libpcap-dev libssl-dev + sudo apt install autoconf libpcap-dev libssl-dev libnet1-dev - name: autogen run: ./autogen.sh - name: configure diff --git a/.gitignore b/.gitignore index 1f30374..e394d18 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ ssl/.deps/ ssl/.dirstamp ssl/Makefile.in stamp-h1 +pcap/.deps/ +pcap/.dirstamp diff --git a/Makefile.am b/Makefile.am index df72dfa..52514cc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,20 +5,24 @@ ssldump_SOURCES = \ base/pcap-snoop.c\ base/network.c\ base/proto_mod.c\ - ssl/ssl_analyze.c\ + base/tcppack.c\ + base/tcpconn.c\ null/null_analyze.c\ common/lib/r_data.c\ common/lib/r_assoc.c\ common/lib/r_errors.c\ common/lib/debug.c\ - base/tcppack.c\ - base/tcpconn.c\ + ssl/ssl_analyze.c\ ssl/ssldecode.c\ ssl/sslprint.c\ ssl/ssl.enums.c\ ssl/sslxprint.c\ ssl/ciphersuites.c\ - ssl/ssl_rec.c + ssl/ssl_rec.c\ + pcap/logpkt.c\ + pcap/pcap_logger.c\ + pcap/sys.c + ssldump_CPPFLAGS = \ -I$(top_srcdir)\ @@ -27,6 +31,7 @@ ssldump_CPPFLAGS = \ -I$(top_srcdir)/null\ -I$(top_srcdir)/ssl\ -I$(top_srcdir)/base\ + -I$(top_srcdir)/pcap\ -D_DEFAULT_SOURCE=1\ -DLINUX\ -DOPENSSL diff --git a/README.md b/README.md index 86f9a5a..cc7f1ee 100644 --- a/README.md +++ b/README.md @@ -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 +apt install build-essential autoconf libssl-dev libpcap-dev libnet1-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 +dnf install autoconf automake gcc make openssl-devel libpcap-devel libnet1-dev ./autogen.sh ./configure --prefix=/usr/local make @@ -71,7 +71,12 @@ Configuration examples: ./configure CC=/usr/bin/clang --enable-asan --disable-optimization ``` -## Contributing +# Notes + +The "save to pcap" (-w) option by @ryabkov, is heavily based on the work of +@droe on https://github.com/droe/sslsplit . + +# Contributing The contributing policy is simple. If you have a patch to propose, make a pull-request via the interface. If the patch works for me, it's merged. diff --git a/base/pcap-snoop.c b/base/pcap-snoop.c index 73e1ef8..f89bf37 100644 --- a/base/pcap-snoop.c +++ b/base/pcap-snoop.c @@ -75,6 +75,7 @@ #ifdef ENABLE_RECORD #include "record_analyze.h" #endif +#include "pcap_logger.h" #ifndef ETHERTYPE_8021Q # define ETHERTYPE_8021Q 0x8100 @@ -96,6 +97,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; @@ -106,7 +109,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); @@ -126,6 +129,7 @@ int print_version() void sig_handler(int sig) { 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; @@ -479,6 +491,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/configure.ac b/configure.ac index b1625a1..3e4ebf0 100644 --- a/configure.ac +++ b/configure.ac @@ -73,6 +73,27 @@ files with ]) fi +have_libnet=no +AC_SEARCH_LIBS([libnet_init], [net], [have_libnet=yes]) + +if test "x${have_libnet}" = xyes; then + AC_CHECK_HEADERS([libnet.h], [], [have_libnet=no]) +fi + +if test "x${have_libnet}" = xno; then + AC_MSG_ERROR([ +--------------------------------------- +Unable to find libnet on this system +Check 'config.log' for more information + +On Debian and Ubuntu systems you can +install the required library and header +files with + apt install libnet1-dev +--------------------------------------- + ]) +fi + AC_ARG_ENABLE([optimization], [ --disable-optimization disable compiler optimizations], [optimization=${enableval}], [optimization=yes]) @@ -118,6 +139,7 @@ AC_CONFIG_FILES([Makefile common/lib/Makefile null/Makefile ssl/Makefile + pcap/Makefile base/Makefile]) AC_OUTPUT diff --git a/pcap/Makefile.am b/pcap/Makefile.am new file mode 100644 index 0000000..e69de29 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..647cba2 --- /dev/null +++ b/pcap/pcap_logger.c @@ -0,0 +1,149 @@ + +#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(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..3d8c33d --- /dev/null +++ b/pcap/sys.c @@ -0,0 +1,99 @@ +/*- + * 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 +#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/ssl/ssl_analyze.c b/ssl/ssl_analyze.c index e8a1d07..80f20e3 100644 --- a/ssl/ssl_analyze.c +++ b/ssl/ssl_analyze.c @@ -265,6 +265,10 @@ static int create_ssl_analyzer(void *handle, proto_ctx *ctx, tcp_conn *conn, *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); @@ -283,6 +287,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); @@ -595,6 +602,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 526fcf8..e622a19 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 2aec627..b5dc6bf 100644 --- a/ssl/sslprint.c +++ b/ssl/sslprint.c @@ -75,6 +75,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); @@ -269,6 +271,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); diff --git a/ssldump.1 b/ssldump.1 index a4a8e97..1e54dd8 100644 --- a/ssldump.1 +++ b/ssldump.1 @@ -85,6 +85,10 @@ ssldump \- dump SSL traffic on a network .B \-r .I dumpfile ] +[ +.B \-w +.I outputpcap +] .br .ti +8 [ @@ -243,6 +247,9 @@ Print the handshake type. Print handshake type and highlights. .RE .TP +.BI \-w " outputpcap" +Use \fIoutputpcap\fP as the destination for decrypted packets. +.TP \fIexpression\fP .RS Selects what packets ssldump will examine. Technically speaking,