aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReyk Floeter <reyk@esdenera.com>2015-02-05 11:39:23 +0100
committerReyk Floeter <reyk@esdenera.com>2015-02-05 11:52:49 +0100
commit2496a64b6cf81b3e41295a1904649e3eb40bd654 (patch)
treef21bf70147e395641e16bca81fae3254f5ce8947
parenta0f456c3c650c7d5066a64b05a73011ddc12c23a (diff)
downloadhttpd-2496a64b6cf81b3e41295a1904649e3eb40bd654.tar.gz
httpd-2496a64b6cf81b3e41295a1904649e3eb40bd654.zip
Implement the "block/pass" directives for blocking, dropping and redirections.
-rw-r--r--config.c39
-rw-r--r--httpd.conf.532
-rw-r--r--httpd.h8
-rw-r--r--parse.y59
-rw-r--r--server.c1
-rw-r--r--server_http.c22
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 <reyk@openbsd.org>
@@ -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 <v.string> STRING
%token <v.number> NUMBER
%type <v.port> port
%type <v.number> opttls
%type <v.tv> timeout
-%type <v.string> numberstring
+%type <v.string> numberstring optstring
%type <v.auth> 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 <reyk@openbsd.org>
@@ -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