Add GnuTLS support. Fixes #68

This commit is contained in:
Kaian 2015-10-22 19:02:47 +02:00
parent d320c4417b
commit 9b06b744f3
10 changed files with 1009 additions and 10 deletions

54
LICENSE.OpenSSL Normal file
View File

@ -0,0 +1,54 @@
/* ====================================================================
* Copyright (c) 1998-2011 The OpenSSL Project. 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.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
*
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* openssl-core@openssl.org.
*
* 5. Products derived from this software may not be called "OpenSSL"
* nor may "OpenSSL" appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED 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 OpenSSL PROJECT OR
* ITS 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.
* ====================================================================
*
* This product includes cryptographic software written by Eric Young
* (eay@cryptsoft.com). This product includes software written by Tim
* Hudson (tjh@cryptsoft.com).
*
*/

View File

@ -36,7 +36,8 @@ Prerequisites
- libncurse5 - for UI, windows, panels.
- libpcap - for capturing packets.
- libssl - (optional) for TLS transport decrypt
- libssl - (optional) for TLS transport decrypt using OpenSSL and libcrypt
- gnutls - (optional) for TLS transport decrypt using GnuTLS and libgcrypt
- libncursesw5 - (optional) for UI, windows, panels (wide-character support)
- libpcre - (optional) for Perl Compatible regular expressions
@ -51,7 +52,8 @@ You can pass following flags to ./configure to enable some features
| configure flag | Feature |
| ------------- | ------------- |
| `--with-gnutls` | Adds GnuTLS support to parse TLS captured messages (req. libssl) |
| `--with-openssl` | Adds OpenSSL support to parse TLS captured messages (req. libssl) |
| `--with-gnutls` | Adds GnuTLS support to parse TLS captured messages (req. gnutls) |
| `--with-pcre`| Adds Perl Compatible regular expressions support in regexp fields |
| `--enable-unicode` | Adds Ncurses UTF-8/Unicode support (req. libncursesw5) |
| `--enable-ipv6` | Enables IPv6 packet capture support. |

View File

