Validate proxyspec protocols http, pop3, and smtp

pull/13/head
Soner Tari 5 years ago
parent f3e7a359a6
commit b6f2203495

@ -123,7 +123,7 @@ split, SSLsplit accepts all certificates by default, including self-signed
ones. See [The Risks of SSL Inspection](https://insights.sei.cmu.edu/cert/2015/03/the-risks-of-ssl-inspection.html)
for the reasons of this difference.
If enabled the UserAuth option requires network users to log in to the system
If enabled, the UserAuth option requires network users to log in to the system
to use SSLproxy (this feature is currently available on OpenBSD only). When
users are logged in, they should be recorded on the users table in an SQLite3
database. The users table is created using the following SQL statement:
@ -146,6 +146,10 @@ connection is redirected to the login page. The atime of the IP address in the
users table is updated with the system time while the connection is being
terminated.
If enabled, the ValidateProto option validates protocols in proxy
specifications. If a connection cannot pass protocol validation, then it is
terminated. This feature currently supports HTTP, POP3, and SMTP protocols.
Logging options include traditional SSLproxy connect and content log files as
well as PCAP files and mirroring decrypted traffic to a network interface.
Additionally, certificates, master secrets and local process information can be

@ -71,6 +71,7 @@ opts_new(void)
opts->remove_http_referer = 1;
opts->verify_peer = 1;
opts->user_timeout = 300;
opts->max_http_header_size = 8192;
return opts;
}
@ -1491,6 +1492,18 @@ opts_set_user_auth_url(opts_t *opts, const char *optarg)
#endif /* DEBUG_OPTS */
}
static void
opts_set_validate_proto(opts_t *opts)
{
opts->validate_proto = 1;
}
static void
opts_unset_validate_proto(opts_t *opts)
{
opts->validate_proto = 0;
}
static int
check_value_yesno(const char *value, const char *name, int line_num)
{
@ -1674,6 +1687,26 @@ set_option(opts_t *opts, const char *argv0,
}
#ifdef DEBUG_OPTS
log_dbg_printf("UserTimeout: %u\n", opts->user_timeout);
#endif /* DEBUG_OPTS */
} else if (!strncmp(name, "ValidateProto", 14)) {
yes = check_value_yesno(value, "ValidateProto", line_num);
if (yes == -1) {
goto leave;
}
yes ? opts_set_validate_proto(opts) : opts_unset_validate_proto(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("ValidateProto: %u\n", opts->validate_proto);
#endif /* DEBUG_OPTS */
} else if (!strncasecmp(name, "MaxHTTPHeaderSize", 18)) {
unsigned int i = atoi(value);
if (i >= 1024 && i <= 65536) {
opts->max_http_header_size = i;
} else {
fprintf(stderr, "Invalid MaxHTTPHeaderSize %s at line %d, use 1024-65536\n", value, line_num);
goto leave;
}
#ifdef DEBUG_OPTS
log_dbg_printf("MaxHTTPHeaderSize: %u\n", opts->max_http_header_size);
#endif /* DEBUG_OPTS */
} else if (!strncmp(name, "ProxySpec", 10)) {
/* Use MAX_TOKEN instead of computing the actual number of tokens in value */

@ -149,6 +149,8 @@ typedef struct opts {
char *user_auth_url;
struct sqlite3_stmt *update_user_atime;
unsigned int user_timeout;
unsigned int validate_proto : 1;
unsigned int max_http_header_size;
} opts_t;
typedef struct userdbkeys {

@ -55,6 +55,10 @@ struct protohttp_ctx {
char *http_status_code;
char *http_status_text;
char *http_content_length;
unsigned int not_valid : 1; /* 1 if cannot find HTTP on first line */
unsigned int seen_keyword_count;
long long unsigned int seen_bytes;
};
static void NONNULL(1)
@ -277,7 +281,7 @@ deny:
*
* Returns NULL if the current line should be deleted from the request.
* Returns a newly allocated string if the current line should be replaced.
* Returns `line' if the line should be kept.
* Returns 'line' if the line should be kept.
*/
static char * NONNULL(1,2,3)
protohttp_filter_request_header_line(const char *line, pxy_conn_ctx_t *ctx, protohttp_ctx_t *http_ctx)
@ -292,6 +296,7 @@ protohttp_filter_request_header_line(const char *line, pxy_conn_ctx_t *ctx, prot
if (!space1) {
/* not HTTP */
http_ctx->seen_req_header = 1;
http_ctx->not_valid = 1;
} else {
http_ctx->http_method = malloc(space1 - line + 1);
if (http_ctx->http_method) {
@ -326,12 +331,14 @@ protohttp_filter_request_header_line(const char *line, pxy_conn_ctx_t *ctx, prot
ctx->conn->enomem = 1;
return NULL;
}
http_ctx->seen_keyword_count++;
} else if (!strncasecmp(line, "Content-Type:", 13)) {
http_ctx->http_content_type = strdup(util_skipws(line + 13));
if (!http_ctx->http_content_type) {
ctx->conn->enomem = 1;
return NULL;
}
http_ctx->seen_keyword_count++;
/* Override Connection: keepalive and Connection: upgrade */
} else if (!strncasecmp(line, "Connection:", 11)) {
http_ctx->sent_http_conn_close = 1;
@ -339,17 +346,21 @@ protohttp_filter_request_header_line(const char *line, pxy_conn_ctx_t *ctx, prot
ctx->conn->enomem = 1;
return NULL;
}
http_ctx->seen_keyword_count++;
return newhdr;
// @attention Always use conn ctx for opts, child ctx does not have opts, see the comments in pxy_conn_child_ctx
} else if (ctx->conn->opts->remove_http_accept_encoding && !strncasecmp(line, "Accept-Encoding:", 16)) {
http_ctx->seen_keyword_count++;
return NULL;
} else if (ctx->conn->opts->remove_http_referer && !strncasecmp(line, "Referer:", 8)) {
http_ctx->seen_keyword_count++;
return NULL;
/* Suppress upgrading to SSL/TLS, WebSockets or HTTP/2,
* unsupported encodings, and keep-alive */
} else if (!strncasecmp(line, "Upgrade:", 8) ||
!strncasecmp(line, "Accept-Encoding:", 16) ||
!strncasecmp(line, "Keep-Alive:", 11)) {
http_ctx->seen_keyword_count++;
return NULL;
} else if ((ctx->type == CONN_TYPE_CHILD) && (!strncasecmp(line, SSLPROXY_KEY, SSLPROXY_KEY_LEN) ||
// @attention flickr keeps redirecting to https with 301 unless we remove the Via line of squid
@ -357,6 +368,7 @@ protohttp_filter_request_header_line(const char *line, pxy_conn_ctx_t *ctx, prot
!strncasecmp(line, "Via:", 4) ||
// Also do not send the loopback address to the Internet
!strncasecmp(line, "X-Forwarded-For:", 16))) {
http_ctx->seen_keyword_count++;
return NULL;
} else if (line[0] == '\0') {
http_ctx->seen_req_header = 1;
@ -476,7 +488,10 @@ protohttp_get_url(struct evbuffer *inbuf, pxy_conn_ctx_t *ctx)
if (host && path) {
// Assume that path will always have a leading /, so do not insert an extra / in between host and path
size_t url_size = 4 + 1 + 3 + strlen(host) + strlen(path) + 1;
// Don't care about computing the exact url size for plain or secure http (http or https)
// http s :// example.com + / + NULL
// 4 + 1 + 3 + strlen(host) + strlen(path) + 1
size_t url_size = strlen(host) + strlen(path) + 9;
url = malloc(url_size);
if (!url) {
ctx->enomem = 1;
@ -495,17 +510,83 @@ memout:
return url;
}
static void NONNULL(1)
// Size = 39
static char *http_methods[] = { "GET", "PUT", "ICY", "COPY", "HEAD", "LOCK", "MOVE", "POLL", "POST", "BCOPY", "BMOVE", "MKCOL", "TRACE", "LABEL", "MERGE", "DELETE",
"SEARCH", "UNLOCK", "REPORT", "UPDATE", "NOTIFY", "BDELETE", "CONNECT", "OPTIONS", "CHECKIN", "PROPFIND", "CHECKOUT", "CCM_POST", "SUBSCRIBE",
"PROPPATCH", "BPROPFIND", "BPROPPATCH", "UNCHECKOUT", "MKACTIVITY", "MKWORKSPACE", "UNSUBSCRIBE", "RPC_CONNECT", "VERSION-CONTROL", "BASELINE-CONTROL" };
static int NONNULL(1)
protohttp_validate_method(char *method)
{
char *m;
unsigned int i;
for (i = 0; i < sizeof(http_methods)/sizeof(char *); i++) {
m = http_methods[i];
if (!strncasecmp(method, m, strlen(m))) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protohttp_validate_method: Passed method validation: %s\n", method);
#endif /* DEBUG_PROXY */
return 0;
}
}
return -1;
}
static int NONNULL(1,2)
protohttp_validate(protohttp_ctx_t *http_ctx, pxy_conn_ctx_t *ctx)
{
if (http_ctx->not_valid) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protohttp_validate: Not http, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
return -1;
}
if (http_ctx->http_method) {
if (protohttp_validate_method(http_ctx->http_method) == -1) {
http_ctx->not_valid = 1;
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protohttp_validate: Failed method validation: %s, fd=%d\n", http_ctx->http_method, ctx->fd);
#endif /* DEBUG_PROXY */
return -1;
}
}
if (http_ctx->seen_keyword_count) {
// The first line has been processed successfully
// Pass validation if we have seen at least one http keyword
ctx->protoctx->is_valid = 1;
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protohttp_validate: Passed validation, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
return 0;
}
if (http_ctx->seen_bytes > ctx->opts->max_http_header_size) {
// Fail validation if still cannot pass as http after reaching max header size
http_ctx->not_valid = 1;
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protohttp_validate: Reached max header size, size=%llu, fd=%d\n", http_ctx->seen_bytes, ctx->fd);
#endif /* DEBUG_PROXY */
return -1;
}
return 0;
}
static void NONNULL(1,2)
protohttp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
const char redirect[] =
static const char redirect[] =
"HTTP/1.1 302 Found\r\n"
"Location: %s\r\n"
"\r\n";
const char redirect_url[] =
static const char redirect_url[] =
"HTTP/1.1 302 Found\r\n"
"Location: %s?SSLproxy=%s\r\n"
"\r\n";
static const char proto_error[] =
"HTTP/1.1 400 Bad request\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"Content-Type: text/html\r\n"
"\r\n";
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protohttp_bev_readcb_src: ENTER, size=%zu, fd=%d\n",
@ -538,6 +619,10 @@ protohttp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
return;
}
if (ctx->opts->validate_proto && !ctx->protoctx->is_valid) {
http_ctx->seen_bytes += evbuffer_get_length(inbuf);
}
// We insert our special header line to the first packet we get, e.g. right after the first \r\n in the case of http
// @todo Should we look for GET/POST or Host header lines to detect the first packet?
// But there is no guarantee that they will exist, due to fragmentation.
@ -563,6 +648,17 @@ protohttp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
evbuffer_add_buffer(outbuf, inbuf);
}
if (ctx->opts->validate_proto && !ctx->protoctx->is_valid) {
if (protohttp_validate(http_ctx, ctx) == -1) {
evbuffer_add(bufferevent_get_output(bev), proto_error, strlen(proto_error));
ctx->sent_protoerror_msg = 1;
pxy_discard_inbuf(bev);
evbuffer_drain(outbuf, evbuffer_get_length(outbuf));
return;
}
}
pxy_try_set_watermark(bev, ctx, ctx->dst.bev);
}

@ -0,0 +1,134 @@
/*-
* SSLproxy - transparent SSL/TLS proxy
*
* Copyright (c) 2018-2019, Soner Tari <sonertari@gmail.com>.
* 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 "protopop3.h"
#include "protossl.h"
#include <string.h>
typedef struct protopop3_ctx protopop3_ctx_t;
struct protopop3_ctx {
unsigned int not_valid : 1;
unsigned int seen_command_count;
};
// Size = 14
static char *protopop3_commands[] = { "CAPA", "USER", "PASS", "AUTH", "APOP", "STLS", "LIST", "STAT", "UIDL", "RETR", "DELE", "RSET", "TOP", "QUIT", "NOOP" };
static int NONNULL(1)
protopop3_validate_command(char *packet, UNUSED size_t packet_size)
{
char *c;
unsigned int i;
for (i = 0; i < sizeof(protopop3_commands)/sizeof(char *); i++) {
c = protopop3_commands[i];
if (!strncasecmp(packet, c, strlen(c))) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protopop3_validate_command: Passed command validation: %.*s\n", (int)packet_size, packet);
#endif /* DEBUG_PROXY */
return 0;
}
}
return -1;
}
static int NONNULL(1,2)
protopop3_validate(pxy_conn_ctx_t *ctx, char *packet, size_t packet_size)
{
protopop3_ctx_t *pop3_ctx = ctx->protoctx->arg;
if (pop3_ctx->not_valid) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protopop3_validate: Not pop3\n");
#endif /* DEBUG_PROXY */
return -1;
}
if (protopop3_validate_command(packet, packet_size) == -1) {
pop3_ctx->not_valid = 1;
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protopop3_validate: Failed command validation: %.*s\n", (int)packet_size, packet);
#endif /* DEBUG_PROXY */
return -1;
} else {
pop3_ctx->seen_command_count++;
}
if (pop3_ctx->seen_command_count > 2) {
ctx->protoctx->is_valid = 1;
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protopop3_validate: Passed validation\n");
#endif /* DEBUG_PROXY */
}
return 0;
}
protocol_t
protopop3_setup(pxy_conn_ctx_t *ctx)
{
ctx->protoctx->proto = PROTO_POP3;
ctx->protoctx->validatecb = protopop3_validate;
ctx->protoctx->arg = malloc(sizeof(protopop3_ctx_t));
if (!ctx->protoctx->arg) {
return PROTO_ERROR;
}
memset(ctx->protoctx->arg, 0, sizeof(protopop3_ctx_t));
return PROTO_POP3;
}
protocol_t
protopop3s_setup(pxy_conn_ctx_t *ctx)
{
ctx->protoctx->proto = PROTO_POP3S;
ctx->protoctx->connectcb = protossl_conn_connect;
ctx->protoctx->fd_readcb = protossl_fd_readcb;
ctx->protoctx->bev_eventcb = protossl_bev_eventcb;
ctx->protoctx->proto_free = protossl_free;
ctx->protoctx->validatecb = protopop3_validate;
ctx->protoctx->arg = malloc(sizeof(protopop3_ctx_t));
if (!ctx->protoctx->arg) {
return PROTO_ERROR;
}
memset(ctx->protoctx->arg, 0, sizeof(protopop3_ctx_t));
ctx->sslctx = malloc(sizeof(ssl_ctx_t));
if (!ctx->sslctx) {
free(ctx->protoctx->arg);
return PROTO_ERROR;
}
memset(ctx->sslctx, 0, sizeof(ssl_ctx_t));
return PROTO_POP3S;
}
/* vim: set noet ft=c: */

@ -0,0 +1,37 @@
/*-
* SSLproxy - transparent SSL/TLS proxy
*
* Copyright (c) 2018-2019, Soner Tari <sonertari@gmail.com>.
* 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 PROTOPOP3_H
#define PROTOPOP3_H
#include "pxyconn.h"
protocol_t protopop3_setup(pxy_conn_ctx_t *) NONNULL(1);
protocol_t protopop3s_setup(pxy_conn_ctx_t *) NONNULL(1);
#endif /* PROTOPOP3_H */

@ -0,0 +1,135 @@
/*-
* SSLproxy - transparent SSL/TLS proxy
*
* Copyright (c) 2018-2019, Soner Tari <sonertari@gmail.com>.
* 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 "protosmtp.h"
#include "protossl.h"
#include <string.h>
typedef struct protosmtp_ctx protosmtp_ctx_t;
struct protosmtp_ctx {
unsigned int not_valid : 1;
unsigned int seen_command_count;
};
// Size = 25
static char *protosmtp_commands[] = { "EHLO", "HELO", "AUTH", "MAIL", "MAIL FROM", "RCPT", "RCPT TO", "DATA", "SEND", "RSET", "QUIT", "ATRN", "ETRN", "TURN",
"SAML", "SOML", "EXPN", "NOOP", "HELP", "ONEX", "BDAT", "BURL", "SUBMITTER", "VERB", "VRFY" };
static int NONNULL(1)
protosmtp_validate_command(char *packet, UNUSED size_t packet_size)
{
char *c;
unsigned int i;
for (i = 0; i < sizeof(protosmtp_commands)/sizeof(char *); i++) {
c = protosmtp_commands[i];
if (!strncasecmp(packet, c, strlen(c))) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protosmtp_validate_command: Passed command validation: %.*s\n", (int)packet_size, packet);
#endif /* DEBUG_PROXY */
return 0;
}
}
return -1;
}
static int NONNULL(1,2)
protosmtp_validate(pxy_conn_ctx_t *ctx, char *packet, size_t packet_size)
{
protosmtp_ctx_t *smtp_ctx = ctx->protoctx->arg;
if (smtp_ctx->not_valid) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protosmtp_validate: Not smtp\n");
#endif /* DEBUG_PROXY */
return -1;
}
if (protosmtp_validate_command(packet, packet_size) == -1) {
smtp_ctx->not_valid = 1;
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protosmtp_validate: Failed command validation: %.*s\n", (int)packet_size, packet);
#endif /* DEBUG_PROXY */
return -1;
} else {
smtp_ctx->seen_command_count++;
}
if (smtp_ctx->seen_command_count > 2) {
ctx->protoctx->is_valid = 1;
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protosmtp_validate: Passed validation\n");
#endif /* DEBUG_PROXY */
}
return 0;
}
protocol_t
protosmtp_setup(pxy_conn_ctx_t *ctx)
{
ctx->protoctx->proto = PROTO_SMTP;
ctx->protoctx->validatecb = protosmtp_validate;
ctx->protoctx->arg = malloc(sizeof(protosmtp_ctx_t));
if (!ctx->protoctx->arg) {
return PROTO_ERROR;
}
memset(ctx->protoctx->arg, 0, sizeof(protosmtp_ctx_t));
return PROTO_SMTP;
}
protocol_t
protosmtps_setup(pxy_conn_ctx_t *ctx)
{
ctx->protoctx->proto = PROTO_SMTPS;
ctx->protoctx->connectcb = protossl_conn_connect;
ctx->protoctx->fd_readcb = protossl_fd_readcb;
ctx->protoctx->bev_eventcb = protossl_bev_eventcb;
ctx->protoctx->proto_free = protossl_free;
ctx->protoctx->validatecb = protosmtp_validate;
ctx->protoctx->arg = malloc(sizeof(protosmtp_ctx_t));
if (!ctx->protoctx->arg) {
return PROTO_ERROR;
}
memset(ctx->protoctx->arg, 0, sizeof(protosmtp_ctx_t));
ctx->sslctx = malloc(sizeof(ssl_ctx_t));
if (!ctx->sslctx) {
free(ctx->protoctx->arg);
return PROTO_ERROR;
}
memset(ctx->sslctx, 0, sizeof(ssl_ctx_t));
return PROTO_SMTPS;
}
/* vim: set noet ft=c: */

@ -0,0 +1,37 @@
/*-
* SSLproxy - transparent SSL/TLS proxy
*
* Copyright (c) 2018-2019, Soner Tari <sonertari@gmail.com>.
* 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 PROTOSMTP_H
#define PROTOSMTP_H
#include "pxyconn.h"
protocol_t protosmtp_setup(pxy_conn_ctx_t *) NONNULL(1);
protocol_t protosmtps_setup(pxy_conn_ctx_t *) NONNULL(1);
#endif /* PROTOSMTP_H */

@ -31,6 +31,7 @@
#include "protopassthrough.h"
#include <sys/param.h>
#include <string.h>
/*
* Set up a bufferevent structure for either a dst or src connection,
@ -260,7 +261,33 @@ prototcp_try_send_userauth_msg(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
return 0;
}
static void NONNULL(1)
static int NONNULL(1,2,3,4)
prototcp_try_validate_proto(struct bufferevent *bev, pxy_conn_ctx_t *ctx, struct evbuffer *inbuf, struct evbuffer *outbuf)
{
if (ctx->opts->validate_proto && ctx->protoctx->validatecb && !ctx->protoctx->is_valid) {
size_t packet_size = evbuffer_get_length(inbuf);
char *packet = (char *)pxy_malloc_packet(packet_size, ctx);
if (!packet) {
return -1;
}
if (evbuffer_copyout(inbuf, packet, packet_size) == -1) {
free(packet);
return -1;
}
if (ctx->protoctx->validatecb(ctx, packet, packet_size) == -1) {
evbuffer_add(bufferevent_get_output(bev), PROTOERROR_MSG, PROTOERROR_MSG_LEN);
ctx->sent_protoerror_msg = 1;
pxy_discard_inbuf(bev);
evbuffer_drain(outbuf, evbuffer_get_length(outbuf));
free(packet);
return 1;
}
free(packet);
}
return 0;
}
static void NONNULL(1,2)
prototcp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
#ifdef DEBUG_PROXY
@ -279,6 +306,10 @@ prototcp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
struct evbuffer *inbuf = bufferevent_get_input(bev);
struct evbuffer *outbuf = bufferevent_get_output(ctx->dst.bev);
if (prototcp_try_validate_proto(bev, ctx, inbuf, outbuf) != 0) {
return;
}
if (!ctx->sent_sslproxy_header) {
size_t packet_size = evbuffer_get_length(inbuf);
@ -422,6 +453,26 @@ prototcp_try_close_unauth_conn(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
return 0;
}
static int NONNULL(1,2)
prototcp_try_close_protoerror_conn(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
if (ctx->opts->validate_proto && ctx->sent_protoerror_msg) {
size_t outbuflen = evbuffer_get_length(bufferevent_get_output(bev));
if (outbuflen > 0) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_try_close_protoerror_conn: Not closing protoerror conn, outbuflen=%zu, fd=%d\n", outbuflen, ctx->fd);
#endif /* DEBUG_PROXY */
} else {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_try_close_protoerror_conn: Closing protoerror conn, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
pxy_conn_term(ctx, 1);
}
return 1;
}
return 0;
}
static void NONNULL(1)
prototcp_bev_writecb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
@ -433,6 +484,10 @@ prototcp_bev_writecb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
return;
}
if (prototcp_try_close_protoerror_conn(bev, ctx)) {
return;
}
if (ctx->dst.closed) {
if (pxy_try_close_conn_end(&ctx->src, ctx) == 1) {
#ifdef DEBUG_PROXY

@ -32,6 +32,8 @@
#include "prototcp.h"
#include "protossl.h"
#include "protohttp.h"
#include "protopop3.h"
#include "protosmtp.h"
#include "protoautossl.h"
#include "protopassthrough.h"
@ -102,15 +104,15 @@ pxy_setup_proto(pxy_conn_ctx_t *ctx)
}
} else if (ctx->spec->pop3) {
if (ctx->spec->ssl) {
proto = (protossl_setup(ctx) != PROTO_ERROR) ? PROTO_POP3S : PROTO_ERROR;
proto = protopop3s_setup(ctx);
} else {
proto = PROTO_POP3;
proto = protopop3_setup(ctx);
}
} else if (ctx->spec->smtp) {
if (ctx->spec->ssl) {
proto = (protossl_setup(ctx) != PROTO_ERROR) ? PROTO_SMTPS : PROTO_ERROR;
proto = protosmtps_setup(ctx);
} else {
proto = PROTO_SMTP;
proto = protosmtp_setup(ctx);
}
} else if (ctx->spec->ssl) {
proto = protossl_setup(ctx);

@ -54,7 +54,10 @@
#define SSLPROXY_KEY "SSLproxy:"
#define SSLPROXY_KEY_LEN strlen(SSLPROXY_KEY)
#define USERAUTH_MSG "You must authenticate to access the Internet at %s"
#define USERAUTH_MSG "You must authenticate to access the Internet at %s\r\n"
#define PROTOERROR_MSG "Connection is terminated due to protocol error\r\n"
#define PROTOERROR_MSG_LEN strlen(PROTOERROR_MSG)
typedef struct pxy_conn_child_ctx pxy_conn_child_ctx_t;
@ -67,6 +70,7 @@ typedef void (*eventcb_func_t)(struct bufferevent *, short, void *);
typedef void (*bev_free_func_t)(struct bufferevent *, pxy_conn_ctx_t *);
typedef void (*proto_free_func_t)(pxy_conn_ctx_t *);
typedef int (*proto_validate_func_t)(pxy_conn_ctx_t *, char *, size_t);
typedef void (*child_connect_func_t)(pxy_conn_child_ctx_t *);
typedef void (*child_proto_free_func_t)(pxy_conn_child_ctx_t *);
@ -141,6 +145,8 @@ struct proto_ctx {
eventcb_func_t bev_eventcb;
proto_free_func_t proto_free;
proto_validate_func_t validatecb;
unsigned int is_valid : 1; /* 0 until passed proto validation */
// For protocol specific fields, if any
void *arg;
@ -282,6 +288,7 @@ struct pxy_conn_ctx {
char *user;
char *ether;
unsigned int sent_userauth_msg : 1;
unsigned int sent_protoerror_msg : 1;
#ifdef HAVE_LOCAL_PROCINFO
/* local process information */

@ -29,7 +29,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.TH "sslproxy" "1" "06 Mar 2019" "v0.5.10" "SSLproxy"
.TH "sslproxy" "1" "07 Mar 2019" "v0.5.10" "SSLproxy"
.SH NAME
sslproxy \-\- transparent SSL/TLS proxy for decrypting and diverting network
traffic to other programs for deep SSL inspection
@ -124,7 +124,7 @@ because in order to maximize the chances that a connection can be successfully
split, SSLsplit accepts all certificates by default, including self-signed
ones.
.LP
If enabled the UserAuth option requires network users to log in to the system
If enabled, the UserAuth option requires network users to log in to the system
to use SSLproxy (this feature is currently available on OpenBSD only). When
users are logged in, they should be recorded on the users table in an SQLite3
database. The users table is created using the following SQL statement:
@ -147,6 +147,10 @@ connection is redirected to the login page. The atime of the IP address in the
users table is updated with the system time while the connection is being
terminated.
.LP
If enabled, the ValidateProto option validates protocols in proxy
specifications. If a connection cannot pass protocol validation, then it is
terminated. This feature currently supports HTTP, POP3, and SMTP protocols.
.LP
Logging options include traditional SSLproxy connect and content log files as
well as PCAP files and mirroring decrypted traffic to a network interface.
Additionally, certificates, master secrets and local process information can be

@ -214,6 +214,12 @@ AllowWrongHost no
# Redirect URL for users to log in to the system
#UserAuthURL https://192.168.0.1/userdblogin.php
# Validate proxy spec protocols
#ValidateProto no
# Max HTTP header size in bytes for protocol validation
#MaxHTTPHeaderSize 8192
# Proxy specifications
# type listenaddr+port up:utmport ua:utmaddr ra:returnaddr
#ProxySpec https 127.0.0.1 8443 up:8080 ua:127.0.0.1 ra:127.0.0.1

@ -26,7 +26,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.TH "sslproxy.conf" "5" "06 Mar 2019" "v0.5.10" "SSLproxy"
.TH "sslproxy.conf" "5" "07 Mar 2019" "v0.5.10" "SSLproxy"
.SH "NAME"
.LP
\fBsslproxy.conf\fR \- Configuration file for SSLproxy
@ -254,6 +254,16 @@ Default: 300.
\fBUserAuthURL STRING\fR
Redirect URL for users to log in to the system.
.TP
\fBValidateProto BOOL\fR
Validate proxy spec protocols.
.br
Default: no
.TP
\fBMaxHTTPHeaderSize NUMBER\fR
Max HTTP header size in bytes for protocol validation.
.br
Default: 8192.
.TP
\fBProxySpec STRING\fR
Proxy specification: type listenaddr+port up:port ua:addr ra:addr. Multiple specs are allowed, one on each line.
.SH "FILES"

Loading…
Cancel
Save