diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2009-04-01 14:56:39 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2009-04-01 14:56:39 +0000 |
commit | 32e2b02a8722eff69b6df3287982cd6803e9add8 (patch) | |
tree | 2380fcda7707d6cd54beb5eff3b17f5e1723c503 | |
parent | 4bd0a5bb0865c3233a6bfae144c72921458f4594 (diff) |
Add support for client-side SSL connections from relays. relayd can
now sit between two SSL connections (Oitm - OpenBSD-in-the-middle),
accept SSL connections and forward to TCP, accept TCP connections and
forward to SSL, and do TCP to TCP of course.
This was tested by some people a while ago.
-rw-r--r-- | usr.sbin/relayd/parse.y | 55 | ||||
-rw-r--r-- | usr.sbin/relayd/relay.c | 117 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.conf.5 | 19 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.h | 3 |
4 files changed, 154 insertions, 40 deletions
diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y index 8746e066c6d..6e2387f1771 100644 --- a/usr.sbin/relayd/parse.y +++ b/usr.sbin/relayd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.128 2009/03/31 21:03:49 tobias Exp $ */ +/* $OpenBSD: parse.y,v 1.129 2009/04/01 14:56:38 reyk Exp $ */ /* * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org> @@ -140,8 +140,9 @@ typedef struct { %token <v.string> STRING %token <v.number> NUMBER %type <v.string> hostname interface table -%type <v.number> http_type loglevel mark optssl parent sslcache +%type <v.number> http_type loglevel mark parent %type <v.number> direction dstmode flag forwardmode proto_type retry +%type <v.number> optssl optsslclient sslcache %type <v.port> port %type <v.host> host %type <v.tv> timeout @@ -180,6 +181,10 @@ optssl : /*empty*/ { $$ = 0; } | SSL { $$ = 1; } ; +optsslclient : /*empty*/ { $$ = 0; } + | WITH SSL { $$ = 1; } + ; + http_type : STRING { if (strcmp("https", $1) == 0) { $$ = 1; @@ -573,7 +578,7 @@ tableopts_l : tableopts tableopts_l | tableopts ; -tableopts : CHECK tablecheck +tableopts : CHECK tablecheck | port { if ($1.op != PF_OP_EQ) { yyerror("invalid port"); @@ -1217,11 +1222,11 @@ relayoptsl : LISTEN ON STRING port optssl { } tableport = h->port.val[0]; } - | forwardmode TO forwardspec interface dstaf { + | forwardmode optsslclient TO forwardspec interface dstaf { rlay->rl_conf.fwdmode = $1; switch ($1) { case FWD_NORMAL: - if ($4 == NULL) + if ($5 == NULL) break; yyerror("superfluous interface"); YYERROR; @@ -1229,15 +1234,19 @@ relayoptsl : LISTEN ON STRING port optssl { yyerror("no route for redirections"); YYERROR; case FWD_TRANS: - if ($4 != NULL) + if ($5 != NULL) break; yyerror("missing interface"); YYERROR; } - if ($4 != NULL) { - strlcpy(rlay->rl_conf.ifname, $4, + if ($5 != NULL) { + strlcpy(rlay->rl_conf.ifname, $5, sizeof(rlay->rl_conf.ifname)); - free($4); + free($5); + } + if ($2) { + rlay->rl_conf.flags |= F_SSLCLIENT; + conf->sc_flags |= F_SSLCLIENT; } } | SESSION TIMEOUT NUMBER { @@ -1266,19 +1275,7 @@ relayoptsl : LISTEN ON STRING port optssl { | include ; -forwardspec : tablespec { - if (rlay->rl_dsttable) { - yyerror("table already specified"); - purge_table(conf->sc_tables, $1); - YYERROR; - } - - rlay->rl_dsttable = $1; - rlay->rl_dsttable->conf.flags |= F_USED; - rlay->rl_conf.dsttable = $1->conf.id; - rlay->rl_conf.dstport = $1->conf.port; - } - | STRING port retry { +forwardspec : STRING port retry { struct addresslist al; struct address *h; @@ -1307,11 +1304,23 @@ forwardspec : tablespec { rlay->rl_conf.dstport = h->port.val[0]; rlay->rl_conf.dstretry = $3; } - | NAT LOOKUP retry { + | NAT LOOKUP retry { conf->sc_flags |= F_NEEDPF; rlay->rl_conf.flags |= F_NATLOOK; rlay->rl_conf.dstretry = $3; } + | tablespec { + if (rlay->rl_dsttable) { + yyerror("table already specified"); + purge_table(conf->sc_tables, $1); + YYERROR; + } + + rlay->rl_dsttable = $1; + rlay->rl_dsttable->conf.flags |= F_USED; + rlay->rl_conf.dsttable = $1->conf.id; + rlay->rl_conf.dstport = $1->conf.port; + } ; dstmode : /* empty */ { $$ = RELAY_DSTMODE_DEFAULT; } diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c index 77451fc9352..a0b45fa4803 100644 --- a/usr.sbin/relayd/relay.c +++ b/usr.sbin/relayd/relay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay.c,v 1.107 2008/09/29 15:50:56 reyk Exp $ */ +/* $OpenBSD: relay.c,v 1.108 2009/04/01 14:56:38 reyk Exp $ */ /* * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org> @@ -103,8 +103,10 @@ void relay_close_http(struct session *, u_int, const char *, u_int16_t); SSL_CTX *relay_ssl_ctx_create(struct relay *); -void relay_ssl_transaction(struct session *); +void relay_ssl_transaction(struct session *, + struct ctl_relay_event *); void relay_ssl_accept(int, short, void *); +void relay_ssl_connect(int, short, void *); void relay_ssl_connected(struct ctl_relay_event *); void relay_ssl_readcb(int, short, void *); void relay_ssl_writecb(int, short, void *); @@ -408,7 +410,7 @@ relay_privinit(void) struct relay *rlay; extern int debug; - if (env->sc_flags & F_SSL) + if (env->sc_flags & (F_SSL|F_SSLCLIENT)) ssl_init(env); TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) { @@ -460,7 +462,7 @@ relay_init(void) fatal("relay_init: failed to set resource limit"); TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) { - if ((rlay->rl_conf.flags & F_SSL) && + if ((rlay->rl_conf.flags & (F_SSL|F_SSLCLIENT)) && (rlay->rl_ssl_ctx = relay_ssl_ctx_create(rlay)) == NULL) fatal("relay_init: failed to create SSL context"); @@ -749,12 +751,18 @@ relay_connected(int fd, short sig, void *arg) evbuffercb outrd = relay_read; evbuffercb outwr = relay_write; struct bufferevent *bev; + struct ctl_relay_event *out = &con->se_out; if (sig == EV_TIMEOUT) { relay_close_http(con, 504, "connect timeout", 0); return; } + if ((rlay->rl_conf.flags & F_SSLCLIENT) && (out->ssl == NULL)) { + relay_ssl_transaction(con, out); + return; + } + DPRINTF("relay_connected: session %d: %ssuccessful", con->se_id, rlay->rl_proto->lateconnect ? "late connect " : ""); @@ -791,8 +799,12 @@ relay_connected(int fd, short sig, void *arg) bev->output = con->se_out.output; if (bev->output == NULL) fatal("relay_connected: invalid output buffer"); - con->se_out.bev = bev; + + /* Initialize the SSL wrapper */ + if ((rlay->rl_conf.flags & F_SSLCLIENT) && (out->ssl != NULL)) + relay_ssl_connected(out); + bufferevent_settimeout(bev, rlay->rl_conf.timeout.tv_sec, rlay->rl_conf.timeout.tv_sec); bufferevent_enable(bev, EV_READ|EV_WRITE); @@ -2179,7 +2191,7 @@ relay_session(struct session *con) } if ((rlay->rl_conf.flags & F_SSL) && (in->ssl == NULL)) { - relay_ssl_transaction(con); + relay_ssl_transaction(con, in); return; } @@ -2603,6 +2615,9 @@ relay_ssl_ctx_create(struct relay *rlay) if (!SSL_CTX_set_cipher_list(ctx, proto->sslciphers)) goto err; + if ((rlay->rl_conf.flags & F_SSL) == 0) + return (ctx); + log_debug("relay_ssl_ctx_create: loading certificate"); if (!ssl_ctx_use_certificate_chain(ctx, rlay->rl_ssl_cert, rlay->rl_ssl_cert_len)) @@ -2630,31 +2645,49 @@ relay_ssl_ctx_create(struct relay *rlay) } void -relay_ssl_transaction(struct session *con) +relay_ssl_transaction(struct session *con, struct ctl_relay_event *cre) { struct relay *rlay = (struct relay *)con->se_relay; SSL *ssl; + SSL_METHOD *method; + void (*cb)(int, short, void *); + u_int flags = EV_TIMEOUT; ssl = SSL_new(rlay->rl_ssl_ctx); if (ssl == NULL) goto err; - if (!SSL_set_ssl_method(ssl, SSLv23_server_method())) + if (cre->dir == RELAY_DIR_REQUEST) { + cb = relay_ssl_accept; + method = SSLv23_server_method(); + flags |= EV_READ; + } else { + cb = relay_ssl_connect; + method = SSLv23_client_method(); + flags |= EV_WRITE; + } + + if (!SSL_set_ssl_method(ssl, method)) goto err; - if (!SSL_set_fd(ssl, con->se_in.s)) + if (!SSL_set_fd(ssl, cre->s)) goto err; - SSL_set_accept_state(ssl); - con->se_in.ssl = ssl; + if (cre->dir == RELAY_DIR_REQUEST) + SSL_set_accept_state(ssl); + else + SSL_set_connect_state(ssl); + + cre->ssl = ssl; - event_again(&con->se_ev, con->se_in.s, EV_TIMEOUT|EV_READ, - relay_ssl_accept, &con->se_tv_start, &env->sc_timeout, con); + event_again(&con->se_ev, cre->s, EV_TIMEOUT|flags, + cb, &con->se_tv_start, &env->sc_timeout, con); return; err: if (ssl != NULL) SSL_free(ssl); ssl_error(rlay->rl_conf.name, "relay_ssl_transaction"); + relay_close(con, "session ssl failed"); } void @@ -2717,6 +2750,64 @@ retry: } void +relay_ssl_connect(int fd, short event, void *arg) +{ + struct session *con = (struct session *)arg; + struct relay *rlay = (struct relay *)con->se_relay; + int ret; + int ssl_err; + int retry_flag; + + if (event == EV_TIMEOUT) { + relay_close(con, "SSL connect timeout"); + return; + } + + retry_flag = ssl_err = 0; + + ret = SSL_connect(con->se_out.ssl); + if (ret <= 0) { + ssl_err = SSL_get_error(con->se_out.ssl, ret); + + switch (ssl_err) { + case SSL_ERROR_WANT_READ: + retry_flag = EV_READ; + goto retry; + case SSL_ERROR_WANT_WRITE: + retry_flag = EV_WRITE; + goto retry; + case SSL_ERROR_ZERO_RETURN: + case SSL_ERROR_SYSCALL: + if (ret == 0) { + relay_close(con, "closed"); + return; + } + /* FALLTHROUGH */ + default: + ssl_error(rlay->rl_conf.name, "relay_ssl_connect"); + relay_close(con, "SSL connect error"); + return; + } + } + +#ifdef DEBUG + log_info("relay %s, session %d connected (%d active)", + rlay->rl_conf.name, con->se_id, relay_sessions); +#else + log_debug("relay %s, session %d connected (%d active)", + rlay->rl_conf.name, con->se_id, relay_sessions); +#endif + relay_connected(fd, EV_WRITE, con); + return; + +retry: + DPRINTF("relay_ssl_connect: session %d: scheduling on %s", con->se_id, + (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE"); + event_again(&con->se_ev, fd, EV_TIMEOUT|retry_flag, relay_ssl_connect, + &con->se_tv_start, &env->sc_timeout, con); +} + +void relay_ssl_connected(struct ctl_relay_event *cre) { /* diff --git a/usr.sbin/relayd/relayd.conf.5 b/usr.sbin/relayd/relayd.conf.5 index ae8daac65fb..3a08596fd07 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.100 2009/02/16 19:46:12 jmc Exp $ +.\" $OpenBSD: relayd.conf.5,v 1.101 2009/04/01 14:56:38 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: February 16 2009 $ +.Dd $Mdocdate: April 1 2009 $ .Dt RELAYD.CONF 5 .Os .Sh NAME @@ -500,7 +500,9 @@ configuration directives are described below: Start the relay but immediately close any accepted connections. .It Xo .Op Ic transparent -.Ic forward to +.Ic forward +.Op Ic with ssl +.Ic to .Ar address .Op Ic port Ar port .Ar options ... @@ -516,6 +518,13 @@ Use the keyword to enable fully-transparent mode; the source address of the client will be retained in this case. .Pp +The +.Ic with ssl +directive enables client-side SSL mode to connect to the remote host. +Note that +.Xr relayd 8 +will not verify the remote SSL certificate. +.Pp The following options may be specified for forward directives: .Pp .Bl -tag -width Ds @@ -1131,3 +1140,7 @@ program was written by .An Pierre-Yves Ritschard Aq pyr@openbsd.org and .An Reyk Floeter Aq reyk@openbsd.org . +.Sh CAVEATS +.Xr relayd 8 +does not support verification of server certificates when connecting +to a remote host using the SSL protocol. diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index 2eb76fc4e75..5a0813250da 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.113 2008/12/05 16:37:56 reyk Exp $ */ +/* $OpenBSD: relayd.h,v 1.114 2009/04/01 14:56:38 reyk Exp $ */ /* * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -344,6 +344,7 @@ TAILQ_HEAD(addresslist, address); #define F_TRAP 0x00040000 #define F_NEEDPF 0x00080000 #define F_PORT 0x00100000 +#define F_SSLCLIENT 0x00200000 enum forwardmode { FWD_NORMAL = 0, |