diff options
34 files changed, 914 insertions, 56 deletions
diff --git a/usr.sbin/hoststatectl/hoststatectl.c b/usr.sbin/hoststatectl/hoststatectl.c index 5119d4e6898..8ef62fff0e2 100644 --- a/usr.sbin/hoststatectl/hoststatectl.c +++ b/usr.sbin/hoststatectl/hoststatectl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hoststatectl.c,v 1.8 2007/01/09 13:50:10 pyr Exp $ */ +/* $OpenBSD: hoststatectl.c,v 1.9 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -36,6 +36,8 @@ #include <unistd.h> #include <event.h> +#include <openssl/ssl.h> + #include "hoststated.h" #include "parser.h" diff --git a/usr.sbin/hoststatectl/parser.c b/usr.sbin/hoststatectl/parser.c index 708fee48bc0..aca5a16c9e9 100644 --- a/usr.sbin/hoststatectl/parser.c +++ b/usr.sbin/hoststatectl/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.6 2007/01/29 10:28:11 claudio Exp $ */ +/* $OpenBSD: parser.c,v 1.7 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -32,6 +32,8 @@ #include <string.h> #include <event.h> +#include <openssl/ssl.h> + #include "hoststated.h" #include "parser.h" diff --git a/usr.sbin/hoststated/Makefile b/usr.sbin/hoststated/Makefile index 2cf8d3a13c1..b619932c623 100644 --- a/usr.sbin/hoststated/Makefile +++ b/usr.sbin/hoststated/Makefile @@ -1,13 +1,13 @@ -# $OpenBSD: Makefile,v 1.4 2007/01/09 02:32:58 reyk Exp $ +# $OpenBSD: Makefile,v 1.5 2007/01/29 14:23:31 pyr Exp $ PROG= hoststated SRCS= parse.y log.c control.c buffer.c imsg.c hoststated.c \ - pfe.c pfe_filter.c hce.c \ + ssl.c pfe.c pfe_filter.c hce.c \ check_icmp.c check_tcp.c check_http.c check_send_expect.c MAN= hoststated.8 hoststated.conf.5 -LDADD= -levent -DPADD= ${LIBEVENT} +LDADD= -levent -lcrypto -lssl +DPADD= ${LIBEVENT} ${LIBCRYPTO} ${LIBSSL} CFLAGS+= -Wall -Werror -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations diff --git a/usr.sbin/hoststated/buffer.c b/usr.sbin/hoststated/buffer.c index 7133094ddfc..4d85f78ea57 100644 --- a/usr.sbin/hoststated/buffer.c +++ b/usr.sbin/hoststated/buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: buffer.c,v 1.4 2007/01/09 00:45:32 deraadt Exp $ */ +/* $OpenBSD: buffer.c,v 1.5 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -30,6 +30,8 @@ #include <string.h> #include <unistd.h> +#include <openssl/ssl.h> + #include "hoststated.h" int buf_realloc(struct buf *, size_t); diff --git a/usr.sbin/hoststated/check_http.c b/usr.sbin/hoststated/check_http.c index ea851233dfd..47146c13b11 100644 --- a/usr.sbin/hoststated/check_http.c +++ b/usr.sbin/hoststated/check_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: check_http.c,v 1.10 2007/01/12 16:43:01 pyr Exp $ */ +/* $OpenBSD: check_http.c,v 1.11 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> * @@ -31,6 +31,8 @@ #include <fcntl.h> #include <errno.h> +#include <openssl/ssl.h> + #include "hoststated.h" int @@ -39,8 +41,17 @@ check_http_code(struct ctl_tcp_event *cte) char *head; char scode[4]; const char *estr; + u_char *b; int code; + /* + * ensure string is nul-terminated. + */ + b = buf_reserve(cte->buf, 1); + if (b == NULL) + fatal("out of memory"); + *b = '\0'; + head = cte->buf->buf; if (strncmp(head, "HTTP/1.1 ", strlen("HTTP/1.1 ")) && strncmp(head, "HTTP/1.0 ", strlen("HTTP/1.0 "))) { @@ -72,8 +83,17 @@ int check_http_digest(struct ctl_tcp_event *cte) { char *head; + u_char *b; char digest[(SHA1_DIGEST_LENGTH*2)+1]; + /* + * ensure string is nul-terminated. + */ + b = buf_reserve(cte->buf, 1); + if (b == NULL) + fatal("out of memory"); + *b = '\0'; + head = cte->buf->buf; if ((head = strstr(head, "\r\n\r\n")) == NULL) { log_debug("check_http_digest: host %u no end of headers", diff --git a/usr.sbin/hoststated/check_icmp.c b/usr.sbin/hoststated/check_icmp.c index f4167359a0e..ce83f7de214 100644 --- a/usr.sbin/hoststated/check_icmp.c +++ b/usr.sbin/hoststated/check_icmp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: check_icmp.c,v 1.11 2007/01/12 17:12:58 pyr Exp $ */ +/* $OpenBSD: check_icmp.c,v 1.12 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -35,6 +35,8 @@ #include <stdlib.h> #include <err.h> +#include <openssl/ssl.h> + #include "hoststated.h" void icmp_setup(struct hoststated *, struct ctl_icmp_event *, int); diff --git a/usr.sbin/hoststated/check_send_expect.c b/usr.sbin/hoststated/check_send_expect.c index f569bf43091..7c727f3de69 100644 --- a/usr.sbin/hoststated/check_send_expect.c +++ b/usr.sbin/hoststated/check_send_expect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: check_send_expect.c,v 1.5 2007/01/12 16:43:01 pyr Exp $ */ +/* $OpenBSD: check_send_expect.c,v 1.6 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> * @@ -31,6 +31,8 @@ #include <fnmatch.h> #include <errno.h> +#include <openssl/ssl.h> + #include "hoststated.h" int diff --git a/usr.sbin/hoststated/check_tcp.c b/usr.sbin/hoststated/check_tcp.c index 7195a216710..a3ebc23c563 100644 --- a/usr.sbin/hoststated/check_tcp.c +++ b/usr.sbin/hoststated/check_tcp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: check_tcp.c,v 1.11 2007/01/20 16:32:10 pyr Exp $ */ +/* $OpenBSD: check_tcp.c,v 1.12 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -30,6 +30,8 @@ #include <stdlib.h> #include <errno.h> +#include <openssl/ssl.h> + #include "hoststated.h" void tcp_write(int, short, void *); @@ -108,6 +110,7 @@ tcp_write(int s, short event, void *arg) else cte->host->up = HOST_UP; } + if (cte->host->up == HOST_UP) tcp_host_up(s, cte); else { @@ -123,8 +126,10 @@ tcp_host_up(int s, struct ctl_tcp_event *cte) switch (cte->table->check) { case CHECK_TCP: + if (cte->table->flags & F_SSL) + break; close(s); - hce_notify_done(cte->host, "tcp_host_up: success"); + hce_notify_done(cte->host, "tcp_host_up: connect successfull"); return; case CHECK_HTTP_CODE: cte->validate_read = NULL; @@ -140,6 +145,11 @@ tcp_host_up(int s, struct ctl_tcp_event *cte) break; } + if (cte->table->flags & F_SSL) { + ssl_transaction(cte); + return; + } + if (cte->table->sendbuf != NULL) { cte->req = cte->table->sendbuf; event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_send_req, diff --git a/usr.sbin/hoststated/control.c b/usr.sbin/hoststated/control.c index 46ac0e87ec8..b7f78b2434e 100644 --- a/usr.sbin/hoststated/control.c +++ b/usr.sbin/hoststated/control.c @@ -1,4 +1,4 @@ -/* $OpenBSD: control.c,v 1.9 2007/01/23 17:43:36 claudio Exp $ */ +/* $OpenBSD: control.c,v 1.10 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -31,6 +31,8 @@ #include <unistd.h> #include <signal.h> +#include <openssl/ssl.h> + #include "hoststated.h" #define CONTROL_BACKLOG 5 diff --git a/usr.sbin/hoststated/hce.c b/usr.sbin/hoststated/hce.c index aceef6f8e88..ac887311164 100644 --- a/usr.sbin/hoststated/hce.c +++ b/usr.sbin/hoststated/hce.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hce.c,v 1.11 2007/01/24 10:26:00 claudio Exp $ */ +/* $OpenBSD: hce.c,v 1.12 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -36,6 +36,8 @@ #include <err.h> #include <pwd.h> +#include <openssl/ssl.h> + #include "hoststated.h" void hce_sig_handler(int sig, short, void *); @@ -70,6 +72,7 @@ hce(struct hoststated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], struct timeval tv; struct event ev_sigint; struct event ev_sigterm; + struct table *table; switch (pid = fork()) { case -1: @@ -135,6 +138,15 @@ hce(struct hoststated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], bzero(&tv, sizeof(tv)); evtimer_add(&env->ev, &tv); + if (env->flags & F_SSL) { + ssl_init(env); + TAILQ_FOREACH(table, &env->tables, entry) { + if (!(table->flags & F_SSL)) + continue; + table->ssl_ctx = ssl_ctx_create(env); + } + } + event_dispatch(); hce_shutdown(); diff --git a/usr.sbin/hoststated/hoststated.c b/usr.sbin/hoststated/hoststated.c index 7e9b2cd8d00..4cdbca45a34 100644 --- a/usr.sbin/hoststated/hoststated.c +++ b/usr.sbin/hoststated/hoststated.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hoststated.c,v 1.12 2007/01/24 10:26:00 claudio Exp $ */ +/* $OpenBSD: hoststated.c,v 1.13 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -33,6 +33,8 @@ #include <unistd.h> #include <pwd.h> +#include <openssl/ssl.h> + #include "hoststated.h" __dead void usage(void); diff --git a/usr.sbin/hoststated/hoststated.conf.5 b/usr.sbin/hoststated/hoststated.conf.5 index 41e4ddf7107..06f04a925c0 100644 --- a/usr.sbin/hoststated/hoststated.conf.5 +++ b/usr.sbin/hoststated/hoststated.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: hoststated.conf.5,v 1.14 2007/01/10 13:42:19 jmc Exp $ +.\" $OpenBSD: hoststated.conf.5,v 1.15 2007/01/29 14:23:31 pyr Exp $ .\" .\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> .\" @@ -109,6 +109,12 @@ For each host in the table, verify that retrieving the URL .Ar path gives the HTTP return code .Ar number . +If +.Ic use ssl +is specified, HTTPS will be used to contact the host. +.It Ic check https Ar path Ic code Ar number +This has the same effect than above but also implies +.Ic use ssl . .It Ic check http Ar path Ic digest Ar string For each host in the table, verify that retrieving the URL .Ar path @@ -125,6 +131,12 @@ that can be used as is in a digest statement: .Bd -literal -offset indent a9993e36476816aba3e25717850c26c9cd0d89d .Ed +If +.Ic use ssl +is specified, HTTPS will be used to contact the host. +.It Ic check https Ar path Ic digest Ar string +This has the same effect than above but also implies +.Ic use ssl . .It Ic check icmp Ping hosts in this table to determine whether they are up or not. This method will automatically use ICMP or ICMPV6 depending on the @@ -145,8 +157,17 @@ then nothing is sent on the connection and data is immediately read. This can be useful with protocols that output a banner like SMTP, NNTP and FTP. +If +.Ic use ssl +is specified, the data will be sent and/or received inside a SSL tunnel. .It Ic check tcp Use a simple TCP connect to check that hosts are up. +If +.Ic use ssl +is specified, a complete SSL handshake will also be performed. +.It Ic check ssl +This has the same effect than above but also implies +.Ic use ssl . .It Ic disable Start the table disabled \(en no hosts will be checked in this table. The table can be later enabled through @@ -166,6 +187,8 @@ Main and backup tables need to have the same real port. Set the timeout in milliseconds for each host that is checked using TCP as the transport. This will override the global timeout, which is 200 milliseconds by default. +.It Ic use ssl +When the table uses a TCP check, wrap it into SSL. .El .Sh SERVICES Services represent a diff --git a/usr.sbin/hoststated/hoststated.h b/usr.sbin/hoststated/hoststated.h index 70cb31605ef..ba216828970 100644 --- a/usr.sbin/hoststated/hoststated.h +++ b/usr.sbin/hoststated/hoststated.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hoststated.h,v 1.16 2007/01/12 17:05:18 pyr Exp $ */ +/* $OpenBSD: hoststated.h,v 1.17 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -143,6 +143,8 @@ struct ctl_tcp_event { struct event ev; int (*validate_read)(struct ctl_tcp_event *); int (*validate_close)(struct ctl_tcp_event *); + SSL *ssl; + char rbuf[SMALL_READ_BUF_SIZE]; }; struct address { @@ -164,6 +166,7 @@ TAILQ_HEAD(addresslist, address); #define F_CHECK_DONE 0x0100 #define F_ACTIVE_RULESET 0x0200 #define F_CHECK_SENT 0x0400 +#define F_SSL 0x0800 struct host { u_int16_t flags; @@ -197,6 +200,7 @@ struct table { char *sendbuf; char exbuf[64]; char digest[41]; /* length of sha1 digest * 2 */ + SSL_CTX *ssl_ctx; struct hostlist hosts; TAILQ_ENTRY(table) entry; }; @@ -230,6 +234,7 @@ enum { struct hoststated { u_int8_t opts; + u_int16_t flags; struct pfdata *pf; int tablecount; int servicecount; @@ -356,6 +361,11 @@ int check_http_digest(struct ctl_tcp_event *); /* check_send_expect.c */ int check_send_expect(struct ctl_tcp_event *); +/* ssl.c */ +void ssl_init(struct hoststated *); +void ssl_transaction(struct ctl_tcp_event *); +SSL_CTX *ssl_ctx_create(struct hoststated *); + /* hoststated.c */ struct host *host_find(struct hoststated *, objid_t); struct table *table_find(struct hoststated *, objid_t); diff --git a/usr.sbin/hoststated/imsg.c b/usr.sbin/hoststated/imsg.c index fb110a45fb8..83d633e2dc1 100644 --- a/usr.sbin/hoststated/imsg.c +++ b/usr.sbin/hoststated/imsg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg.c,v 1.4 2007/01/09 00:45:32 deraadt Exp $ */ +/* $OpenBSD: imsg.c,v 1.5 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -28,6 +28,8 @@ #include <string.h> #include <unistd.h> +#include <openssl/ssl.h> + #include "hoststated.h" void diff --git a/usr.sbin/hoststated/parse.y b/usr.sbin/hoststated/parse.y index 7dc5648ea6f..195823f2aa3 100644 --- a/usr.sbin/hoststated/parse.y +++ b/usr.sbin/hoststated/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.18 2007/01/25 19:40:08 niallo Exp $ */ +/* $OpenBSD: parse.y,v 1.19 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -41,6 +41,8 @@ #include <netdb.h> #include <string.h> +#include <openssl/ssl.h> + #include "hoststated.h" struct hoststated *conf = NULL; @@ -98,14 +100,14 @@ typedef struct { %} %token SERVICE TABLE BACKUP HOST REAL -%token CHECK HTTP TCP ICMP EXTERNAL +%token CHECK HTTP HTTPS TCP ICMP EXTERNAL %token TIMEOUT CODE DIGEST PORT TAG INTERFACE %token VIRTUAL IP INTERVAL DISABLE STICKYADDR -%token SEND EXPECT NOTHING +%token SEND EXPECT NOTHING USE SSL %token ERROR %token <v.string> STRING %type <v.string> interface -%type <v.number> number port +%type <v.number> number port http_type %type <v.host> host %type <v.tv> timeout @@ -134,6 +136,10 @@ number : STRING { } ; +http_type : HTTP { $$ = 0; } + | HTTPS { $$ = 1; } + ; + port : PORT STRING { const char *estr; struct servent *servent; @@ -156,12 +162,22 @@ port : PORT STRING { $$ = htons($$); free($2); } - | PORT HTTP { + | PORT http_type { struct servent *servent; + int port; + const char *sport; + + if ($2) { + port = 443; + sport = "https"; + } else { + port = 80; + sport = "http"; + } - servent = getservbyname("http", "tcp"); + servent = getservbyname(sport, "tcp"); if (servent == NULL) - $$ = htons(80); + $$ = htons(port); else $$ = servent->s_port; } @@ -380,7 +396,16 @@ tableoptsl : host { | CHECK TCP { table->check = CHECK_TCP; } - | CHECK HTTP STRING CODE number { + | CHECK SSL { + table->check = CHECK_TCP; + conf->flags |= F_SSL; + table->flags |= F_SSL; + } + | CHECK http_type STRING CODE number { + if ($2) { + conf->flags |= F_SSL; + table->flags |= F_SSL; + } table->check = CHECK_HTTP_CODE; table->retcode = $5; asprintf(&table->sendbuf, "HEAD %s HTTP/1.0\r\n\r\n", @@ -389,7 +414,11 @@ tableoptsl : host { if (table->sendbuf == NULL) fatal("out of memory"); } - | CHECK HTTP STRING DIGEST STRING { + | CHECK http_type STRING DIGEST STRING { + if ($2) { + conf->flags |= F_SSL; + table->flags |= F_SSL; + } table->check = CHECK_HTTP_DIGEST; asprintf(&table->sendbuf, "GET %s HTTP/1.0\r\n\r\n", $3); @@ -418,6 +447,10 @@ tableoptsl : host { table->port = $2; } | DISABLE { table->flags |= F_DISABLE; } + | USE SSL { + table->flags |= F_SSL; + conf->flags |= F_SSL; + } ; interface : /*empty*/ { $$ = NULL; } @@ -515,6 +548,7 @@ lookup(char *s) { "external", EXTERNAL }, { "host", HOST }, { "http", HTTP }, + { "https", HTTPS }, { "icmp", ICMP }, { "interface", INTERFACE }, { "interval", INTERVAL }, @@ -524,11 +558,13 @@ lookup(char *s) { "real", REAL }, { "send", SEND }, { "service", SERVICE }, + { "ssl", SSL }, { "sticky-address", STICKYADDR }, { "table", TABLE }, { "tag", TAG }, { "tcp", TCP }, { "timeout", TIMEOUT }, + { "use", USE }, { "virtual", VIRTUAL } }; const struct keywords *p; diff --git a/usr.sbin/hoststated/pfe.c b/usr.sbin/hoststated/pfe.c index 1077928d1ef..43c6f358c37 100644 --- a/usr.sbin/hoststated/pfe.c +++ b/usr.sbin/hoststated/pfe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfe.c,v 1.8 2007/01/24 10:26:00 claudio Exp $ */ +/* $OpenBSD: pfe.c,v 1.9 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -31,6 +31,8 @@ #include <unistd.h> #include <pwd.h> +#include <openssl/ssl.h> + #include "hoststated.h" void pfe_sig_handler(int sig, short, void *); diff --git a/usr.sbin/hoststated/pfe_filter.c b/usr.sbin/hoststated/pfe_filter.c index 18e0af927e3..22c95844271 100644 --- a/usr.sbin/hoststated/pfe_filter.c +++ b/usr.sbin/hoststated/pfe_filter.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfe_filter.c,v 1.9 2007/01/09 13:50:11 pyr Exp $ */ +/* $OpenBSD: pfe_filter.c,v 1.10 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -35,6 +35,8 @@ #include <stdlib.h> #include <errno.h> +#include <openssl/ssl.h> + #include "hoststated.h" struct pfdata { diff --git a/usr.sbin/hoststated/ssl.c b/usr.sbin/hoststated/ssl.c new file mode 100644 index 00000000000..9cedfb9b37d --- /dev/null +++ b/usr.sbin/hoststated/ssl.c @@ -0,0 +1,309 @@ +/* $OpenBSD: ssl.c,v 1.1 2007/01/29 14:23:31 pyr Exp $ */ + +/* + * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <net/if.h> +#include <limits.h> +#include <event.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include <openssl/ssl.h> +#include <openssl/err.h> + +#include "hoststated.h" + +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 *); +void ssl_error(const char *); + +void +ssl_read(int s, short event, void *arg) +{ + struct ctl_tcp_event *cte = arg; + int ret; + int ssl_err; + int retry_flag; + char rbuf[SMALL_READ_BUF_SIZE]; + + if (event == EV_TIMEOUT) { + cte->host->up = HOST_DOWN; + ssl_cleanup(cte); + hce_notify_done(cte->host, "ssl_read: timeout"); + return; + } + log_debug("ssl_read: event occurred"); + + bzero(rbuf, sizeof(rbuf)); + ssl_err = 0; + retry_flag = EV_READ; + + ret = SSL_read(cte->ssl, rbuf, sizeof(rbuf)); + + if (ret <= 0) { + ssl_err = SSL_get_error(cte->ssl, ret); + switch (ssl_err) { + case SSL_ERROR_WANT_READ: + log_debug("ssl_read: want read"); + retry_flag = EV_READ; + goto retry; + case SSL_ERROR_WANT_WRITE: + log_debug("ssl_read: want read"); + retry_flag = EV_WRITE; + goto retry; + case SSL_ERROR_ZERO_RETURN: /* FALLTHROUGH */ + case SSL_ERROR_SYSCALL: + if (ret == 0) { + cte->host->up = HOST_DOWN; + (void)cte->validate_close(cte); + ssl_cleanup(cte); + if (cte->host->up == HOST_UP) + hce_notify_done(cte->host, + "ssl_read: check succeeded"); + else + hce_notify_done(cte->host, + "ssl_read: check failed"); + return; + } + /* FALLTHROUGH */ + default: + cte->host->up = HOST_DOWN; + ssl_error("cannot read"); + ssl_cleanup(cte); + hce_notify_done(cte->host, "ssl_read: SSL error"); + break; + } + return; + } + buf_add(cte->buf, rbuf, ret); + + if (cte->validate_read != NULL) { + if (cte->validate_read(cte) != 0) + goto retry; + + ssl_cleanup(cte); + if (cte->host->up == HOST_UP) + hce_notify_done(cte->host, "ssl_read: check succeeded"); + else + hce_notify_done(cte->host, "ssl_read: check failed"); + return; + } + +retry: + log_debug("ssl_read: scheduling ssl_read on %s", + (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE"); + event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_read, + &cte->tv_start, &cte->table->timeout, cte); + return; +} + +void +ssl_write(int s, short event, void *arg) +{ + struct ctl_tcp_event *cte = arg; + int len; + int ret; + int ssl_err; + int retry_flag; + + if (event == EV_TIMEOUT) { + cte->host->up = HOST_DOWN; + ssl_cleanup(cte); + hce_notify_done(cte->host, "ssl_write: timeout"); + return; + } + + log_debug("ssl_write: event occurred"); + len = strlen(cte->table->sendbuf); + retry_flag = EV_WRITE; + + ret = SSL_write(cte->ssl, cte->table->sendbuf, len); + + if (ret <= 0) { + ssl_err = SSL_get_error(cte->ssl, ret); + switch (ssl_err) { + case SSL_ERROR_WANT_READ: + log_debug("ssl_write: want read"); + retry_flag = EV_READ; + goto retry; + case SSL_ERROR_WANT_WRITE: + log_debug("ssl_write: want write"); + retry_flag = EV_WRITE; + goto retry; + default: + cte->host->up = HOST_DOWN; + ssl_error("cannot write"); + ssl_cleanup(cte); + hce_notify_done(cte->host, "ssl_write: SSL error"); + return; + } + } + if ((cte->buf = buf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) + fatalx("ssl_write: cannot create dynamic buffer"); + + log_debug("ssl_write: scheduling ssl_read on EV_READ"); + event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, ssl_read, + &cte->tv_start, &cte->table->timeout, cte); + return; +retry: + log_debug("ssl_write: scheduling ssl_write on %s", + (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE"); + event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_write, + &cte->tv_start, &cte->table->timeout, cte); +} + +void +ssl_connect(int s, short event, void *arg) +{ + struct ctl_tcp_event *cte = arg; + int ret; + int ssl_err; + int retry_flag; + + if (event == EV_TIMEOUT) { + cte->host->up = HOST_DOWN; + hce_notify_done(cte->host, "ssl_connect: timeout"); + return; + } + + retry_flag = ssl_err = 0; + + ret = SSL_connect(cte->ssl); + + if (ret <= 0) { + ssl_err = SSL_get_error(cte->ssl, ret); + switch (ssl_err) { + case SSL_ERROR_WANT_READ: + log_debug("ssl_connect: want read"); + retry_flag = EV_READ; + goto retry; + case SSL_ERROR_WANT_WRITE: + log_debug("ssl_connect: want write"); + retry_flag = EV_WRITE; + goto retry; + default: + cte->host->up = HOST_DOWN; + ssl_error("ssl_connect: cannot connect"); + hce_notify_done(cte->host, "ssl_connect: SSL error"); + ssl_cleanup(cte); + return; + } + } + + if (cte->table->check == CHECK_TCP) { + cte->host->up = HOST_UP; + hce_notify_done(cte->host, "ssl_connect: connect successful"); + ssl_cleanup(cte); + return; + } + log_debug("ssl_connect: connect succeeded"); + if (cte->table->sendbuf != NULL) { + log_debug("ssl_connect: scheduling ssl_write on EV_WRITE"); + event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, ssl_write, + &cte->tv_start, &cte->table->timeout, cte); + return; + } + + if ((cte->buf = buf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) + fatalx("ssl_connect: cannot create dynamic buffer"); + log_debug("ssl_connect: scheduling ssl_read on EV_READ"); + event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ, ssl_read, + &cte->tv_start, &cte->table->timeout, cte); + return; + +retry: + log_debug("ssl_write: scheduling ssl_write on %s", + (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE"); + event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_connect, + &cte->tv_start, &cte->table->timeout, cte); +} + +void +ssl_cleanup(struct ctl_tcp_event *cte) +{ + log_debug("ssl_cleanup: cleaning for %s", cte->host->name); + close(cte->s); + if (cte->ssl != NULL) + SSL_free(cte->ssl); + if (cte->buf != NULL) + buf_free(cte->buf); +} + +void +ssl_error(const char *msg) +{ + unsigned long code; + char errbuf[128]; + + + for (; (code = ERR_get_error()) != 0 ;) { + ERR_error_string_n(code, errbuf, sizeof(errbuf)); + log_debug("ssl_error: %s: %s", msg, errbuf); + } +} + +void +ssl_init(struct hoststated *env) +{ + SSL_library_init(); + SSL_load_error_strings(); +} + +void +ssl_transaction(struct ctl_tcp_event *cte) +{ + cte->ssl = SSL_new(cte->table->ssl_ctx); + if (cte->ssl == NULL) { + ssl_error("ssl_transaction: cannot create object"); + fatal("cannot create SSL object"); + } + + if (SSL_set_fd(cte->ssl, cte->s) == 0) { + cte->host->up = HOST_UNKNOWN; + ssl_error("ssl_transaction: cannot set fd"); + ssl_cleanup(cte); + hce_notify_done(cte->host, "cannot set SSL fd"); + return; + } + SSL_set_connect_state(cte->ssl); + + event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, ssl_connect, + &cte->tv_start, &cte->table->timeout, cte); +} + +SSL_CTX * +ssl_ctx_create(struct hoststated *env) +{ + SSL_CTX *ctx; + + ctx = SSL_CTX_new(SSLv23_client_method()); + if (ctx == NULL) { + ssl_error("ssl_ctx_create: cannot create context"); + fatal("could not create SSL context"); + } + return (ctx); +} diff --git a/usr.sbin/relayctl/parser.c b/usr.sbin/relayctl/parser.c index 708fee48bc0..aca5a16c9e9 100644 --- a/usr.sbin/relayctl/parser.c +++ b/usr.sbin/relayctl/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.6 2007/01/29 10:28:11 claudio Exp $ */ +/* $OpenBSD: parser.c,v 1.7 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -32,6 +32,8 @@ #include <string.h> #include <event.h> +#include <openssl/ssl.h> + #include "hoststated.h" #include "parser.h" diff --git a/usr.sbin/relayctl/relayctl.c b/usr.sbin/relayctl/relayctl.c index 505541b3d16..8332f2f0229 100644 --- a/usr.sbin/relayctl/relayctl.c +++ b/usr.sbin/relayctl/relayctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayctl.c,v 1.8 2007/01/09 13:50:10 pyr Exp $ */ +/* $OpenBSD: relayctl.c,v 1.9 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -36,6 +36,8 @@ #include <unistd.h> #include <event.h> +#include <openssl/ssl.h> + #include "hoststated.h" #include "parser.h" diff --git a/usr.sbin/relayd/Makefile b/usr.sbin/relayd/Makefile index 2cf8d3a13c1..b619932c623 100644 --- a/usr.sbin/relayd/Makefile +++ b/usr.sbin/relayd/Makefile @@ -1,13 +1,13 @@ -# $OpenBSD: Makefile,v 1.4 2007/01/09 02:32:58 reyk Exp $ +# $OpenBSD: Makefile,v 1.5 2007/01/29 14:23:31 pyr Exp $ PROG= hoststated SRCS= parse.y log.c control.c buffer.c imsg.c hoststated.c \ - pfe.c pfe_filter.c hce.c \ + ssl.c pfe.c pfe_filter.c hce.c \ check_icmp.c check_tcp.c check_http.c check_send_expect.c MAN= hoststated.8 hoststated.conf.5 -LDADD= -levent -DPADD= ${LIBEVENT} +LDADD= -levent -lcrypto -lssl +DPADD= ${LIBEVENT} ${LIBCRYPTO} ${LIBSSL} CFLAGS+= -Wall -Werror -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations diff --git a/usr.sbin/relayd/buffer.c b/usr.sbin/relayd/buffer.c index 7133094ddfc..4d85f78ea57 100644 --- a/usr.sbin/relayd/buffer.c +++ b/usr.sbin/relayd/buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: buffer.c,v 1.4 2007/01/09 00:45:32 deraadt Exp $ */ +/* $OpenBSD: buffer.c,v 1.5 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -30,6 +30,8 @@ #include <string.h> #include <unistd.h> +#include <openssl/ssl.h> + #include "hoststated.h" int buf_realloc(struct buf *, size_t); diff --git a/usr.sbin/relayd/check_icmp.c b/usr.sbin/relayd/check_icmp.c index f4167359a0e..ce83f7de214 100644 --- a/usr.sbin/relayd/check_icmp.c +++ b/usr.sbin/relayd/check_icmp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: check_icmp.c,v 1.11 2007/01/12 17:12:58 pyr Exp $ */ +/* $OpenBSD: check_icmp.c,v 1.12 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -35,6 +35,8 @@ #include <stdlib.h> #include <err.h> +#include <openssl/ssl.h> + #include "hoststated.h" void icmp_setup(struct hoststated *, struct ctl_icmp_event *, int); diff --git a/usr.sbin/relayd/check_tcp.c b/usr.sbin/relayd/check_tcp.c index 7195a216710..a3ebc23c563 100644 --- a/usr.sbin/relayd/check_tcp.c +++ b/usr.sbin/relayd/check_tcp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: check_tcp.c,v 1.11 2007/01/20 16:32:10 pyr Exp $ */ +/* $OpenBSD: check_tcp.c,v 1.12 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -30,6 +30,8 @@ #include <stdlib.h> #include <errno.h> +#include <openssl/ssl.h> + #include "hoststated.h" void tcp_write(int, short, void *); @@ -108,6 +110,7 @@ tcp_write(int s, short event, void *arg) else cte->host->up = HOST_UP; } + if (cte->host->up == HOST_UP) tcp_host_up(s, cte); else { @@ -123,8 +126,10 @@ tcp_host_up(int s, struct ctl_tcp_event *cte) switch (cte->table->check) { case CHECK_TCP: + if (cte->table->flags & F_SSL) + break; close(s); - hce_notify_done(cte->host, "tcp_host_up: success"); + hce_notify_done(cte->host, "tcp_host_up: connect successfull"); return; case CHECK_HTTP_CODE: cte->validate_read = NULL; @@ -140,6 +145,11 @@ tcp_host_up(int s, struct ctl_tcp_event *cte) break; } + if (cte->table->flags & F_SSL) { + ssl_transaction(cte); + return; + } + if (cte->table->sendbuf != NULL) { cte->req = cte->table->sendbuf; event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_send_req, diff --git a/usr.sbin/relayd/control.c b/usr.sbin/relayd/control.c index 46ac0e87ec8..b7f78b2434e 100644 --- a/usr.sbin/relayd/control.c +++ b/usr.sbin/relayd/control.c @@ -1,4 +1,4 @@ -/* $OpenBSD: control.c,v 1.9 2007/01/23 17:43:36 claudio Exp $ */ +/* $OpenBSD: control.c,v 1.10 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -31,6 +31,8 @@ #include <unistd.h> #include <signal.h> +#include <openssl/ssl.h> + #include "hoststated.h" #define CONTROL_BACKLOG 5 diff --git a/usr.sbin/relayd/hce.c b/usr.sbin/relayd/hce.c index aceef6f8e88..ac887311164 100644 --- a/usr.sbin/relayd/hce.c +++ b/usr.sbin/relayd/hce.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hce.c,v 1.11 2007/01/24 10:26:00 claudio Exp $ */ +/* $OpenBSD: hce.c,v 1.12 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -36,6 +36,8 @@ #include <err.h> #include <pwd.h> +#include <openssl/ssl.h> + #include "hoststated.h" void hce_sig_handler(int sig, short, void *); @@ -70,6 +72,7 @@ hce(struct hoststated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], struct timeval tv; struct event ev_sigint; struct event ev_sigterm; + struct table *table; switch (pid = fork()) { case -1: @@ -135,6 +138,15 @@ hce(struct hoststated *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2], bzero(&tv, sizeof(tv)); evtimer_add(&env->ev, &tv); + if (env->flags & F_SSL) { + ssl_init(env); + TAILQ_FOREACH(table, &env->tables, entry) { + if (!(table->flags & F_SSL)) + continue; + table->ssl_ctx = ssl_ctx_create(env); + } + } + event_dispatch(); hce_shutdown(); diff --git a/usr.sbin/relayd/imsg.c b/usr.sbin/relayd/imsg.c index fb110a45fb8..83d633e2dc1 100644 --- a/usr.sbin/relayd/imsg.c +++ b/usr.sbin/relayd/imsg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg.c,v 1.4 2007/01/09 00:45:32 deraadt Exp $ */ +/* $OpenBSD: imsg.c,v 1.5 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -28,6 +28,8 @@ #include <string.h> #include <unistd.h> +#include <openssl/ssl.h> + #include "hoststated.h" void diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y index 7dc5648ea6f..195823f2aa3 100644 --- a/usr.sbin/relayd/parse.y +++ b/usr.sbin/relayd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.18 2007/01/25 19:40:08 niallo Exp $ */ +/* $OpenBSD: parse.y,v 1.19 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -41,6 +41,8 @@ #include <netdb.h> #include <string.h> +#include <openssl/ssl.h> + #include "hoststated.h" struct hoststated *conf = NULL; @@ -98,14 +100,14 @@ typedef struct { %} %token SERVICE TABLE BACKUP HOST REAL -%token CHECK HTTP TCP ICMP EXTERNAL +%token CHECK HTTP HTTPS TCP ICMP EXTERNAL %token TIMEOUT CODE DIGEST PORT TAG INTERFACE %token VIRTUAL IP INTERVAL DISABLE STICKYADDR -%token SEND EXPECT NOTHING +%token SEND EXPECT NOTHING USE SSL %token ERROR %token <v.string> STRING %type <v.string> interface -%type <v.number> number port +%type <v.number> number port http_type %type <v.host> host %type <v.tv> timeout @@ -134,6 +136,10 @@ number : STRING { } ; +http_type : HTTP { $$ = 0; } + | HTTPS { $$ = 1; } + ; + port : PORT STRING { const char *estr; struct servent *servent; @@ -156,12 +162,22 @@ port : PORT STRING { $$ = htons($$); free($2); } - | PORT HTTP { + | PORT http_type { struct servent *servent; + int port; + const char *sport; + + if ($2) { + port = 443; + sport = "https"; + } else { + port = 80; + sport = "http"; + } - servent = getservbyname("http", "tcp"); + servent = getservbyname(sport, "tcp"); if (servent == NULL) - $$ = htons(80); + $$ = htons(port); else $$ = servent->s_port; } @@ -380,7 +396,16 @@ tableoptsl : host { | CHECK TCP { table->check = CHECK_TCP; } - | CHECK HTTP STRING CODE number { + | CHECK SSL { + table->check = CHECK_TCP; + conf->flags |= F_SSL; + table->flags |= F_SSL; + } + | CHECK http_type STRING CODE number { + if ($2) { + conf->flags |= F_SSL; + table->flags |= F_SSL; + } table->check = CHECK_HTTP_CODE; table->retcode = $5; asprintf(&table->sendbuf, "HEAD %s HTTP/1.0\r\n\r\n", @@ -389,7 +414,11 @@ tableoptsl : host { if (table->sendbuf == NULL) fatal("out of memory"); } - | CHECK HTTP STRING DIGEST STRING { + | CHECK http_type STRING DIGEST STRING { + if ($2) { + conf->flags |= F_SSL; + table->flags |= F_SSL; + } table->check = CHECK_HTTP_DIGEST; asprintf(&table->sendbuf, "GET %s HTTP/1.0\r\n\r\n", $3); @@ -418,6 +447,10 @@ tableoptsl : host { table->port = $2; } | DISABLE { table->flags |= F_DISABLE; } + | USE SSL { + table->flags |= F_SSL; + conf->flags |= F_SSL; + } ; interface : /*empty*/ { $$ = NULL; } @@ -515,6 +548,7 @@ lookup(char *s) { "external", EXTERNAL }, { "host", HOST }, { "http", HTTP }, + { "https", HTTPS }, { "icmp", ICMP }, { "interface", INTERFACE }, { "interval", INTERVAL }, @@ -524,11 +558,13 @@ lookup(char *s) { "real", REAL }, { "send", SEND }, { "service", SERVICE }, + { "ssl", SSL }, { "sticky-address", STICKYADDR }, { "table", TABLE }, { "tag", TAG }, { "tcp", TCP }, { "timeout", TIMEOUT }, + { "use", USE }, { "virtual", VIRTUAL } }; const struct keywords *p; diff --git a/usr.sbin/relayd/pfe.c b/usr.sbin/relayd/pfe.c index 1077928d1ef..43c6f358c37 100644 --- a/usr.sbin/relayd/pfe.c +++ b/usr.sbin/relayd/pfe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfe.c,v 1.8 2007/01/24 10:26:00 claudio Exp $ */ +/* $OpenBSD: pfe.c,v 1.9 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -31,6 +31,8 @@ #include <unistd.h> #include <pwd.h> +#include <openssl/ssl.h> + #include "hoststated.h" void pfe_sig_handler(int sig, short, void *); diff --git a/usr.sbin/relayd/pfe_filter.c b/usr.sbin/relayd/pfe_filter.c index 18e0af927e3..22c95844271 100644 --- a/usr.sbin/relayd/pfe_filter.c +++ b/usr.sbin/relayd/pfe_filter.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfe_filter.c,v 1.9 2007/01/09 13:50:11 pyr Exp $ */ +/* $OpenBSD: pfe_filter.c,v 1.10 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -35,6 +35,8 @@ #include <stdlib.h> #include <errno.h> +#include <openssl/ssl.h> + #include "hoststated.h" struct pfdata { diff --git a/usr.sbin/relayd/relayd.c b/usr.sbin/relayd/relayd.c index 2a0b1233620..ae31748ae37 100644 --- a/usr.sbin/relayd/relayd.c +++ b/usr.sbin/relayd/relayd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.c,v 1.12 2007/01/24 10:26:00 claudio Exp $ */ +/* $OpenBSD: relayd.c,v 1.13 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -33,6 +33,8 @@ #include <unistd.h> #include <pwd.h> +#include <openssl/ssl.h> + #include "hoststated.h" __dead void usage(void); diff --git a/usr.sbin/relayd/relayd.conf.5 b/usr.sbin/relayd/relayd.conf.5 index ae3f6ec209f..ce0d6b760e8 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.14 2007/01/10 13:42:19 jmc Exp $ +.\" $OpenBSD: relayd.conf.5,v 1.15 2007/01/29 14:23:31 pyr Exp $ .\" .\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> .\" @@ -109,6 +109,12 @@ For each host in the table, verify that retrieving the URL .Ar path gives the HTTP return code .Ar number . +If +.Ic use ssl +is specified, HTTPS will be used to contact the host. +.It Ic check https Ar path Ic code Ar number +This has the same effect than above but also implies +.Ic use ssl . .It Ic check http Ar path Ic digest Ar string For each host in the table, verify that retrieving the URL .Ar path @@ -125,6 +131,12 @@ that can be used as is in a digest statement: .Bd -literal -offset indent a9993e36476816aba3e25717850c26c9cd0d89d .Ed +If +.Ic use ssl +is specified, HTTPS will be used to contact the host. +.It Ic check https Ar path Ic digest Ar string +This has the same effect than above but also implies +.Ic use ssl . .It Ic check icmp Ping hosts in this table to determine whether they are up or not. This method will automatically use ICMP or ICMPV6 depending on the @@ -145,8 +157,17 @@ then nothing is sent on the connection and data is immediately read. This can be useful with protocols that output a banner like SMTP, NNTP and FTP. +If +.Ic use ssl +is specified, the data will be sent and/or received inside a SSL tunnel. .It Ic check tcp Use a simple TCP connect to check that hosts are up. +If +.Ic use ssl +is specified, a complete SSL handshake will also be performed. +.It Ic check ssl +This has the same effect than above but also implies +.Ic use ssl . .It Ic disable Start the table disabled \(en no hosts will be checked in this table. The table can be later enabled through @@ -166,6 +187,8 @@ Main and backup tables need to have the same real port. Set the timeout in milliseconds for each host that is checked using TCP as the transport. This will override the global timeout, which is 200 milliseconds by default. +.It Ic use ssl +When the table uses a TCP check, wrap it into SSL. .El .Sh SERVICES Services represent a diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index b76c4e68e9b..f375d5724c6 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.16 2007/01/12 17:05:18 pyr Exp $ */ +/* $OpenBSD: relayd.h,v 1.17 2007/01/29 14:23:31 pyr Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> @@ -143,6 +143,8 @@ struct ctl_tcp_event { struct event ev; int (*validate_read)(struct ctl_tcp_event *); int (*validate_close)(struct ctl_tcp_event *); + SSL *ssl; + char rbuf[SMALL_READ_BUF_SIZE]; }; struct address { @@ -164,6 +166,7 @@ TAILQ_HEAD(addresslist, address); #define F_CHECK_DONE 0x0100 #define F_ACTIVE_RULESET 0x0200 #define F_CHECK_SENT 0x0400 +#define F_SSL 0x0800 struct host { u_int16_t flags; @@ -197,6 +200,7 @@ struct table { char *sendbuf; char exbuf[64]; char digest[41]; /* length of sha1 digest * 2 */ + SSL_CTX *ssl_ctx; struct hostlist hosts; TAILQ_ENTRY(table) entry; }; @@ -230,6 +234,7 @@ enum { struct hoststated { u_int8_t opts; + u_int16_t flags; struct pfdata *pf; int tablecount; int servicecount; @@ -356,6 +361,11 @@ int check_http_digest(struct ctl_tcp_event *); /* check_send_expect.c */ int check_send_expect(struct ctl_tcp_event *); +/* ssl.c */ +void ssl_init(struct hoststated *); +void ssl_transaction(struct ctl_tcp_event *); +SSL_CTX *ssl_ctx_create(struct hoststated *); + /* hoststated.c */ struct host *host_find(struct hoststated *, objid_t); struct table *table_find(struct hoststated *, objid_t); diff --git a/usr.sbin/relayd/ssl.c b/usr.sbin/relayd/ssl.c new file mode 100644 index 00000000000..9cedfb9b37d --- /dev/null +++ b/usr.sbin/relayd/ssl.c @@ -0,0 +1,309 @@ +/* $OpenBSD: ssl.c,v 1.1 2007/01/29 14:23:31 pyr Exp $ */ + +/* + * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <net/if.h> +#include <limits.h> +#include <event.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include <openssl/ssl.h> +#include <openssl/err.h> + +#include "hoststated.h" + +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 *); +void ssl_error(const char *); + +void +ssl_read(int s, short event, void *arg) +{ + struct ctl_tcp_event *cte = arg; + int ret; + int ssl_err; + int retry_flag; + char rbuf[SMALL_READ_BUF_SIZE]; + + if (event == EV_TIMEOUT) { + cte->host->up = HOST_DOWN; + ssl_cleanup(cte); + hce_notify_done(cte->host, "ssl_read: timeout"); + return; + } + log_debug("ssl_read: event occurred"); + + bzero(rbuf, sizeof(rbuf)); + ssl_err = 0; + retry_flag = EV_READ; + + ret = SSL_read(cte->ssl, rbuf, sizeof(rbuf)); + + if (ret <= 0) { + ssl_err = SSL_get_error(cte->ssl, ret); + switch (ssl_err) { + case SSL_ERROR_WANT_READ: + log_debug("ssl_read: want read"); + retry_flag = EV_READ; + goto retry; + case SSL_ERROR_WANT_WRITE: + log_debug("ssl_read: want read"); + retry_flag = EV_WRITE; + goto retry; + case SSL_ERROR_ZERO_RETURN: /* FALLTHROUGH */ + case SSL_ERROR_SYSCALL: + if (ret == 0) { + cte->host->up = HOST_DOWN; + (void)cte->validate_close(cte); + ssl_cleanup(cte); + if (cte->host->up == HOST_UP) + hce_notify_done(cte->host, + "ssl_read: check succeeded"); + else + hce_notify_done(cte->host, + "ssl_read: check failed"); + return; + } + /* FALLTHROUGH */ + default: + cte->host->up = HOST_DOWN; + ssl_error("cannot read"); + ssl_cleanup(cte); + hce_notify_done(cte->host, "ssl_read: SSL error"); + break; + } + return; + } + buf_add(cte->buf, rbuf, ret); + + if (cte->validate_read != NULL) { + if (cte->validate_read(cte) != 0) + goto retry; + + ssl_cleanup(cte); + if (cte->host->up == HOST_UP) + hce_notify_done(cte->host, "ssl_read: check succeeded"); + else + hce_notify_done(cte->host, "ssl_read: check failed"); + return; + } + +retry: + log_debug("ssl_read: scheduling ssl_read on %s", + (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE"); + event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_read, + &cte->tv_start, &cte->table->timeout, cte); + return; +} + +void +ssl_write(int s, short event, void *arg) +{ + struct ctl_tcp_event *cte = arg; + int len; + int ret; + int ssl_err; + int retry_flag; + + if (event == EV_TIMEOUT) { + cte->host->up = HOST_DOWN; + ssl_cleanup(cte); + hce_notify_done(cte->host, "ssl_write: timeout"); + return; + } + + log_debug("ssl_write: event occurred"); + len = strlen(cte->table->sendbuf); + retry_flag = EV_WRITE; + + ret = SSL_write(cte->ssl, cte->table->sendbuf, len); + + if (ret <= 0) { + ssl_err = SSL_get_error(cte->ssl, ret); + switch (ssl_err) { + case SSL_ERROR_WANT_READ: + log_debug("ssl_write: want read"); + retry_flag = EV_READ; + goto retry; + case SSL_ERROR_WANT_WRITE: + log_debug("ssl_write: want write"); + retry_flag = EV_WRITE; + goto retry; + default: + cte->host->up = HOST_DOWN; + ssl_error("cannot write"); + ssl_cleanup(cte); + hce_notify_done(cte->host, "ssl_write: SSL error"); + return; + } + } + if ((cte->buf = buf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) + fatalx("ssl_write: cannot create dynamic buffer"); + + log_debug("ssl_write: scheduling ssl_read on EV_READ"); + event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, ssl_read, + &cte->tv_start, &cte->table->timeout, cte); + return; +retry: + log_debug("ssl_write: scheduling ssl_write on %s", + (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE"); + event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_write, + &cte->tv_start, &cte->table->timeout, cte); +} + +void +ssl_connect(int s, short event, void *arg) +{ + struct ctl_tcp_event *cte = arg; + int ret; + int ssl_err; + int retry_flag; + + if (event == EV_TIMEOUT) { + cte->host->up = HOST_DOWN; + hce_notify_done(cte->host, "ssl_connect: timeout"); + return; + } + + retry_flag = ssl_err = 0; + + ret = SSL_connect(cte->ssl); + + if (ret <= 0) { + ssl_err = SSL_get_error(cte->ssl, ret); + switch (ssl_err) { + case SSL_ERROR_WANT_READ: + log_debug("ssl_connect: want read"); + retry_flag = EV_READ; + goto retry; + case SSL_ERROR_WANT_WRITE: + log_debug("ssl_connect: want write"); + retry_flag = EV_WRITE; + goto retry; + default: + cte->host->up = HOST_DOWN; + ssl_error("ssl_connect: cannot connect"); + hce_notify_done(cte->host, "ssl_connect: SSL error"); + ssl_cleanup(cte); + return; + } + } + + if (cte->table->check == CHECK_TCP) { + cte->host->up = HOST_UP; + hce_notify_done(cte->host, "ssl_connect: connect successful"); + ssl_cleanup(cte); + return; + } + log_debug("ssl_connect: connect succeeded"); + if (cte->table->sendbuf != NULL) { + log_debug("ssl_connect: scheduling ssl_write on EV_WRITE"); + event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, ssl_write, + &cte->tv_start, &cte->table->timeout, cte); + return; + } + + if ((cte->buf = buf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) + fatalx("ssl_connect: cannot create dynamic buffer"); + log_debug("ssl_connect: scheduling ssl_read on EV_READ"); + event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ, ssl_read, + &cte->tv_start, &cte->table->timeout, cte); + return; + +retry: + log_debug("ssl_write: scheduling ssl_write on %s", + (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE"); + event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_connect, + &cte->tv_start, &cte->table->timeout, cte); +} + +void +ssl_cleanup(struct ctl_tcp_event *cte) +{ + log_debug("ssl_cleanup: cleaning for %s", cte->host->name); + close(cte->s); + if (cte->ssl != NULL) + SSL_free(cte->ssl); + if (cte->buf != NULL) + buf_free(cte->buf); +} + +void +ssl_error(const char *msg) +{ + unsigned long code; + char errbuf[128]; + + + for (; (code = ERR_get_error()) != 0 ;) { + ERR_error_string_n(code, errbuf, sizeof(errbuf)); + log_debug("ssl_error: %s: %s", msg, errbuf); + } +} + +void +ssl_init(struct hoststated *env) +{ + SSL_library_init(); + SSL_load_error_strings(); +} + +void +ssl_transaction(struct ctl_tcp_event *cte) +{ + cte->ssl = SSL_new(cte->table->ssl_ctx); + if (cte->ssl == NULL) { + ssl_error("ssl_transaction: cannot create object"); + fatal("cannot create SSL object"); + } + + if (SSL_set_fd(cte->ssl, cte->s) == 0) { + cte->host->up = HOST_UNKNOWN; + ssl_error("ssl_transaction: cannot set fd"); + ssl_cleanup(cte); + hce_notify_done(cte->host, "cannot set SSL fd"); + return; + } + SSL_set_connect_state(cte->ssl); + + event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, ssl_connect, + &cte->tv_start, &cte->table->timeout, cte); +} + +SSL_CTX * +ssl_ctx_create(struct hoststated *env) +{ + SSL_CTX *ctx; + + ctx = SSL_CTX_new(SSLv23_client_method()); + if (ctx == NULL) { + ssl_error("ssl_ctx_create: cannot create context"); + fatal("could not create SSL context"); + } + return (ctx); +} |