summaryrefslogtreecommitdiff
path: root/usr.sbin/relayd
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2013-05-30 20:17:13 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2013-05-30 20:17:13 +0000
commit0c2d8cd7deeae266c9f95b1c0cf9b50f5db07c8a (patch)
treed94ea7e5c9d6a8e00007e14c32c9c9bc993dc613 /usr.sbin/relayd
parent44440dacaf7f504a2a860b04576e7c1d91c85652 (diff)
Support SSL inspection, the ability to transparently filter in SSL/TLS
connections (eg. HTTPS) by using a local CA that is accepted by the clients. See the "SSL RELAYS" and "EXAMPLES" sections in the relayd.conf(5) manpage for more details. ok benno@, manpage bits jmc@
Diffstat (limited to 'usr.sbin/relayd')
-rw-r--r--usr.sbin/relayd/config.c29
-rw-r--r--usr.sbin/relayd/parse.y35
-rw-r--r--usr.sbin/relayd/relay.c91
-rw-r--r--usr.sbin/relayd/relayd.c12
-rw-r--r--usr.sbin/relayd/relayd.conf.5161
-rw-r--r--usr.sbin/relayd/relayd.h18
-rw-r--r--usr.sbin/relayd/ssl.c148
7 files changed, 474 insertions, 20 deletions
diff --git a/usr.sbin/relayd/config.c b/usr.sbin/relayd/config.c
index 73e6b856a05..ab87e2ff3eb 100644
--- a/usr.sbin/relayd/config.c
+++ b/usr.sbin/relayd/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.8 2012/12/18 15:57:16 reyk Exp $ */
+/* $OpenBSD: config.c,v 1.9 2013/05/30 20:17:12 reyk Exp $ */
/*
* Copyright (c) 2011 Reyk Floeter <reyk@openbsd.org>
@@ -177,6 +177,8 @@ config_purge(struct relayd *env, u_int reset)
purge_tree(&proto->response_tree);
if (proto->style != NULL)
free(proto->style);
+ if (proto->sslcapass != NULL)
+ free(proto->sslcapass);
free(proto);
}
env->sc_protocount = 0;
@@ -635,6 +637,7 @@ config_getproto(struct relayd *env, struct imsg *imsg)
proto->request_nodes = 0;
proto->response_nodes = 0;
+ proto->sslcapass = NULL;
RB_INIT(&proto->request_tree);
RB_INIT(&proto->response_tree);
@@ -830,6 +833,14 @@ config_setrelay(struct relayd *env, struct relay *rlay)
iov[c].iov_base = rlay->rl_ssl_ca;
iov[c++].iov_len = rlay->rl_conf.ssl_ca_len;
}
+ if (rlay->rl_conf.ssl_cacert_len) {
+ iov[c].iov_base = rlay->rl_ssl_cacert;
+ iov[c++].iov_len = rlay->rl_conf.ssl_cacert_len;
+ }
+ if (rlay->rl_conf.ssl_cakey_len) {
+ iov[c].iov_base = rlay->rl_ssl_cakey;
+ iov[c++].iov_len = rlay->rl_conf.ssl_cakey_len;
+ }
if (id == PROC_RELAY) {
/* XXX imsg code will close the fd after 1st call */
@@ -898,7 +909,9 @@ config_getrelay(struct relayd *env, struct imsg *imsg)
if ((u_int)(IMSG_DATA_SIZE(imsg) - s) <
(rlay->rl_conf.ssl_cert_len +
rlay->rl_conf.ssl_key_len +
- rlay->rl_conf.ssl_ca_len)) {
+ rlay->rl_conf.ssl_ca_len +
+ rlay->rl_conf.ssl_cacert_len +
+ rlay->rl_conf.ssl_cakey_len)) {
log_debug("%s: invalid message length", __func__);
goto fail;
}
@@ -921,6 +934,18 @@ config_getrelay(struct relayd *env, struct imsg *imsg)
goto fail;
s += rlay->rl_conf.ssl_ca_len;
}
+ if (rlay->rl_conf.ssl_cacert_len) {
+ if ((rlay->rl_ssl_cacert = get_data(p + s,
+ rlay->rl_conf.ssl_cacert_len)) == NULL)
+ goto fail;
+ s += rlay->rl_conf.ssl_cacert_len;
+ }
+ if (rlay->rl_conf.ssl_cakey_len) {
+ if ((rlay->rl_ssl_cakey = get_data(p + s,
+ rlay->rl_conf.ssl_cakey_len)) == NULL)
+ goto fail;
+ s += rlay->rl_conf.ssl_cakey_len;
+ }
TAILQ_INIT(&rlay->rl_tables);
TAILQ_INSERT_TAIL(env->sc_relays, rlay, rl_entry);
diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y
index 7735d99409b..3d401dd2414 100644
--- a/usr.sbin/relayd/parse.y
+++ b/usr.sbin/relayd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.170 2013/04/27 16:39:30 benno Exp $ */
+/* $OpenBSD: parse.y,v 1.171 2013/05/30 20:17:12 reyk Exp $ */
/*
* Copyright (c) 2007-2011 Reyk Floeter <reyk@openbsd.org>
@@ -159,7 +159,7 @@ typedef struct {
%token RETURN ROUNDROBIN ROUTE SACK SCRIPT SEND SESSION SOCKET SPLICE
%token SSL STICKYADDR STYLE TABLE TAG TCP TIMEOUT TO ROUTER RTLABEL
%token TRANSPARENT TRAP UPDATES URL VIRTUAL WITH TTL RTABLE MATCH
-%token RANDOM LEASTSTATES SRCHASH
+%token RANDOM LEASTSTATES SRCHASH KEY CERTIFICATE PASSWORD
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.string> hostname interface table
@@ -980,6 +980,34 @@ sslflags : SESSION CACHE sslcache { proto->cache = $3; }
}
free($3);
}
+ | CA KEY STRING PASSWORD STRING {
+ if (strlcpy(proto->sslcakey, $3,
+ sizeof(proto->sslcakey)) >=
+ sizeof(proto->sslcakey)) {
+ yyerror("sslcakey truncated");
+ free($3);
+ free($5);
+ YYERROR;
+ }
+ if ((proto->sslcapass = strdup($5)) == NULL) {
+ yyerror("sslcapass");
+ free($3);
+ free($5);
+ YYERROR;
+ }
+ free($3);
+ free($5);
+ }
+ | CA CERTIFICATE STRING {
+ if (strlcpy(proto->sslcacert, $3,
+ sizeof(proto->sslcacert)) >=
+ sizeof(proto->sslcacert)) {
+ yyerror("sslcacert truncated");
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
| NO flag { proto->sslflags &= ~($2); }
| flag { proto->sslflags |= $1; }
;
@@ -1799,6 +1827,7 @@ lookup(char *s)
{ "buffer", BUFFER },
{ "ca", CA },
{ "cache", CACHE },
+ { "cert", CERTIFICATE },
{ "change", CHANGE },
{ "check", CHECK },
{ "ciphers", CIPHERS },
@@ -1825,6 +1854,7 @@ lookup(char *s)
{ "interface", INTERFACE },
{ "interval", INTERVAL },
{ "ip", IP },
+ { "key", KEY },
{ "label", LABEL },
{ "least-states", LEASTSTATES },
{ "listen", LISTEN },
@@ -1841,6 +1871,7 @@ lookup(char *s)
{ "nothing", NOTHING },
{ "on", ON },
{ "parent", PARENT },
+ { "password", PASSWORD },
{ "path", PATH },
{ "port", PORT },
{ "prefork", PREFORK },
diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c
index ef276ab2a1b..ca30f51daac 100644
--- a/usr.sbin/relayd/relay.c
+++ b/usr.sbin/relayd/relay.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: relay.c,v 1.165 2013/04/20 17:45:02 deraadt Exp $ */
+/* $OpenBSD: relay.c,v 1.166 2013/05/30 20:17:12 reyk Exp $ */
/*
- * Copyright (c) 2006 - 2012 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2006 - 2013 Reyk Floeter <reyk@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -1127,6 +1127,11 @@ relay_accept(int fd, short event, void *arg)
return;
}
+ if (rlay->rl_conf.flags & F_SSLINSPECT) {
+ relay_preconnect(con);
+ return;
+ }
+
relay_session(con);
return;
err:
@@ -1444,12 +1449,30 @@ relay_connect_retry(int fd, short sig, void *arg)
}
int
+relay_preconnect(struct rsession *con)
+{
+ log_debug("%s: session %d: process %d", __func__,
+ con->se_id, privsep_process);
+ return (relay_connect(con));
+}
+
+int
relay_connect(struct rsession *con)
{
struct relay *rlay = con->se_relay;
struct timeval evtpause = { 1, 0 };
int bnds = -1, ret;
+ /* Connection is already established but session not active */
+ if ((rlay->rl_conf.flags & F_SSLINSPECT) && con->se_out.s != -1) {
+ if (con->se_out.ssl == NULL) {
+ log_debug("%s: ssl connect failed", __func__);
+ return (-1);
+ }
+ relay_connected(con->se_out.s, EV_WRITE, con);
+ return (0);
+ }
+
if (relay_inflight < 1)
fatalx("relay_connect: no connection in flight");
@@ -1573,6 +1596,8 @@ relay_close(struct rsession *con, const char *msg)
SSL_shutdown(con->se_in.ssl);
SSL_free(con->se_in.ssl);
}
+ if (con->se_in.sslcert != NULL)
+ X509_free(con->se_in.sslcert);
if (con->se_in.s != -1) {
close(con->se_in.s);
if (con->se_out.s == -1) {
@@ -1602,6 +1627,8 @@ relay_close(struct rsession *con, const char *msg)
SSL_shutdown(con->se_out.ssl);
SSL_free(con->se_out.ssl);
}
+ if (con->se_out.sslcert != NULL)
+ X509_free(con->se_out.sslcert);
if (con->se_out.s != -1) {
close(con->se_out.s);
@@ -1903,6 +1930,10 @@ relay_ssl_transaction(struct rsession *con, struct ctl_relay_event *cre)
cb = relay_ssl_accept;
method = SSLv23_server_method();
flag = EV_READ;
+
+ /* Use session-specific certificate for SSL inspection. */
+ if (cre->sslcert != NULL)
+ SSL_use_certificate(ssl, cre->sslcert);
} else {
cb = relay_ssl_connect;
method = SSLv23_client_method();
@@ -2000,6 +2031,7 @@ relay_ssl_connect(int fd, short event, void *arg)
int retry_flag = 0;
int ssl_err = 0;
int ret;
+ X509 *servercert = NULL;
if (event == EV_TIMEOUT) {
relay_close(con, "SSL connect timeout");
@@ -2036,9 +2068,28 @@ relay_ssl_connect(int fd, short event, void *arg)
#else
log_debug(
#endif
- "relay %s, session %d connected (%d active)",
+ "relay %s, ssl session %d connected (%d active)",
rlay->rl_conf.name, con->se_id, relay_sessions);
+ if (rlay->rl_conf.flags & F_SSLINSPECT) {
+ if ((servercert =
+ SSL_get_peer_certificate(con->se_out.ssl)) != NULL) {
+ con->se_in.sslcert =
+ ssl_update_certificate(servercert,
+ rlay->rl_ssl_key, rlay->rl_conf.ssl_key_len,
+ rlay->rl_ssl_cakey, rlay->rl_conf.ssl_cakey_len,
+ rlay->rl_ssl_cacert, rlay->rl_conf.ssl_cacert_len);
+ } else
+ con->se_in.sslcert = NULL;
+ if (servercert != NULL)
+ X509_free(servercert);
+ if (con->se_in.sslcert == NULL)
+ relay_close(con, "could not create certificate");
+ else
+ relay_session(con);
+ return;
+ }
+
relay_connected(fd, EV_WRITE, con);
return;
@@ -2360,11 +2411,31 @@ relay_load_certfiles(struct relay *rlay)
struct protocol *proto = rlay->rl_proto;
int useport = htons(rlay->rl_conf.port);
- if ((rlay->rl_conf.flags & F_SSLCLIENT) && strlen(proto->sslca)) {
- if ((rlay->rl_ssl_ca = relay_load_file(proto->sslca,
- &rlay->rl_conf.ssl_ca_len)) == NULL)
- return (-1);
- log_debug("%s: using ca %s", __func__, proto->sslca);
+ if (rlay->rl_conf.flags & F_SSLCLIENT) {
+ if (strlen(proto->sslca)) {
+ if ((rlay->rl_ssl_ca =
+ relay_load_file(proto->sslca,
+ &rlay->rl_conf.ssl_ca_len)) == NULL)
+ return (-1);
+ log_debug("%s: using ca %s", __func__, proto->sslca);
+ }
+ if (strlen(proto->sslcacert)) {
+ if ((rlay->rl_ssl_cacert =
+ relay_load_file(proto->sslcacert,
+ &rlay->rl_conf.ssl_cacert_len)) == NULL)
+ return (-1);
+ log_debug("%s: using ca certificate %s", __func__,
+ proto->sslcacert);
+ }
+ if (strlen(proto->sslcakey) && proto->sslcapass != NULL) {
+ if ((rlay->rl_ssl_cakey =
+ ssl_load_key(env, proto->sslcakey,
+ &rlay->rl_conf.ssl_cakey_len,
+ proto->sslcapass)) == NULL)
+ return (-1);
+ log_debug("%s: using ca key %s", __func__,
+ proto->sslcakey);
+ }
}
if ((rlay->rl_conf.flags & F_SSL) == 0)
@@ -2397,8 +2468,8 @@ relay_load_certfiles(struct relay *rlay)
"/etc/ssl/private/%s.key", hbuf) == -1)
return -1;
}
- if ((rlay->rl_ssl_key = relay_load_file(certfile,
- &rlay->rl_conf.ssl_key_len)) == NULL)
+ if ((rlay->rl_ssl_key = ssl_load_key(env, certfile,
+ &rlay->rl_conf.ssl_key_len, NULL)) == NULL)
return (-1);
log_debug("%s: using private key %s", __func__, certfile);
diff --git a/usr.sbin/relayd/relayd.c b/usr.sbin/relayd/relayd.c
index ce09304e7a1..8be99615419 100644
--- a/usr.sbin/relayd/relayd.c
+++ b/usr.sbin/relayd/relayd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: relayd.c,v 1.116 2013/03/10 23:32:53 reyk Exp $ */
+/* $OpenBSD: relayd.c,v 1.117 2013/05/30 20:17:12 reyk Exp $ */
/*
* Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
@@ -290,8 +290,16 @@ parent_configure(struct relayd *env)
config_setrt(env, rt);
TAILQ_FOREACH(proto, env->sc_protos, entry)
config_setproto(env, proto);
- TAILQ_FOREACH(rlay, env->sc_relays, rl_entry)
+ TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
+ /* Check for SSL Inspection */
+ if ((rlay->rl_conf.flags & (F_SSL|F_SSLCLIENT)) ==
+ (F_SSL|F_SSLCLIENT) &&
+ rlay->rl_conf.ssl_cacert_len &&
+ rlay->rl_conf.ssl_cakey_len)
+ rlay->rl_conf.flags |= F_SSLINSPECT;
+
config_setrelay(env, rlay);
+ }
/* HCE, PFE and the preforked relays need to reload their config. */
env->sc_reload = 2 + env->sc_prefork_relay;
diff --git a/usr.sbin/relayd/relayd.conf.5 b/usr.sbin/relayd/relayd.conf.5
index 3f0cc4e9b6d..93956c91a96 100644
--- a/usr.sbin/relayd/relayd.conf.5
+++ b/usr.sbin/relayd/relayd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: relayd.conf.5,v 1.133 2013/04/27 16:39:30 benno Exp $
+.\" $OpenBSD: relayd.conf.5,v 1.134 2013/05/30 20:17:12 reyk Exp $
.\"
.\" Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org>
.\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: April 27 2013 $
+.Dd $Mdocdate: May 30 2013 $
.Dt RELAYD.CONF 5
.Os
.Sh NAME
@@ -680,6 +680,83 @@ Specify the inactivity timeout in seconds for accepted sessions.
The default timeout is 600 seconds (10 minutes).
The maximum is 2147483647 seconds (68 years).
.El
+.Sh SSL RELAYS
+In addition to plain TCP,
+.Xr relayd 8
+supports the Secure Sockets Layer (SSL) and Transport Layer Security
+(TLS) cryptographic protocols for authenticated and encrypted relays.
+TLS is the successor of the original SSL protocol but the term SSL can
+refer to either of the protocols in
+.Nm .
+.Xr relayd 8
+can operate as an SSL client or server to offer a variety of options
+for different use cases related to SSL.
+.Bl -tag -width Ds
+.It Ic SSL client
+When configuring the relay
+.Ic forward
+statements with the
+.Ic with ssl
+directive,
+.Xr relayd 8
+will enable client-side SSL to connect to the remote host.
+This is commonly used for SSL tunneling and transparent encapsulation
+of plain TCP connections.
+See the
+.Ic forward to
+description in the
+.Sx RELAYS
+section for more details.
+.It Ic SSL server
+When specifying the
+.Ic ssl
+keyword in the relay
+.Ic listen
+statements,
+.Xr relayd 8
+will accept connections from clients as an SSL server.
+This mode is also known as
+.Dq SSL acceleration .
+See the
+.Ic listen on
+description in the
+.Sx RELAYS
+section for more details.
+.It Ic SSL client and server
+When combining both modes, SSL server and client,
+.Xr relayd 8
+can filter SSL connections as a man-in-the-middle.
+This combined mode is also called
+.Dq SSL inspection .
+The configuration requires additional X.509 certificate settings;
+see the
+.Ic ca key
+description in the
+.Sx PROTOCOLS
+section for more details.
+.El
+.Pp
+When configured for
+.Dq SSL inspection
+mode,
+.Xr relayd 8
+will listen for incoming connections which have been diverted to the
+local socket by PF.
+Before accepting and negotiating the incoming SSL connection as a
+server, it will look up the original destination address on the
+diverted socket, and pre-connect to the target server as an SSL client
+to obtain the remote SSL certificate.
+It will update or patch the obtained SSL certificate by replacing the
+included public key with its local server key because it doesn't have
+the private key of the remote server certificate.
+It also updates the X.509 issuer name to the local CA subject name and
+signs the certificate with its local CA key.
+This way it keeps all the other X.509 attributes that are already
+present in the server certificate, including the "green bar" extended
+validation attributes.
+Now it finally accepts the SSL connection from the diverted client
+using the updated certificate and continues to handle the connection
+and to connect to the remote server.
.Sh PROTOCOLS
Protocols are templates defining actions and settings for relays.
They allow setting generic TCP options, SSL settings, and actions
@@ -1008,6 +1085,11 @@ Set the SSL options and session settings.
This is only used if SSL is enabled in the relay.
Valid options are:
.Bl -tag -width Ds
+.It Ic ca cert Ar path
+Specify a CA certificate for SSL inspection.
+For more information, see the
+.Ic ca key
+option below.
.It Ic ca file Ar path
This option enables CA verification in SSL client mode.
The daemon will load the CA (Certificate Authority) certificates from
@@ -1015,6 +1097,35 @@ the specified path to verify the server certificates.
.Ox
provides a default CA bundle in
.Pa /etc/ssl/cert.pem .
+.It Ic ca key Ar path Ic password Ar password
+Specify a CA key for SSL inspection.
+The
+.Ar password
+argument will specify the password to decrypt the CA key
+(typically an RSA key).
+This option will enable SSL inspection if the following conditions
+are true:
+.Pp
+.Bl -bullet -compact -offset indent
+.It
+SSL client mode is enabled by the
+.Ic listen
+directive:
+.Ic listen on ... ssl .
+.It
+SSL server mode and divert lookups are enabled by the
+.Ic forward
+directive:
+.Ic forward with ssl to destination .
+.It
+The
+.Ic ca cert
+option is specified.
+.It
+The
+.Ic ca key
+option is specified.
+.El
.It Ic ciphers Ar string
Set the string defining the SSL cipher suite.
If not specified, the default value
@@ -1270,6 +1381,52 @@ relay "sshforward" {
}
.Ed
.Pp
+The following relay example will configure
+.Dq SSL inspection
+as described in the
+.Sx SSL RELAYS
+section.
+To start, first generate a new local CA key and certificate:
+.Bd -literal -offset indent
+# openssl req -x509 -days 365 -newkey rsa:2048 \e
+ -keyout /etc/ssl/private/ca.key -out /etc/ssl/ca.crt
+.Ed
+.Pp
+An SSL server key and self-signed cert for 127.0.0.1 are also required;
+see
+.Ic listen on
+in the
+.Sx RELAYS
+section for more details about certificate locations.
+Configure the packet filter with a matching divert rule in
+.Xr pf.conf 5 :
+.Bd -literal -offset indent
+# Divert incoming HTTPS traffic to relayd
+pass in on vlan1 inet proto tcp to port 443 \e
+ divert-to localhost port 8443
+.Ed
+.Pp
+And finally configure the SSL inspection in
+.Nm :
+.Bd -literal -offset indent
+http protocol httpfilter {
+ return error
+
+ label "Prohibited!"
+ request url filter "social.network.example.com/"
+
+ # New configuration directives for SSL Interception
+ ssl ca key "/etc/ssl/private/ca.key" password "password123"
+ ssl ca cert "/etc/ssl/ca.crt"
+}
+
+relay sslinspect {
+ listen on 127.0.0.1 port 8443 ssl
+ protocol httpfilter
+ forward with ssl to destination
+}
+.Ed
+.Pp
The next simple router configuration example can be used to run
redundant, health-checked WAN links:
.Bd -literal -offset indent
diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h
index 85934cb1b2e..92315134cbb 100644
--- a/usr.sbin/relayd/relayd.h
+++ b/usr.sbin/relayd/relayd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: relayd.h,v 1.168 2013/04/27 16:39:30 benno Exp $ */
+/* $OpenBSD: relayd.h,v 1.169 2013/05/30 20:17:12 reyk Exp $ */
/*
* Copyright (c) 2006 - 2012 Reyk Floeter <reyk@openbsd.org>
@@ -47,6 +47,7 @@
#define SRV_NAME_SIZE 64
#define MAX_NAME_SIZE 64
#define SRV_MAX_VIRTS 16
+#define SSL_NAME_SIZE 512
#define FD_RESERVE 5
@@ -77,6 +78,7 @@
#if DEBUG > 1
#define DPRINTF log_debug
+#define DEBUG_CERT 1
#else
#define DPRINTF(x...) do {} while(0)
#endif
@@ -177,6 +179,7 @@ struct ctl_relay_event {
struct ctl_relay_event *dst;
struct rsession *con;
SSL *ssl;
+ X509 *sslcert;
u_int8_t *nodes;
struct proto_tree *tree;
@@ -282,6 +285,7 @@ TAILQ_HEAD(addresslist, address);
#define F_MATCH 0x00800000
#define F_DIVERT 0x01000000
#define F_SCRIPT 0x02000000
+#define F_SSLINSPECT 0x04000000
#define F_BITS \
"\10\01DISABLE\02BACKUP\03USED\04DOWN\05ADD\06DEL\07CHANGED" \
@@ -455,6 +459,7 @@ struct rsession {
int se_done;
int se_retry;
int se_retrycount;
+ int se_connectcount;
u_int16_t se_mark;
struct evbuffer *se_log;
struct relay *se_relay;
@@ -576,6 +581,9 @@ struct protocol {
u_int8_t sslflags;
char sslciphers[768];
char sslca[MAXPATHLEN];
+ char sslcacert[MAXPATHLEN];
+ char sslcakey[MAXPATHLEN];
+ char *sslcapass;
char name[MAX_NAME_SIZE];
int cache;
enum prototype type;
@@ -625,6 +633,8 @@ struct relay_config {
off_t ssl_cert_len;
off_t ssl_key_len;
off_t ssl_ca_len;
+ off_t ssl_cacert_len;
+ off_t ssl_cakey_len;
};
struct relay {
@@ -648,6 +658,8 @@ struct relay {
char *rl_ssl_cert;
char *rl_ssl_key;
char *rl_ssl_ca;
+ char *rl_ssl_cacert;
+ char *rl_ssl_cakey;
struct ctl_stats rl_stats[RELAY_MAXPROC + 1];
@@ -1001,6 +1013,7 @@ int relay_spliceadjust(struct ctl_relay_event *);
void relay_error(struct bufferevent *, short, void *);
int relay_lognode(struct rsession *,
struct protonode *, struct protonode *, char *, size_t);
+int relay_preconnect(struct rsession *);
int relay_connect(struct rsession *);
void relay_connected(int, short, void *);
void relay_bindanyreq(struct rsession *, in_port_t, int);
@@ -1048,6 +1061,9 @@ void ssl_init(struct relayd *);
void ssl_transaction(struct ctl_tcp_event *);
SSL_CTX *ssl_ctx_create(struct relayd *);
void ssl_error(const char *, const char *);
+char *ssl_load_key(struct relayd *, const char *, off_t *, char *);
+X509 *ssl_update_certificate(X509 *, char *, off_t,
+ char *, off_t, char *, off_t);
/* ssl_privsep.c */
int ssl_ctx_use_private_key(SSL_CTX *, char *, off_t);
diff --git a/usr.sbin/relayd/ssl.c b/usr.sbin/relayd/ssl.c
index b5bb33e1563..4a7770c3396 100644
--- a/usr.sbin/relayd/ssl.c
+++ b/usr.sbin/relayd/ssl.c
@@ -1,6 +1,7 @@
-/* $OpenBSD: ssl.c,v 1.17 2012/12/18 15:34:07 reyk Exp $ */
+/* $OpenBSD: ssl.c,v 1.18 2013/05/30 20:17:12 reyk Exp $ */
/*
+ * Copyright (c) 2007-2013 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -41,6 +42,7 @@ void ssl_read(int, short, void *);
void ssl_write(int, short, void *);
void ssl_connect(int, short, void *);
void ssl_cleanup(struct ctl_tcp_event *);
+int ssl_password_cb(char *, int, int, void *);
void
ssl_read(int s, short event, void *arg)
@@ -240,12 +242,19 @@ ssl_error(const char *where, const char *what)
void
ssl_init(struct relayd *env)
{
+ static int initialized = 0;
+
+ if (initialized)
+ return;
+
SSL_library_init();
SSL_load_error_strings();
/* Init hardware crypto engines. */
ENGINE_load_builtin_engines();
ENGINE_register_all_complete();
+
+ initialized = 1;
}
void
@@ -284,3 +293,140 @@ ssl_ctx_create(struct relayd *env)
}
return (ctx);
}
+
+int
+ssl_password_cb(char *buf, int size, int rwflag, void *u)
+{
+ size_t len;
+ if (u == NULL) {
+ bzero(buf, size);
+ return (0);
+ }
+ if ((len = strlcpy(buf, u, size)) >= (size_t)size)
+ return (0);
+ return (len);
+}
+
+char *
+ssl_load_key(struct relayd *env, const char *name, off_t *len, char *pass)
+{
+ FILE *fp;
+ EVP_PKEY *key = NULL;
+ BIO *bio = NULL;
+ long size;
+ char *data, *buf = NULL;
+
+ /* Initialize SSL library once */
+ ssl_init(env);
+
+ /*
+ * Read (possibly) encrypted key from file
+ */
+ if ((fp = fopen(name, "r")) == NULL)
+ return (NULL);
+
+ key = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, pass);
+ fclose(fp);
+ if (key == NULL)
+ goto fail;
+
+ /*
+ * Write unencrypted key to memory buffer
+ */
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ goto fail;
+ if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL))
+ goto fail;
+ if ((size = BIO_get_mem_data(bio, &data)) <= 0)
+ goto fail;
+ if ((buf = calloc(1, size)) == NULL)
+ goto fail;
+ memcpy(buf, data, size);
+
+ BIO_free_all(bio);
+ *len = (off_t)size;
+ return (buf);
+
+ fail:
+ ssl_error(__func__, name);
+
+ free(buf);
+ if (bio != NULL)
+ BIO_free_all(bio);
+ return (NULL);
+}
+
+X509 *
+ssl_update_certificate(X509 *oldcert, char *keystr, off_t keylen,
+ char *cakeystr, off_t cakeylen, char *cacertstr, off_t cacertlen)
+{
+ char name[2][SSL_NAME_SIZE];
+ X509 *cert = NULL, *cacert = NULL;
+ EVP_PKEY *key = NULL, *cakey = NULL;
+ BIO *bio = NULL;
+
+ name[0][0] = name[1][0] = '\0';
+ if (!X509_NAME_oneline(X509_get_subject_name(oldcert),
+ name[0], sizeof(name[0])) ||
+ !X509_NAME_oneline(X509_get_issuer_name(oldcert),
+ name[1], sizeof(name[1])))
+ goto done;
+
+ /* Get SSL key */
+ if ((bio = BIO_new_mem_buf(keystr, keylen)) == NULL)
+ goto done;
+ if ((key = PEM_read_bio_PrivateKey(bio, &key,
+ ssl_password_cb, NULL)) == NULL)
+ goto done;
+
+ /* Get CA key */
+ BIO_free_all(bio);
+ if ((bio = BIO_new_mem_buf(cakeystr, cakeylen)) == NULL)
+ goto done;
+ if ((cakey = PEM_read_bio_PrivateKey(bio, &cakey,
+ ssl_password_cb, NULL)) == NULL)
+ goto done;
+
+ /* Get CA certificate */
+ BIO_free_all(bio);
+ if ((bio = BIO_new_mem_buf(cacertstr, cacertlen)) == NULL)
+ goto done;
+ if ((cacert = PEM_read_bio_X509(bio, &cacert,
+ ssl_password_cb, NULL)) == NULL)
+ goto done;
+
+ if ((cert = X509_dup(oldcert)) == NULL)
+ goto done;
+
+ /* Update certificate key and use our CA as the issuer */
+ X509_set_pubkey(cert, key);
+ X509_set_issuer_name(cert, X509_get_subject_name(cacert));
+
+ /* Sign with our CA */
+ if (!X509_sign(cert, cakey, EVP_sha1())) {
+ X509_free(cert);
+ cert = NULL;
+ }
+
+#if DEBUG_CERT
+ log_debug("%s: subject %s", __func__, name[0]);
+ log_debug("%s: issuer %s", __func__, name[1]);
+#if DEBUG > 2
+ X509_print_fp(stdout, cert);
+#endif
+#endif
+
+ done:
+ if (cert == NULL)
+ ssl_error(__func__, name[0]);
+ if (bio != NULL)
+ BIO_free_all(bio);
+ if (key != NULL)
+ EVP_PKEY_free(key);
+ if (cacert != NULL)
+ X509_free(cacert);
+ if (cakey != NULL)
+ EVP_PKEY_free(cakey);
+
+ return (cert);
+}