@ -120,6 +120,31 @@ AS_IF([test "x$WITH_GNUTLS" == "xyes"], [
AC_DEFINE([WITH_GNUTLS],[],[Compile With GnuTLS compatibility])
], [])
####
#### OpenSSL Support
####
AC_ARG_WITH([openssl],
AS_HELP_STRING([--with-openssl], [Enable SSL Support (TLS SIP Transport)]),
[AC_SUBST(WITH_OPENSSL, $withval)],
[AC_SUBST(WITH_OPENSSL, no)]
)
AS_IF([test "x$WITH_OPENSSL" == "xyes"], [
AS_IF([test "x$WITH_GNUTLS" == "xyes"], [
AC_MSG_ERROR([ GnuTLS and OpenSSL can not be enabled at the same time ])
], [])
AC_CHECK_LIB([ssl], [SSL_new], [], [
AC_MSG_ERROR([ You need to have libssl installed to compile sngrep])
])
AC_CHECK_LIB([crypto], [EVP_get_cipherbyname], [], [
AC_MSG_ERROR([ You need to have libcrypto installed to compile sngrep])
])
AC_DEFINE([WITH_OPENSSL],[],[Compile With Openssl compatibility])
], [])
####
#### PCRE Support
####
@ -158,6 +183,7 @@ AS_IF([test "x$USE_IPV6" == "xyes"], [
# Conditional Source inclusion
AM_CONDITIONAL([WITH_GNUTLS], [test "x$WITH_GNUTLS" == "xyes"])
AM_CONDITIONAL([WITH_OPENSSL],[test "x$WITH_OPENSSL" == "xyes"])
######################################################################
@ -182,6 +208,7 @@ AC_MSG_NOTICE
AC_MSG_NOTICE( sngrep configure finished )
AC_MSG_NOTICE( ====================================================== )
AC_MSG_NOTICE( GnuTLS Support : ${WITH_GNUTLS} )
AC_MSG_NOTICE( OpenSSL Support : ${WITH_OPENSSL} )
AC_MSG_NOTICE( Unicode Support : ${UNICODE} )
AC_MSG_NOTICE( Perl Expressions Support : ${WITH_PCRE} )
AC_MSG_NOTICE( IPv6 Support : ${USE_IPV6} )

View File

@ -1,7 +1,10 @@
bin_PROGRAMS=sngrep
sngrep_SOURCES=capture.c capture_eep.c capture_reasm.c capture_ws.c
if WITH_GNUTLS
sngrep_SOURCES+=capture_tls.c
sngrep_SOURCES+=capture_gnutls.c
endif
if WITH_OPENSSL
sngrep_SOURCES+=capture_openssl.c
endif
sngrep_SOURCES+=sip.c sip_call.c sip_msg.c sip_attr.c main.c option.c
sngrep_SOURCES+=group.c filter.c keybinding.c media.c setting.c rtp.c util.c vector.c

View File

@ -37,7 +37,10 @@
#include "capture_reasm.h"
#include "capture_eep.h"
#ifdef WITH_GNUTLS
#include "capture_tls.h"
#include "capture_gnutls.h"
#endif
#ifdef WITH_OPENSSL
#include "capture_openssl.h"
#endif
#include "sip.h"
#include "rtp.h"
@ -300,7 +303,7 @@ parse_packet(u_char *info, const struct pcap_pkthdr *header, const u_char *packe
if (!(pkt = capture_packet_reasm_tcp(pkt, tcp, payload, size_payload)))
return;
#ifdef WITH_GNUTLS
#if defined(WITH_GNUTLS) || defined(WITH_OPENSSL)
// Check if packet is TLS
if (capture_cfg.keyfile)
tls_process_segment(pkt, tcp);

View File

@ -32,7 +32,7 @@
#include <unistd.h>
#include "capture.h"
#include "capture_tls.h"
#include "capture_gnutls.h"
#include "option.h"
#include "util.h"
#include "sip.h"

492
src/capture_openssl.c Normal file
View File

@ -0,0 +1,492 @@
/**************************************************************************
**
** sngrep - SIP Messages flow viewer
**
** Copyright (C) 2013,2014 Ivan Alonso (Kaian)
** Copyright (C) 2013,2014 Irontec SL. All rights reserved.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
****************************************************************************/
/**
* @file capture_tls.c
* @author Ivan Alonso [aka Kaian] <kaian@irontec.com>
*
* @brief Functions to manage SIP TLS transport for messages
*
* This file contains the functions and structures to manage the SIP messages
* that use TLS as transport.
*
*/
#include <unistd.h>
#include "capture.h"
#include "capture_openssl.h"
#include "option.h"
#include "util.h"
#include "sip.h"
struct SSLConnection *connections;
struct CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA =
{ 0x00, 0x2F };
struct CipherSuite TLS_RSA_WITH_AES_256_CBC_SHA =
{ 0x00, 0x35 };
int
P_hash(const char *digest, unsigned char *dest, int dlen, unsigned char *secret, int sslen,
unsigned char *seed, int slen)
{
unsigned char hmac[20];
unsigned int hlen;
HMAC_CTX hm;
const EVP_MD *md = EVP_get_digestbyname(digest);
unsigned int tmpslen;
unsigned char tmpseed[slen];
unsigned char *out = dest;
int pending = dlen;
// Copy initial seed
memcpy(tmpseed, seed, slen);
tmpslen = slen;
// Calculate enough data to fill destination
while (pending > 0) {
HMAC_Init(&hm, secret, sslen, md);
HMAC_Update(&hm, tmpseed, tmpslen);
HMAC_Final(&hm, tmpseed, &tmpslen);
HMAC_Init(&hm, secret, sslen, md);
HMAC_Update(&hm, tmpseed, tmpslen);
HMAC_Update(&hm, seed, slen);
HMAC_Final(&hm, hmac, &hlen);
hlen = (hlen > pending) ? pending : hlen;
memcpy(out, hmac, hlen);
out += hlen;
pending -= hlen;
}
HMAC_cleanup(&hm);
return hlen;
}
int
PRF(unsigned char *dest, int dlen, unsigned char *pre_master_secret, int plen, unsigned char *label,
unsigned char *seed, int slen)
{
int i;
// Split the secret by half to generate MD5 and SHA secret parts
int hplen = plen / 2 + plen % 2;
unsigned char md5_secret[hplen];
unsigned char sha_secret[hplen];
memcpy(md5_secret, pre_master_secret, hplen);
memcpy(sha_secret, pre_master_secret + plen / 2, plen / 2);
// This vars will store the values of P_MD5 and P_SHA-1
unsigned char h_md5[dlen];
unsigned char h_sha[dlen];
// Concatenate given seed to the label to get the final seed
int llen = strlen((const char*) label);
unsigned char fseed[slen + llen];
memcpy(fseed, label, llen);
memcpy(fseed + llen, seed, slen);
// Get enough MD5 and SHA1 data to fill output len
P_hash("MD5", h_md5, dlen, pre_master_secret, hplen, fseed, slen + llen);
P_hash("SHA1", h_sha, dlen, pre_master_secret + hplen, hplen, fseed, slen + llen);
// Final output will be MD5 and SHA1 X-ORed
for (i = 0; i < dlen; i++)
dest[i] = h_md5[i] ^ h_sha[i];
return dlen;
}
struct SSLConnection *
tls_connection_create(struct in_addr caddr, u_short cport, struct in_addr saddr, u_short sport) {
struct SSLConnection *conn = NULL;
conn = sng_malloc(sizeof(struct SSLConnection));
memcpy(&conn->client_addr, &caddr, sizeof(struct in_addr));
memcpy(&conn->server_addr, &saddr, sizeof(struct in_addr));
memcpy(&conn->client_port, &cport, sizeof(u_short));
memcpy(&conn->server_port, &sport, sizeof(u_short));
SSL_library_init();
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
if (!(conn->ssl_ctx = SSL_CTX_new(SSLv23_server_method())))
return NULL;
SSL_CTX_use_PrivateKey_file(conn->ssl_ctx, capture_get_keyfile(),
SSL_FILETYPE_PEM);
if (!(conn->ssl = SSL_new(conn->ssl_ctx)))
return NULL;
conn->server_private_key = SSL_get_privatekey(conn->ssl);
// Add this connection to the list
conn->next = connections;
connections = conn;
return conn;
}
void
tls_connection_destroy(struct SSLConnection *conn)
{
struct SSLConnection *c;
// Remove connection from connections list
if (conn == connections) {
connections = conn->next;
} else {
for (c = connections; c; c = c->next) {
if (c->next == conn) {
c->next = conn->next;
break;
}
}
}
// Deallocate connection memory
SSL_CTX_free(conn->ssl_ctx);
SSL_free(conn->ssl);
sng_free(conn);
}
/**
* FIXME Replace this with a tls_load_key function and use it
* in tls_connection_create.
*
* Most probably we only need one context and key for all connections
*/
int
tls_check_keyfile(const char *keyfile)
{
SSL *ssl;
SSL_CTX *ssl_ctx;
SSL_library_init();
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
if (access(capture_get_keyfile(), R_OK) != 0)
return 0;
if (!(ssl_ctx = SSL_CTX_new(SSLv23_server_method())))
return 0;
SSL_CTX_use_PrivateKey_file(ssl_ctx, capture_get_keyfile(), SSL_FILETYPE_PEM);
if (!(ssl = SSL_new(ssl_ctx)))
return 0;
if (!SSL_get_privatekey(ssl))
return 0;
return 1;
}
int
tls_connection_dir(struct SSLConnection *conn, struct in_addr addr, u_short port)
{
if (conn->client_addr.s_addr == addr.s_addr && conn->client_port == port)
return 0;
if (conn->server_addr.s_addr == addr.s_addr && conn->server_port == port)
return 1;
return -1;
}
struct SSLConnection*
tls_connection_find(struct in_addr addr, u_short port) {
struct SSLConnection *conn;
for (conn = connections; conn; conn = conn->next) {
if (tls_connection_dir(conn, addr, port) != -1) {
return conn;
}
}
return NULL;
}
int
tls_process_segment(capture_packet_t *packet, struct tcphdr *tcp)
{
struct SSLConnection *conn;
const u_char *payload = capture_packet_get_payload(packet);
uint32_t size_payload = capture_packet_get_payload_len(packet);
uint8 *out;
uint32_t outl = packet->payload_len;
out = sng_malloc(outl);
struct in_addr ip_src, ip_dst;
u_short sport = packet->sport;
u_short dport = packet->dport;
// Convert addresses
inet_pton(AF_INET, packet->ip_src, &ip_src);
inet_pton(AF_INET, packet->ip_dst, &ip_dst);
// Try to find a session for this ip
if ((conn = tls_connection_find(ip_src, sport))) {
// Update last connection direction
conn->direction = tls_connection_dir(conn, ip_src, sport);
// Check current connection state
switch (conn->state) {
case TCP_STATE_SYN:
// First SYN received, this package must be SYN/ACK
if (tcp->th_flags & TH_SYN & ~TH_ACK)
conn->state = TCP_STATE_SYN_ACK;
break;
case TCP_STATE_SYN_ACK:
// We expect an ACK packet here
if (tcp->th_flags & ~TH_SYN & TH_ACK)
conn->state = TCP_STATE_ESTABLISHED;
break;
case TCP_STATE_ACK:
case TCP_STATE_ESTABLISHED:
// Process data segment!
if (tls_process_record(conn, payload, size_payload, &out, &outl) == 0) {
if ((int32_t) outl > 0) {
capture_packet_set_payload(packet, out, outl);
capture_packet_set_type(packet, CAPTURE_PACKET_SIP_TLS);
return 0;
}
}
break;
case TCP_STATE_FIN:
case TCP_STATE_CLOSED:
// We can delete this connection
tls_connection_destroy(conn);
break;
}
} else {
if (tcp->th_flags & TH_SYN & ~TH_ACK) {
// New connection, store it status and leave
tls_connection_create(ip_src, sport, ip_dst, dport);
}
}
sng_free(out);
return 0;
}
int
tls_process_record(struct SSLConnection *conn, const uint8 *payload, const int len, uint8 **out,
uint32_t *outl)
{
struct TLSPlaintext *record;
int record_len;
const opaque *fragment;
// No record data here!
if (len == 0)
return 0;
// Get Record data
record = (struct TLSPlaintext *) payload;
record_len = sizeof(struct TLSPlaintext) + UINT16_INT(record->length);
// Process record fragment
if (UINT16_INT(record->length) > 0) {
// TLSPlaintext fragment pointer
fragment = (opaque *) payload + sizeof(struct TLSPlaintext);
switch (record->type) {
case handshake:
// Hanshake Record, Try to get MasterSecret data
if (tls_process_record_handshake(conn, fragment) != 0)
return 1;
break;
case change_cipher_spec:
// From now on, this connection will be encrypted using MasterSecret
conn->encrypted = 1;
break;
case application_data:
if (conn->encrypted) {
// Decrypt application data using MasterSecret
tls_process_record_data(conn, fragment, UINT16_INT(record->length), out, outl);
}
break;
default:
break;
}
}
// MultiRecord packet
if (len > record_len)
return tls_process_record(conn, payload + record_len, len - record_len, out, outl);
return 0;
}
int
tls_process_record_handshake(struct SSLConnection *conn, const opaque *fragment)
{
struct Handshake *handshake;
struct ClientHello *clienthello;
struct ServerHello *serverhello;
struct ClientKeyExchange *clientkeyex;
const opaque *body;
// Get Handshake data
handshake = (struct Handshake *) fragment;
if (UINT24_INT(handshake->length) > 0) {
// Hanshake body pointer
body = fragment + sizeof(struct Handshake);
switch (handshake->type) {
case hello_request:
break;
case client_hello:
// Store client random
clienthello = (struct ClientHello *) body;
memcpy(&conn->client_random, &clienthello->random, sizeof(struct Random));
// Check we have a TLS handshake
if (!(clienthello->client_version.major == 0x03
&& clienthello->client_version.minor == 0x01)) {
tls_connection_destroy(conn);
return 1;
}
break;
case server_hello:
// Store server random
serverhello = (struct ServerHello *) body;
memcpy(&conn->server_random, &serverhello->random, sizeof(struct Random));
// Get the selected cipher
memcpy(&conn->cipher_suite,
body + sizeof(struct ServerHello) + serverhello->session_id_length,
sizeof(uint16));
// Check if we have a handled cipher
if (tls_connection_load_cipher(conn) != 0) {
tls_connection_destroy(conn);
return 1;
}
break;
case certificate:
case certificate_request:
case server_hello_done:
case certificate_verify:
break;
case client_key_exchange:
// Decrypt PreMasterKey
clientkeyex = (struct ClientKeyExchange *) body;
RSA_private_decrypt(UINT16_INT(clientkeyex->length),
(const unsigned char *) &clientkeyex->exchange_keys,
(unsigned char *) &conn->pre_master_secret,
conn->server_private_key->pkey.rsa, RSA_PKCS1_PADDING);
unsigned char *seed = sng_malloc(sizeof(struct Random) * 2);
memcpy(seed, &conn->client_random, sizeof(struct Random));
memcpy(seed + sizeof(struct Random), &conn->server_random, sizeof(struct Random));
// Get MasterSecret
PRF((unsigned char *) &conn->master_secret, sizeof(struct MasterSecret),
(unsigned char *) &conn->pre_master_secret, sizeof(struct PreMasterSecret),
(unsigned char *) "master secret", seed, sizeof(struct Random) * 2);
memcpy(seed, &conn->server_random, sizeof(struct Random));
memcpy(seed + sizeof(struct Random), &conn->client_random, sizeof(struct Random));
// Generate MACs, Write Keys and IVs
PRF((unsigned char *) &conn->key_material, sizeof(struct tls_data),
(unsigned char *) &conn->master_secret, sizeof(struct MasterSecret),
(unsigned char *) "key expansion", seed, sizeof(struct Random) * 2);
// Done with the seed
sng_free(seed);
// Create Client decoder
EVP_CIPHER_CTX_init(&conn->client_cipher_ctx);
EVP_CipherInit(&conn->client_cipher_ctx, conn->ciph,
conn->key_material.client_write_key, conn->key_material.client_write_IV,
0);
EVP_CIPHER_CTX_init(&conn->server_cipher_ctx);
EVP_CipherInit(&conn->server_cipher_ctx, conn->ciph,
conn->key_material.server_write_key, conn->key_material.server_write_IV,
0);
break;
case finished:
break;
default:
if (conn->encrypted) {
// Encrypted Hanshake Message
unsigned char *decoded = sng_malloc(48);
uint32_t decodedlen;
tls_process_record_data(conn, fragment, 48, &decoded, &decodedlen);
sng_free(decoded);
}
break;
}
}
return 0;
}
int
tls_process_record_data(struct SSLConnection *conn, const opaque *fragment, const int len,
uint8 **out, uint32_t *outl)
{
EVP_CIPHER_CTX *evp;
unsigned char pad;
unsigned char *decoded;
uint32_t dlen;
if (conn->direction == 0) {
evp = &conn->client_cipher_ctx;
} else {
evp = &conn->server_cipher_ctx;
}
decoded = sng_malloc(len);
EVP_Cipher(evp, decoded, (unsigned char *) fragment, len);
// Get padding counter and remove from data
pad = decoded[len - 1];
dlen = (len - (pad + 1) - /* Trailing MAC */20);
if ((int32_t)dlen > 0 && dlen <= *outl) {
memcpy(*out, decoded, dlen);
*outl = dlen;
}
// Clenaup decoded memory
sng_free(decoded);
return *outl;
}
int
tls_connection_load_cipher(struct SSLConnection *conn)
{
if (conn->cipher_suite.cs1 != 0x00)
return 1;
if (conn->cipher_suite.cs2 == TLS_RSA_WITH_AES_256_CBC_SHA.cs2) {
conn->ciph = EVP_get_cipherbyname("AES256");
} else if (conn->cipher_suite.cs2 == TLS_RSA_WITH_AES_128_CBC_SHA.cs2) {
conn->ciph = EVP_get_cipherbyname("AES128");
} else {
return 1;
}
return 0;
}

412
src/capture_openssl.h Normal file
View File

@ -0,0 +1,412 @@
/**************************************************************************
**
** sngrep - SIP Messages flow viewer
**
** Copyright (C) 2013,2014 Ivan Alonso (Kaian)
** Copyright (C) 2013,2014 Irontec SL. All rights reserved.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
**
** In addition, as a special exception, the copyright holders give
** permission to link the code of portions of this program with the
** OpenSSL library under certain conditions as described in each
** individual source file, and distribute linked combinations
** including the two.
** You must obey the GNU General Public License in all respects
** for all of the code used other than OpenSSL. If you modify
** file(s) with this exception, you may extend this exception to your
** version of the file(s), but you are not obligated to do so. If you
** do not wish to do so, delete this exception statement from your
** version. If you delete this exception statement from all source
** files in the program, then also delete it here.
**
****************************************************************************/
/**
* @file capture_tls.h
* @author Ivan Alonso [aka Kaian] <kaian@irontec.com>
*
* @brief Functions to manage SIP TLS messages
*
* This file contains the functions and structures to manage the SIP messages
* that use TLS as transport.
*
*/
#ifndef __SNGREP_CAPTURE_TLS_
#define __SNGREP_CAPTURE_TLS_
#include "config.h"
#include <openssl/ssl.h>
#include <openssl/tls1.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include "capture.h"
//! Cast two bytes into decimal (Big Endian)
#define UINT16_INT(i) ((i.x[0] << 8) | i.x[1])
//! Cast three bytes into decimal (Big Endian)
#define UINT24_INT(i) ((i.x[0] << 16) | (i.x[1] << 8) | i.x[2])
//! One byte unsigned integer
typedef unsigned char uint8;
//! Two bytes unsigned integer
typedef struct uint16 {
unsigned char x[2];
} uint16;
//! Three bytes unsigned integer
typedef struct uint24 {
unsigned char x[3];
} uint24;
//! Four bytes unsigned interger
typedef struct uint32 {
unsigned char x[4];
} uint32;
//! One byte generic type
typedef unsigned char opaque;
//! SSLConnections states
enum SSLConnectionState {
//! Initial SYN packet has been received from client
TCP_STATE_SYN = 0,
//! SYN/ACK packet has been sent from the server
TCP_STATE_SYN_ACK,
//! Client ACK'ed the connection
TCP_STATE_ACK,
//! Connection is up, now SSL handshake should start!
TCP_STATE_ESTABLISHED,
//! Connection about to end
TCP_STATE_FIN,
//! Connection closed
TCP_STATE_CLOSED
};
//! ContentType values as defined in RFC5246
enum ContentType {
change_cipher_spec = SSL3_RT_CHANGE_CIPHER_SPEC,
alert = SSL3_RT_ALERT,
handshake = SSL3_RT_HANDSHAKE,
application_data = SSL3_RT_APPLICATION_DATA
};
//! HanshakeType values as defined in RFC5246
enum HandshakeType {
hello_request = SSL3_MT_HELLO_REQUEST,
client_hello = SSL3_MT_CLIENT_HELLO,
server_hello = SSL3_MT_SERVER_HELLO,
certificate = SSL3_MT_CERTIFICATE,
certificate_request = SSL3_MT_CERTIFICATE_REQUEST,
server_hello_done = SSL3_MT_SERVER_DONE,
certificate_verify = SSL3_MT_CERTIFICATE_VERIFY,
client_key_exchange = SSL3_MT_CLIENT_KEY_EXCHANGE,
finished = SSL3_MT_FINISHED
};
//! ProtocolVersion header as defined in RFC5246
struct ProtocolVersion {
uint8 major;
uint8 minor;
};
//! TLSPlaintext record structure
struct TLSPlaintext {
uint8 type;
struct ProtocolVersion version;
uint16 length;
};
//! Hanshake record structure
struct Handshake {
uint8 type;
uint24 length;
};
//! Handshake random structure
struct Random {
uint32 gmt_unix_time;
opaque random_bytes[28];
};
struct CipherSuite {
uint8 cs1;
uint8 cs2;
};
//! ClientHello type in Handshake records
struct ClientHello {
struct ProtocolVersion client_version;
struct Random random;
// uint8 session_id_length;
// CipherSuite cipher_suite;
// Extension extensions;
};
//! ServerHello type in Handshake records
struct ServerHello {
struct ProtocolVersion server_version;
struct Random random;
uint8 session_id_length;
// SessionID session_id;
// CipherSuite cipher_suite;
// CompressionMethod compression_method;
};
struct MasterSecret {
uint8 random[48];
};
struct PreMasterSecret {
struct ProtocolVersion client_version;
uint8 random[46];
};
struct EncryptedPreMasterSecret {
uint8 pre_master_secret[128];
};
//! ClientKeyExchange type in Handshake records
struct ClientKeyExchange {
uint16 length;
struct EncryptedPreMasterSecret exchange_keys;
};
/**
* Structure to store all information from a TLS
* connection. This is also used as linked list
* node.
*/
struct SSLConnection {
//! Connection status
enum SSLConnectionState state;
//! Current packet direction
int direction;
//! Data is encrypted flag
int encrypted;
//! Client IP address
struct in_addr client_addr;
//! Server IP address
struct in_addr server_addr;
//! Client port
u_short client_port;
//! Server port
u_short server_port;
SSL *ssl;
SSL_CTX *ssl_ctx;
EVP_PKEY *server_private_key;
const EVP_CIPHER *ciph;
struct Random client_random;
struct Random server_random;
struct CipherSuite cipher_suite;
struct PreMasterSecret pre_master_secret;
struct MasterSecret master_secret;
struct tls_data {
uint8 client_write_MAC_key[20];
uint8 server_write_MAC_key[20];
uint8 client_write_key[32];
uint8 server_write_key[32];
uint8 client_write_IV[16];
uint8 server_write_IV[16];
} key_material;
EVP_CIPHER_CTX client_cipher_ctx;
EVP_CIPHER_CTX server_cipher_ctx;
struct SSLConnection *next;
};
/**
* @brief P_hash expansion function as defined in RFC5246
*
* This function will expand Secret and Seed into output using digest
* hash function. The amount of data generated will be determined by output
* length (dlen).
*
* @param digest Digest name to get the hash function
* @param dest Destination of hash function result. Memory must be already allocated
* @param dlen Destination length in bytes
* @param secret Input for the hash function
* @param sslen Secret length in bytes
* @param seed Input for the hash function
* @param slen Seed length in bytes
* @return Output bytes
*/
int
P_hash(const char *digest, unsigned char *dest, int dlen, unsigned char *secret, int sslen,
unsigned char *seed, int slen);
/**
* @brief Pseudorandom Function as defined in RFC5246
*
* This function will generate MasterSecret and KeyMaterial data from PreMasterSecret and Seed
*
* @param dest Destination of PRF function result. Memory must be already allocated
* @param dlen Destination length in bytes
* @param pre_master_secret PreMasterSecret decrypted from ClientKeyExchange Handhsake record
* @param pslen PreMasterSecret length in bytes
* @param label Fixed ASCII string
* @param seed Concatenation of Random data from Hello Handshake records
* @param slen Seed length in bytes
* @return destination length in bytes
*/
int
PRF(unsigned char *dest, int dlen, unsigned char *pre_master_secret, int plen, unsigned char *label,
unsigned char *seed, int slen);
/**
* @brief Create a new SSLConnection
*
* This will allocate enough memory to store all connection data
* from a detected SSL connection. This will also add this structure to
* the connections linked list.
*
* @param caddr Client address
* @param cport Client port
* @param saddr Server address
* @param sport Server port
* @return a pointer to a new allocated SSLConnection structure
*/
struct SSLConnection *
tls_connection_create(struct in_addr caddr, u_short cport, struct in_addr saddr, u_short sport);
/**
* @brief Destroys an existing SSLConnection
*
* This will free all allocated memory of SSLConnection also removing
* the connection from connections list.
*
* @param conn Existing connection pointer
*/
void
tls_connection_destroy(struct SSLConnection *conn);
/**
* @brief Check if given keyfile is valid
*
* This can be used to check if a file contains valid RSA data
*
* @param keyfile Absolute path the keyfile
* @return 1 if file contains RSA private info, 0 otherwise
*/
int
tls_check_keyfile(const char *keyfile);
/**
* @brief Determines packet direction
*
* Determine if the given address is from client or server.
*
* @param conn Existing connection pointer
* @param addr Client or server address
* @param port Client or server port
* @return 0 if address belongs to client, 1 to server or -1 otherwise
*/
int
tls_connection_dir(struct SSLConnection *conn, struct in_addr addr, u_short port);
/**
* @brief Find a connection
*
* Try to find connection data for a given address and port.
* This address:port convination can be the client or server one.
*
* @param addr Client or server address
* @param port Client or server port
* @return an existing Connection pointer or NULL if not found
*/
struct SSLConnection*
tls_connection_find(struct in_addr addr, u_short port);
/**
* @brief Process a TCP segment to check TLS data
*
* Check if a TCP segment contains TLS data. In case a TLS record is found
* process it and return decrypted data if case of application_data record.
*
* @param tcp Pointer to tcp header of the packet
* @param out Pointer to the output char array. Memory must be already allocated
* @param out Number of bytes returned by this function
* @return 0 in all cases
*/
int
tls_process_segment(capture_packet_t *packet, struct tcphdr *tcp);
/**
* @brief Process TLS record data
*
* Process a TLS record
* - If the record type is Handshake process it in tls_process_record_handshake
* - If the record type is Application Data process it in tls_process_record_data
*
* @param conn Existing connection pointer
* @param payload Packet peyload
* @param len Payload length
* @param out pointer to store decryted data
* @param outl decrypted data length
* @return Decrypted data length
*/
int
tls_process_record(struct SSLConnection *conn, const uint8 *payload, const int len, uint8 **out,
uint32_t *outl);
/**
* @brief Process TLS Handshake record types
*
* Process all types of Handshake records to store and compute all required
* data to decrypt application data packets
*
* @param conn Existing connection pointer
* @param fragment Handshake record data
* @return 0 on valid record processed, 1 otherwise
*/
int
tls_process_record_handshake(struct SSLConnection *conn, const opaque *fragment);
/**
* @brief Process TLS ApplicationData record types
*
* Process application data record, trying to decrypt it with connection
* information
*
* @param conn Existing connection pointer
* @param fragment Application record data
* @param len record length in bytes
* @param out pointer to store decryted data
* @param outl decrypted data length
* @return decoded data length
*/
int
tls_process_record_data(struct SSLConnection *conn, const opaque *fragment, const int len,
uint8 **out, uint32_t *outl);
/**
* @brief Get the cipher data from the given connection
*
* Load cipher pointer depending on the selected cipher in
* Handshake messages.
*
* This function can be used to test is a cipher decrypting is supported
* @param conn Existing connection pointer
* @return 0 on valid cipher, 1 otherwise
*/
int
tls_connection_load_cipher(struct SSLConnection *conn);
#endif

View File

@ -38,7 +38,10 @@
#include "capture.h"
#include "capture_eep.h"
#ifdef WITH_GNUTLS
#include "capture_tls.h"
#include "capture_gnutls.h"
#endif
#ifdef WITH_OPENSSL
#include "capture_openssl.h"
#endif
/**
@ -50,7 +53,7 @@ void
usage()
{
printf("Usage: %s [-hVcivNq] [-IO pcap_dump] [-d dev] [-l limit]"
#ifdef WITH_GNUTLS
#if defined(WITH_GNUTLS) || defined(WITH_OPENSSL)
" [-k keyfile]"
#endif
" [<match expression>] [<bpf filter>]\n\n"
@ -69,7 +72,7 @@ usage()
" -H --eep-send\t Homer sipcapture url (udp:X.X.X.X:XXXX)\n"
" -L --eep-listen\t Listen for encapsulated packets (udp:X.X.X.X:XXXX)\n"
" -q --quiet\t\t Don't print captured dialogs in no interface mode\n"
#ifdef WITH_GNUTLS
#if defined(WITH_GNUTLS) || defined(WITH_OPENSSL)
" -k --keyfile\t RSA private keyfile to decrypt captured packets\n"
#endif
"\n",PACKAGE);
@ -87,6 +90,9 @@ version()
#ifdef WITH_GNUTLS
" * Compiled with GnuTLS support.\n"
#endif
#ifdef WITH_OPENSSL
" * Compiled with OpenSSL support.\n"
#endif
#ifdef WITH_UNICODE
" * Compiled with Wide-character support.\n"
#endif
@ -228,7 +234,7 @@ main(int argc, char* argv[])
}
}
#ifdef WITH_GNUTLS
#if defined(WITH_GNUTLS) || defined(WITH_OPENSSL)
// Set capture decrypt key file
capture_set_keyfile(keyfile);
// Check if we have a keyfile and is valid