Merge branch 'feature/genstore' into develop

pull/13/head
Daniel Roethlisberger 10 years ago
commit 27cf6c90e7

@ -9,6 +9,7 @@ patches or pull requests, in chronological order of their first contribution:
- Steve Wills ([swills](https://github.com/swills))
- Landon Fuller ([landonf](https://github.com/landonf))
- Wayne Jensen ([wjjensen](https://github.com/wjjensen))
- Rory McNamara ([psychomario](https://github.com/psychomario))
Many more individuals have contributed by reporting bugs or feature requests.
See [issue tracker on Github][1], `NEWS.md` and `git log` for details.

@ -1,4 +1,9 @@
### SSLsplit feature/genstore
- Add options -w and -W to write generated leaf key, original and forged
certificates to disk (issue #67 by @psychomario).
### SSLsplit develop
- Add signal SIGUSR1 to re-open long-living -l/-L log files (issue #52).

@ -117,6 +117,8 @@ main_usage(void)
" -K pemfile use key from pemfile for leaf certs (default: generate)\n"
" -t certdir use cert+chain+key PEM files from certdir to target all sites\n"
" matching the common names (non-matching: generate if CA)\n"
" -w gendir write leaf key and only generated certificates to gendir\n"
" -W gendir write leaf key and all certificates to gendir\n"
" -O deny all OCSP requests on all proxyspecs\n"
" -P passthrough SSL connections if they cannot be split because of\n"
" client cert auth or no matching cert and no CA (default: drop)\n"
@ -274,7 +276,7 @@ main(int argc, char *argv[])
}
while ((ch = getopt(argc, argv, OPT_g OPT_G OPT_Z OPT_i "k:c:C:K:t:"
"OPs:r:R:e:Eu:m:j:p:l:L:S:F:dDVh")) != -1) {
"OPs:r:R:e:Eu:m:j:p:l:L:S:F:dDVhW:w:")) != -1) {
switch (ch) {
case 'c':
if (opts->cacrt)
@ -615,6 +617,22 @@ main(int argc, char *argv[])
free(lhs);
free(rhs);
break;
case 'W':
opts->certgen_writeall = 1;
if (opts->certgendir)
free(opts->certgendir);
opts->certgendir = strdup(optarg);
if (!opts->certgendir)
oom_die(argv0);
break;
case 'w':
opts->certgen_writeall = 0;
if (opts->certgendir)
free(opts->certgendir);
opts->certgendir = strdup(optarg);
if (!opts->certgendir)
oom_die(argv0);
break;
}
#ifdef HAVE_LOCAL_PROCINFO
case 'i':
@ -747,6 +765,40 @@ main(int argc, char *argv[])
}
}
if (opts->certgendir) {
char *keyid, *keyfn;
int prv;
FILE *keyf;
keyid = ssl_key_identifier(opts->key, 0);
if (!keyid) {
fprintf(stderr, "%s: error generating key id\n", argv0);
exit(EXIT_FAILURE);
}
prv = asprintf(&keyfn, "%s/%s.key", opts->certgendir, keyid);
if (prv == -1) {
fprintf(stderr, "%s: %s (%i)\n", argv0,
strerror(errno), errno);
exit(EXIT_FAILURE);
}
if (!(keyf = fopen(keyfn, "w"))) {
fprintf(stderr, "%s: Failed to open '%s' for writing: "
"%s (%i)\n", argv0, keyfn,
strerror(errno), errno);
exit(EXIT_FAILURE);
}
if (!PEM_write_PrivateKey(keyf, opts->key, NULL, 0, 0,
NULL, NULL)) {
fprintf(stderr, "%s: Failed to write key to '%s': "
"%s (%i)\n", argv0, keyfn,
strerror(errno), errno);
exit(EXIT_FAILURE);
}
fclose(keyf);
}
/* usage checks after defaults */
if (opts->dropgroup && !opts->dropuser) {
fprintf(stderr, "%s: -m depends on -u.\n", argv0);

@ -105,6 +105,9 @@ opts_free(opts_t *opts)
if (opts->contentlog) {
free(opts->contentlog);
}
if (opts->certgendir) {
free(opts->certgendir);
}
if (opts->contentlog_basedir) {
free(opts->contentlog_basedir);
}

@ -80,7 +80,9 @@ typedef struct opts {
#ifdef HAVE_LOCAL_PROCINFO
unsigned int lprocinfo : 1;
#endif /* HAVE_LOCAL_PROCINFO */
unsigned int certgen_writeall: 1;
char *ciphers;
char *certgendir;
char *tgcrtdir;
char *dropuser;
char *dropgroup;

@ -40,6 +40,7 @@
#include "attrib.h"
#include "proc.h"
#include <sys/stat.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
@ -120,6 +121,7 @@ typedef struct pxy_conn_ctx {
/* status flags */
unsigned int immutable_cert : 1; /* 1 if the cert cannot be changed */
unsigned int generated_cert : 1; /* 1 if we generated a new cert */
unsigned int connected : 1; /* 0 until both ends are connected */
unsigned int seen_req_header : 1; /* 0 until request header complete */
unsigned int seen_resp_header : 1; /* 0 until response hdr complete */
@ -147,8 +149,10 @@ typedef struct pxy_conn_ctx {
char *http_status_text;
char *http_content_length;
/* log strings from SSL context */
/* log strings related to SSL */
char *ssl_names;
char *origcrtfpr;
char *usedcrtfpr;
#ifdef HAVE_LOCAL_PROCINFO
/* local process information */
@ -209,9 +213,7 @@ pxy_conn_ctx_new(proxyspec_t *spec, opts_t *opts,
return ctx;
}
static void
pxy_conn_ctx_free(pxy_conn_ctx_t *ctx) NONNULL(1);
static void
static void NONNULL(1)
pxy_conn_ctx_free(pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
@ -251,6 +253,12 @@ pxy_conn_ctx_free(pxy_conn_ctx_t *ctx)
if (ctx->ssl_names) {
free(ctx->ssl_names);
}
if (ctx->origcrtfpr) {
free(ctx->origcrtfpr);
}
if (ctx->usedcrtfpr) {
free(ctx->usedcrtfpr);
}
#ifdef HAVE_LOCAL_PROCINFO
if (ctx->lproc.exec_path) {
free(ctx->lproc.exec_path);
@ -312,17 +320,11 @@ pxy_debug_crt(X509 *crt)
free(names);
}
unsigned char fpr[SSL_X509_FPRSZ];
if (ssl_x509_fingerprint_sha1(crt, fpr) == -1) {
char *fpr;
if (!(fpr = ssl_x509_fingerprint(crt, 1))) {
log_err_printf("Warning: Error generating X509 fingerprint\n");
} else {
log_dbg_printf("Fingerprint: " "%02x:%02x:%02x:%02x:"
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
fpr[0], fpr[1], fpr[2], fpr[3], fpr[4],
fpr[5], fpr[6], fpr[7], fpr[8], fpr[9],
fpr[10], fpr[11], fpr[12], fpr[13], fpr[14],
fpr[15], fpr[16], fpr[17], fpr[18], fpr[19]);
log_dbg_printf("Fingerprint: %s\n", fpr);
}
#ifdef DEBUG_CERTIFICATE
@ -373,7 +375,8 @@ pxy_log_connect_nonhttp(pxy_conn_ctx_t *ctx)
} else {
rv = asprintf(&msg, "ssl %s %s "
"sni:%s names:%s "
"sproto:%s:%s dproto:%s:%s"
"sproto:%s:%s dproto:%s:%s "
"origcrt:%s usedcrt:%s"
#ifdef HAVE_LOCAL_PROCINFO
" %s"
#endif /* HAVE_LOCAL_PROCINFO */
@ -385,11 +388,13 @@ pxy_log_connect_nonhttp(pxy_conn_ctx_t *ctx)
SSL_get_version(ctx->src.ssl),
SSL_get_cipher(ctx->src.ssl),
SSL_get_version(ctx->dst.ssl),
SSL_get_cipher(ctx->dst.ssl)
SSL_get_cipher(ctx->dst.ssl),
STRORDASH(ctx->origcrtfpr),
STRORDASH(ctx->usedcrtfpr)
#ifdef HAVE_LOCAL_PROCINFO
, lpi
#endif /* HAVE_LOCAL_PROCINFO */
);
);
}
if ((rv < 0) || !msg) {
ctx->enomem = 1;
@ -466,7 +471,8 @@ pxy_log_connect_http(pxy_conn_ctx_t *ctx)
} else {
rv = asprintf(&msg, "https %s %s %s %s %s %s %s "
"sni:%s names:%s "
"sproto:%s:%s dproto:%s:%s"
"sproto:%s:%s dproto:%s:%s "
"origcrt:%s usedcrt:%s"
#ifdef HAVE_LOCAL_PROCINFO
" %s"
#endif /* HAVE_LOCAL_PROCINFO */
@ -484,6 +490,8 @@ pxy_log_connect_http(pxy_conn_ctx_t *ctx)
SSL_get_cipher(ctx->src.ssl),
SSL_get_version(ctx->dst.ssl),
SSL_get_cipher(ctx->dst.ssl),
STRORDASH(ctx->origcrtfpr),
STRORDASH(ctx->usedcrtfpr),
#ifdef HAVE_LOCAL_PROCINFO
lpi,
#endif /* HAVE_LOCAL_PROCINFO */
@ -724,6 +732,66 @@ pxy_srcsslctx_create(pxy_conn_ctx_t *ctx, X509 *crt, STACK_OF(X509) *chain,
return sslctx;
}
static int
pxy_srccert_write_to_gendir(pxy_conn_ctx_t *ctx, X509 *crt, int is_orig)
{
char *fn;
int rv;
struct stat sb;
FILE *f;
if (!ctx->origcrtfpr)
return -1;
if (is_orig) {
rv = asprintf(&fn, "%s/%s.crt", ctx->opts->certgendir,
ctx->origcrtfpr);
} else {
if (!ctx->usedcrtfpr)
return -1;
rv = asprintf(&fn, "%s/%s-%s.crt", ctx->opts->certgendir,
ctx->origcrtfpr, ctx->usedcrtfpr);
}
if (rv == -1) {
ctx->enomem = 1;
return -1;
}
if (stat(fn, &sb) == 0) {
free(fn);
return 0;
}
if (!(f = fopen(fn, "w"))) {
log_err_printf("Failed to open '%s' for writing: %s (%i)\n",
fn, strerror(errno), errno);
free(fn);
return -1;
}
if (!PEM_write_X509(f, crt)) {
log_err_printf("Failed to write certificate to '%s'\n", fn);
fclose(f);
free(fn);
return -1;
}
fclose(f);
free(fn);
return 0;
}
static void
pxy_srccert_write(pxy_conn_ctx_t *ctx)
{
if (ctx->opts->certgen_writeall || ctx->generated_cert) {
if (pxy_srccert_write_to_gendir(ctx,
SSL_get_certificate(ctx->src.ssl), 0) == -1) {
log_err_printf("Failed to write used certificate\n");
}
}
if (ctx->opts->certgen_writeall) {
if (pxy_srccert_write_to_gendir(ctx, ctx->origcrt, 1) == -1) {
log_err_printf("Failed to write orig certificate\n");
}
}
}
static cert_t *
pxy_srccert_create(pxy_conn_ctx_t *ctx)
{
@ -796,6 +864,19 @@ pxy_srccert_create(pxy_conn_ctx_t *ctx)
}
cert_set_key(cert, ctx->opts->key);
cert_set_chain(cert, ctx->opts->chain);
ctx->generated_cert = 1;
}
if ((WANT_CONNECT_LOG(ctx) || ctx->opts->certgendir) && ctx->origcrt) {
ctx->origcrtfpr = ssl_x509_fingerprint(ctx->origcrt, 0);
if (!ctx->origcrtfpr)
ctx->enomem = 1;
}
if ((WANT_CONNECT_LOG(ctx) || ctx->opts->certgen_writeall) &&
cert && cert->crt) {
ctx->usedcrtfpr = ssl_x509_fingerprint(cert->crt, 0);
if (!ctx->usedcrtfpr)
ctx->enomem = 1;
}
return cert;
@ -919,6 +1000,7 @@ pxy_ossl_servername_cb(SSL *ssl, UNUSED int *al, void *arg)
return SSL_TLSEXT_ERR_NOACK;
}
cachemgr_fkcrt_set(ctx->origcrt, newcrt);
ctx->generated_cert = 1;
if (OPTS_DEBUG(ctx->opts)) {
log_dbg_printf("===> Updated forged server "
"certificate:\n");
@ -933,6 +1015,16 @@ pxy_ossl_servername_cb(SSL *ssl, UNUSED int *al, void *arg)
ctx->enomem = 1;
}
}
if (WANT_CONNECT_LOG(ctx) || ctx->opts->certgendir) {
if (ctx->usedcrtfpr) {
free(ctx->usedcrtfpr);
}
ctx->usedcrtfpr = ssl_x509_fingerprint(newcrt, 0);
if (!ctx->usedcrtfpr) {
ctx->enomem = 1;
}
}
newsslctx = pxy_srcsslctx_create(ctx, newcrt, ctx->opts->chain,
ctx->opts->key);
if (!newsslctx) {
@ -1760,6 +1852,12 @@ connected:
pxy_log_connect_nonhttp(ctx);
}
/* write SSL certificates to gendir */
if (this->ssl && (bev == ctx->src.bev) &&
ctx->opts->certgendir) {
pxy_srccert_write(ctx);
}
if (OPTS_DEBUG(ctx->opts)) {
if (this->ssl) {
/* for SSL, we get two connect events */

54
ssl.c

@ -421,6 +421,30 @@ ssl_fini(void)
CRYPTO_cleanup_all_ex_data();
}
/*
* Format SHA1 hash into newly allocated string, with or without colons.
*/
char *
ssl_sha1_to_str(unsigned char *sha1, int colons)
{
char *str;
int rv;
rv = asprintf(&str, colons ?
"%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X"
"%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X" :
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
sha1[ 0], sha1[ 1], sha1[ 2], sha1[ 3],
sha1[ 4], sha1[ 5], sha1[ 6], sha1[ 7],
sha1[ 8], sha1[ 9], sha1[10], sha1[11],
sha1[12], sha1[13], sha1[14], sha1[15],
sha1[16], sha1[17], sha1[18], sha1[19]);
if (rv == -1)
return NULL;
return str;
}
/*
* Format SSL state into newly allocated string.
* Returns pointer to string that must be freed by caller, or NULL on error.
@ -1082,6 +1106,21 @@ errout:
return -1;
}
/*
* Returns the result of ssl_key_identifier_sha1() as hex characters with or
* without colons in a newly allocated string.
*/
char *
ssl_key_identifier(EVP_PKEY *key, int colons)
{
unsigned char id[SSL_KEY_IDSZ];
if (ssl_key_identifier_sha1(key, id) == -1)
return NULL;
return ssl_sha1_to_str(id, colons);
}
/*
* Returns the one-line representation of the subject DN in a newly allocated
* string which must be freed by the caller.
@ -1131,6 +1170,21 @@ ssl_x509_fingerprint_sha1(X509 *crt, unsigned char *fpr)
return X509_digest(crt, EVP_sha1(), fpr, &sz) ? 0 : -1;
}
/*
* Returns the result of ssl_x509_fingerprint_sha1() as hex characters with or
* without colons in a newly allocated string.
*/
char *
ssl_x509_fingerprint(X509 *crt, int colons)
{
unsigned char fpr[SSL_X509_FPRSZ];
if (ssl_x509_fingerprint_sha1(crt, fpr) == -1)
return NULL;
return ssl_sha1_to_str(fpr, colons);
}
#ifndef OPENSSL_NO_DH
/*
* Increment the reference count of DH parameters in a thread-safe

@ -113,6 +113,8 @@ int ssl_init(void) WUNRES;
void ssl_reinit(void);
void ssl_fini(void);
char * ssl_sha1_to_str(unsigned char *, int) NONNULL(1) MALLOC;
char * ssl_ssl_state_to_str(SSL *) NONNULL(1) MALLOC;
#ifndef OPENSSL_NO_DH
@ -130,6 +132,7 @@ EVP_PKEY * ssl_key_genrsa(const int) MALLOC;
void ssl_key_refcount_inc(EVP_PKEY *) NONNULL(1);
#define SSL_KEY_IDSZ 20
int ssl_key_identifier_sha1(EVP_PKEY *, unsigned char *) NONNULL(1,2);
char * ssl_key_identifier(EVP_PKEY *, int) NONNULL(1) MALLOC;
#ifndef OPENSSL_NO_TLSEXT
int ssl_x509_v3ext_add(X509V3_CTX *, X509 *, char *, char *) NONNULL(1,2,3,4);
@ -143,6 +146,7 @@ char * ssl_x509_subject(X509 *) NONNULL(1) MALLOC;
char * ssl_x509_subject_cn(X509 *, size_t *) NONNULL(1,2) MALLOC;
#define SSL_X509_FPRSZ 20
int ssl_x509_fingerprint_sha1(X509 *, unsigned char *) NONNULL(1,2);
char * ssl_x509_fingerprint(X509 *, int) NONNULL(1) MALLOC;
char ** ssl_x509_names(X509 *) NONNULL(1) MALLOC;
int ssl_x509_names_match(X509 *, const char *) NONNULL(1,2);
char * ssl_x509_names_to_str(X509 *) NONNULL(1) MALLOC;

@ -30,15 +30,15 @@ sslsplit \-\- transparent and scalable SSL/TLS interception
.SH SYNOPSIS
.na
.B sslsplit
[\fB-kCKOPZdDgGsrReumjplLSFi\fP] \fB-c\fP \fIpem\fP
[\fB-kCKwWOPZdDgGsrReumjplLSFi\fP] \fB-c\fP \fIpem\fP
\fIproxyspecs\fP [...]
.br
.B sslsplit
[\fB-kCKOPZdDgGsrReumjplLSFi\fP] \fB-c\fP \fIpem\fP \fB-t\fP \fIdir\fP
[\fB-kCKwWOPZdDgGsrReumjplLSFi\fP] \fB-c\fP \fIpem\fP \fB-t\fP \fIdir\fP
\fIproxyspecs\fP [...]
.br
.B sslsplit
[\fB-OPZdDgGsrReumjplLSFi\fP] \fB-t\fP \fIdir\fP
[\fB-OPZwWdDgGsrReumjplLSFi\fP] \fB-t\fP \fIdir\fP
\fIproxyspecs\fP [...]
.br
.B sslsplit -E
@ -291,6 +291,19 @@ Mac OS X.
.B \-V
Display version and compiled features information and exit.
.TP
.B \-w \fIgendir\fP
Write generated keys and certificates to individual files in \fIgendir\fP.
For keys, the key identifier is used as filename, which consists of the SHA-1
hash of the ASN.1 bit string of the public key, as referenced by the
subjectKeyIdentifier extension in certificates.
For certificates, the SHA-1 fingerprints of the original and the used (forged)
certificate are combined to form the filename.
Note that only newly generated certificates are written to disk.
.TP
.B \-W \fIgendir\fP
Same as \fB-w\fP, but also write original certificates and certificates not
newly generated, such as those loaded from \fB-t\fP.
.TP
.B \-Z
Disable SSL/TLS compression on all connections. This is useful if your
limiting factor is CPU, not network bandwidth.
@ -656,7 +669,7 @@ SSLsplit was written by Daniel Roethlisberger <daniel@roe.ch>.
The following individuals have contributed to the codebase, in chronological
order of their first contribution:
Steve Wills, Landon Fuller and Wayne Jensen.
Steve Wills, Landon Fuller, Wayne Jensen and Rory McNamara.
.SH BUGS
Use Github for submission of bug reports or patches:
.LP

Loading…
Cancel
Save