From 2496a64b6cf81b3e41295a1904649e3eb40bd654 Mon Sep 17 00:00:00 2001 From: Reyk Floeter Date: Thu, 5 Feb 2015 11:39:23 +0100 Subject: Implement the "block/pass" directives for blocking, dropping and redirections. --- config.c | 39 +++++++++++++++++++++++++++++++++++++-- httpd.conf.5 | 32 ++++++++++++++++++++++++++++++++ httpd.h | 8 +++++++- parse.y | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- server.c | 1 + server_http.c | 22 ++++++++++++++++------ 6 files changed, 150 insertions(+), 11 deletions(-) diff --git a/config.c b/config.c index 28fbecb..7cd94c6 100644 --- a/config.c +++ b/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.32 2015/01/21 22:21:05 reyk Exp $ */ +/* $OpenBSD: config.c,v 1.33 2015/02/05 10:46:17 reyk Exp $ */ /* * Copyright (c) 2011 - 2015 Reyk Floeter @@ -189,6 +189,10 @@ config_setserver(struct httpd *env, struct server *srv) c = 0; iov[c].iov_base = &s; iov[c++].iov_len = sizeof(s); + if (srv->srv_conf.return_uri_len != 0) { + iov[c].iov_base = srv->srv_conf.return_uri; + iov[c++].iov_len = srv->srv_conf.return_uri_len; + } if (srv->srv_conf.tls_cert_len != 0) { iov[c].iov_base = srv->srv_conf.tls_cert; iov[c++].iov_len = srv->srv_conf.tls_cert_len; @@ -246,12 +250,14 @@ config_getserver_config(struct httpd *env, struct server *srv, struct server_config *srv_conf, *parent; u_int8_t *p = imsg->data; u_int f; + size_t s; if ((srv_conf = calloc(1, sizeof(*srv_conf))) == NULL) return (-1); IMSG_SIZE_CHECK(imsg, srv_conf); memcpy(srv_conf, p, sizeof(*srv_conf)); + s = sizeof(*srv_conf); /* Reset these variables to avoid free'ing invalid pointers */ serverconfig_reset(srv_conf); @@ -264,7 +270,7 @@ config_getserver_config(struct httpd *env, struct server *srv, parent = &srv->srv_conf; if (config_getserver_auth(env, srv_conf) != 0) - return (-1); + goto fail; if (srv_conf->flags & SRVFLAG_LOCATION) { /* Inherit configuration from the parent */ @@ -336,6 +342,24 @@ config_getserver_config(struct httpd *env, struct server *srv, sizeof(srv_conf->errorlog)); } + f = SRVFLAG_BLOCK|SRVFLAG_NO_BLOCK; + if ((srv_conf->flags & f) == 0) { + srv_conf->flags |= parent->flags & f; + srv_conf->return_code = parent->return_code; + srv_conf->return_uri_len = parent->return_uri_len; + if (srv_conf->return_uri_len && + (srv_conf->return_uri = + strdup(parent->return_uri)) == NULL) + goto fail; + } else { + if (srv_conf->return_uri_len != 0) { + if ((srv_conf->return_uri = get_data(p + s, + srv_conf->return_uri_len)) == NULL) + goto fail; + s += srv_conf->return_uri_len; + } + } + memcpy(&srv_conf->timeout, &parent->timeout, sizeof(srv_conf->timeout)); srv_conf->maxrequests = parent->maxrequests; @@ -358,6 +382,11 @@ config_getserver_config(struct httpd *env, struct server *srv, TAILQ_INSERT_TAIL(&srv->srv_hosts, srv_conf, entry); return (0); + + fail: + serverconfig_free(srv_conf); + free(srv_conf); + return (-1); } int @@ -421,6 +450,12 @@ config_getserver(struct httpd *env, struct imsg *imsg) srv->srv_conf.name, srv->srv_conf.id, printb_flags(srv->srv_conf.flags, SRVFLAG_BITS)); + if (srv->srv_conf.return_uri_len != 0) { + if ((srv->srv_conf.return_uri = get_data(p + s, + srv->srv_conf.return_uri_len)) == NULL) + goto fail; + s += srv->srv_conf.return_uri_len; + } if (srv->srv_conf.tls_cert_len != 0) { if ((srv->srv_conf.tls_cert = get_data(p + s, srv->srv_conf.tls_cert_len)) == NULL) diff --git a/httpd.conf.5 b/httpd.conf.5 index f40273c..6db62e8 100644 --- a/httpd.conf.5 +++ b/httpd.conf.5 @@ -150,6 +150,20 @@ and must be readable by the www user. Use the .Ic no authenticate directive to disable authentication in a location. +.It Ic block drop +Drop the connection without sending an error page. +.It Ic block Op Ic return Ar code Op uri +Close the connection and send an error page. +If the optional return code is not specified, +.Xr httpd 8 +denies access with a +.Sq 404 Forbidden +response. +The optional +.Ar uri +argument can be used with return codes in the 3xx range to send a +.Sq Location: +header for redirection to a specified URI. .It Ic connection Ar option Set the specified options and limits for HTTP connections. Valid options are: @@ -261,6 +275,10 @@ Enable or disable logging to .Xr syslog 3 instead of the log files. .El +.It Ic pass +Disable any previous +.Ic block +in a location. .It Ic root Ar option Configure the document root and options for the request path. Valid options are: @@ -440,6 +458,20 @@ server "intranet.example.com" { } .Ed .Pp +Simple redirections can be configured with the +.Ic block +directive: +.Bd -literal -offset indent +server "example.com" { + listen on 10.0.0.1 port 80 + block return 301 "http://www.example.com/" +} + +server "www.example.com" { + listen on 10.0.0.1 port 80 +} +.Ed +.Pp The syntax of the types section is also compatible with the format used by nginx, so it is possible to include its .Pa mime.types diff --git a/httpd.h b/httpd.h index fbf125a..24e1f9a 100644 --- a/httpd.h +++ b/httpd.h @@ -337,12 +337,14 @@ SPLAY_HEAD(client_tree, client); #define SRVFLAG_ERROR_LOG 0x00008000 #define SRVFLAG_AUTH 0x00010000 #define SRVFLAG_NO_AUTH 0x00020000 +#define SRVFLAG_BLOCK 0x00040000 +#define SRVFLAG_NO_BLOCK 0x00080000 #define SRVFLAG_BITS \ "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX" \ "\05ROOT\06LOCATION\07FCGI\10NO_FCGI\11LOG\12NO_LOG\13SOCKET" \ "\14SYSLOG\15NO_SYSLOG\16TLS\17ACCESS_LOG\20ERROR_LOG" \ - "\21AUTH\22NO_AUTH" + "\21AUTH\22NO_AUTH\23BLOCK\24NO_BLOCK" #define TCPFLAG_NODELAY 0x01 #define TCPFLAG_NNODELAY 0x02 @@ -421,6 +423,10 @@ struct server_config { u_int32_t auth_id; struct auth *auth; + int return_code; + char *return_uri; + off_t return_uri_len; + TAILQ_ENTRY(server_config) entry; }; TAILQ_HEAD(serverhosts, server_config); diff --git a/parse.y b/parse.y index 7be13b1..2f8fbcd 100644 --- a/parse.y +++ b/parse.y @@ -133,13 +133,13 @@ typedef struct { %token COMBINED CONNECTION DIRECTORY ERR FCGI INDEX IP KEY LISTEN LOCATION %token LOG LOGDIR MAXIMUM NO NODELAY ON PORT PREFORK REQUEST REQUESTS ROOT %token SACK SERVER SOCKET STRIP STYLE SYSLOG TCP TIMEOUT TLS TYPES -%token ERROR INCLUDE AUTHENTICATE WITH +%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS %token STRING %token NUMBER %type port %type opttls %type timeout -%type numberstring +%type numberstring optstring %type authopts %% @@ -439,6 +439,7 @@ serveroptsl : LISTEN ON STRING opttls port { | logformat | fastcgi | authenticate + | filter | LOCATION STRING { struct server *s; @@ -798,6 +799,50 @@ logstyle : COMMON { } ; +filter : block RETURN NUMBER optstring { + if ($3 <= 0 || server_httperror_byid($3) == NULL) { + yyerror("invalid return code: %lld", $3); + free($4); + YYERROR; + } + srv_conf->return_code = $3; + + if ($4 != NULL) { + /* Only for 3xx redirection headers */ + if ($3 < 300 || $3 > 399) { + yyerror("invalid return code for " + "location URI"); + free($4); + YYERROR; + } + srv_conf->return_uri = $4; + srv_conf->return_uri_len = strlen($4) + 1; + } + } + | block DROP { + /* No return code, silently drop the connection */ + srv_conf->return_code = 0; + } + | block { + /* Forbidden */ + srv_conf->return_code = 403; + } + | PASS { + srv_conf->flags &= ~SRVFLAG_BLOCK; + srv_conf->flags |= SRVFLAG_NO_BLOCK; + } + ; + +block : BLOCK { + srv_conf->flags &= ~SRVFLAG_NO_BLOCK; + srv_conf->flags |= SRVFLAG_BLOCK; + } + ; + +optstring : /* empty */ { $$ = NULL; } + | STRING { $$ = $1; } + ; + tcpip : TCP '{' optnl tcpflags_l '}' | TCP tcpflags ; @@ -995,6 +1040,7 @@ lookup(char *s) { "authenticate", AUTHENTICATE}, { "auto", AUTO }, { "backlog", BACKLOG }, + { "block", BLOCK }, { "body", BODY }, { "buffer", BUFFER }, { "certificate", CERTIFICATE }, @@ -1004,6 +1050,7 @@ lookup(char *s) { "common", COMMON }, { "connection", CONNECTION }, { "directory", DIRECTORY }, + { "drop", DROP }, { "error", ERR }, { "fastcgi", FCGI }, { "include", INCLUDE }, @@ -1018,10 +1065,12 @@ lookup(char *s) { "no", NO }, { "nodelay", NODELAY }, { "on", ON }, + { "pass", PASS }, { "port", PORT }, { "prefork", PREFORK }, { "request", REQUEST }, { "requests", REQUESTS }, + { "return", RETURN }, { "root", ROOT }, { "sack", SACK }, { "server", SERVER }, @@ -1801,6 +1850,7 @@ server_inherit(struct server *src, const char *name, /* Copy the source server and assign a new Id */ memcpy(&dst->srv_conf, &src->srv_conf, sizeof(dst->srv_conf)); + if ((dst->srv_conf.tls_cert_file = strdup(src->srv_conf.tls_cert_file)) == NULL) fatal("out of memory"); @@ -1810,6 +1860,11 @@ server_inherit(struct server *src, const char *name, dst->srv_conf.tls_cert = NULL; dst->srv_conf.tls_key = NULL; + if (src->srv_conf.return_uri != NULL && + (dst->srv_conf.return_uri = + strdup(src->srv_conf.return_uri)) == NULL) + fatal("out of memory"); + dst->srv_conf.id = ++last_server_id; dst->srv_conf.parent_id = dst->srv_conf.id; dst->srv_s = -1; diff --git a/server.c b/server.c index 15e1453..3c2632e 100644 --- a/server.c +++ b/server.c @@ -324,6 +324,7 @@ server_purge(struct server *srv) void serverconfig_free(struct server_config *srv_conf) { + free(srv_conf->return_uri); free(srv_conf->tls_cert_file); free(srv_conf->tls_cert); free(srv_conf->tls_key_file); diff --git a/server_http.c b/server_http.c index 0113a82..f7990b0 100644 --- a/server_http.c +++ b/server_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server_http.c,v 1.69 2015/01/21 22:21:05 reyk Exp $ */ +/* $OpenBSD: server_http.c,v 1.70 2015/02/05 10:47:53 reyk Exp $ */ /* * Copyright (c) 2006 - 2015 Reyk Floeter @@ -736,6 +736,11 @@ server_abort_http(struct client *clt, u_int code, const char *msg) char tmbuf[32], hbuf[128]; int bodylen; + if (code == 0) { + server_close(clt, "dropped"); + return; + } + if ((httperr = server_httperror_byid(code)) == NULL) httperr = "Unknown Error"; @@ -957,7 +962,11 @@ server_response(struct httpd *httpd, struct client *clt) /* Now search for the location */ srv_conf = server_getlocation(clt, desc->http_path); - if (srv_conf->flags & SRVFLAG_AUTH && + if (srv_conf->flags & SRVFLAG_BLOCK) { + server_abort_http(clt, srv_conf->return_code, + srv_conf->return_uri); + return (-1); + } else if (srv_conf->flags & SRVFLAG_AUTH && server_http_authenticate(srv_conf, clt) == -1) { server_abort_http(clt, 401, srv_conf->auth_realm); return (-1); @@ -1199,16 +1208,17 @@ server_httpmethod_cmp(const void *a, const void *b) const char * server_httperror_byid(u_int id) { - struct http_error error, *res = NULL; + struct http_error error, *res; /* Set up key */ error.error_code = (int)id; - res = bsearch(&error, http_errors, + if ((res = bsearch(&error, http_errors, sizeof(http_errors) / sizeof(http_errors[0]) - 1, - sizeof(http_errors[0]), server_httperror_cmp); + sizeof(http_errors[0]), server_httperror_cmp)) != NULL) + return (res->error_name); - return (res->error_name); + return (NULL); } static int -- cgit v1.2.3-54-g00ecf