summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Obser <florian@cvs.openbsd.org>2015-01-18 14:01:18 +0000
committerFlorian Obser <florian@cvs.openbsd.org>2015-01-18 14:01:18 +0000
commitf55c920eb4a59e8b8251258da2fbc89f381cbc05 (patch)
tree0aa1f62a038cbe3ba6b0abf93dbc3195691ed3fc
parentb58b7b50aa0f6b94702c2daa0337c79b80b11add (diff)
First stab at implementing basic auth.
Currently the htpasswd file needs to be in the chroot; will hopefully improved soonish. Based on a diff from Oscar Linderholm many months ago but turned into a complete rewrite. input/OK reyk@
-rw-r--r--usr.sbin/httpd/httpd.conf.514
-rw-r--r--usr.sbin/httpd/httpd.h9
-rw-r--r--usr.sbin/httpd/parse.y40
-rw-r--r--usr.sbin/httpd/server_fcgi.c10
-rw-r--r--usr.sbin/httpd/server_http.c99
5 files changed, 162 insertions, 10 deletions
diff --git a/usr.sbin/httpd/httpd.conf.5 b/usr.sbin/httpd/httpd.conf.5
index 08efe74f7a7..f1cf3cc4e4a 100644
--- a/usr.sbin/httpd/httpd.conf.5
+++ b/usr.sbin/httpd/httpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: httpd.conf.5,v 1.44 2015/01/13 09:21:15 reyk Exp $
+.\" $OpenBSD: httpd.conf.5,v 1.45 2015/01/18 14:01:17 florian Exp $
.\"
.\" Copyright (c) 2014, 2015 Reyk Floeter <reyk@openbsd.org>
.\"
@@ -14,7 +14,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: January 13 2015 $
+.Dd $Mdocdate: January 18 2015 $
.Dt HTTPD.CONF 5
.Os
.Sh NAME
@@ -139,6 +139,15 @@ and include one or more lines of the following syntax:
Specify an additional alias
.Ar name
for this server.
+.It Ic authenticate Oo Ar realm Oc Ic with Pa htpasswd
+Authenticate a remote user for
+.Ar realm
+by checking the credentials against the user authentication file
+.Pa htpasswd .
+This file needs to be readable by the user
+.Xr httpd 8
+drops to
+.Pq www by default Pc .
.It Ic connection Ar option
Set the specified options and limits for HTTP connections.
Valid options are:
@@ -437,6 +446,7 @@ file directly:
include "/etc/nginx/mime.types"
.Ed
.Sh SEE ALSO
+.Xr htpasswd 1 ,
.Xr httpd 8
.Sh AUTHORS
.An -nosplit
diff --git a/usr.sbin/httpd/httpd.h b/usr.sbin/httpd/httpd.h
index a6f470658b7..0822a5b3014 100644
--- a/usr.sbin/httpd/httpd.h
+++ b/usr.sbin/httpd/httpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: httpd.h,v 1.68 2015/01/16 06:40:17 deraadt Exp $ */
+/* $OpenBSD: httpd.h,v 1.69 2015/01/18 14:01:17 florian Exp $ */
/*
* Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -296,6 +296,7 @@ struct client {
int clt_fcgi_type;
int clt_fcgi_chunked;
int clt_fcgi_end;
+ char *clt_fcgi_remote_user;
struct evbuffer *clt_srvevb;
struct evbuffer *clt_log;
@@ -324,11 +325,13 @@ SPLAY_HEAD(client_tree, client);
#define SRVFLAG_TLS 0x00002000
#define SRVFLAG_ACCESS_LOG 0x00004000
#define SRVFLAG_ERROR_LOG 0x00008000
+#define SRVFLAG_AUTH_BASIC 0x00010000
#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"
+ "\14SYSLOG\15NO_SYSLOG\16TLS\17ACCESS_LOG\20ERROR_LOG" \
+ "\21AUTH_BASIC"
#define TCPFLAG_NODELAY 0x01
#define TCPFLAG_NNODELAY 0x02
@@ -367,6 +370,8 @@ struct server_config {
char socket[PATH_MAX];
char accesslog[NAME_MAX];
char errorlog[NAME_MAX];
+ char auth_realm[NAME_MAX];
+ char auth_htpasswd[PATH_MAX];
in_port_t port;
struct sockaddr_storage ss;
diff --git a/usr.sbin/httpd/parse.y b/usr.sbin/httpd/parse.y
index f06927095a2..b47173a0f69 100644
--- a/usr.sbin/httpd/parse.y
+++ b/usr.sbin/httpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.54 2015/01/16 06:40:17 deraadt Exp $ */
+/* $OpenBSD: parse.y,v 1.55 2015/01/18 14:01:17 florian Exp $ */
/*
* Copyright (c) 2007 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -131,7 +131,7 @@ 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
+%token ERROR INCLUDE AUTHENTICATE WITH
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.port> port
@@ -439,6 +439,7 @@ serveroptsl : LISTEN ON STRING opttls port {
| directory
| logformat
| fastcgi
+ | authenticate
| LOCATION STRING {
struct server *s;
@@ -644,6 +645,37 @@ rootflags : STRING {
}
;
+authenticate : AUTHENTICATE STRING WITH STRING {
+ if (strlcpy(srv->srv_conf.auth_realm, $2,
+ sizeof(srv->srv_conf.auth_realm)) >=
+ sizeof(srv->srv_conf.auth_realm)) {
+ yyerror("basic auth realm name too long");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ if (strlcpy(srv->srv_conf.auth_htpasswd, $4,
+ sizeof(srv->srv_conf.auth_htpasswd)) >=
+ sizeof(srv->srv_conf.auth_htpasswd)) {
+ yyerror("password file name too long");
+ free($4);
+ YYERROR;
+ }
+ free($4);
+ srv->srv_conf.flags |= SRVFLAG_AUTH_BASIC;
+ }
+ | AUTHENTICATE WITH STRING {
+ if (strlcpy(srv->srv_conf.auth_htpasswd, $3,
+ sizeof(srv->srv_conf.auth_htpasswd)) >=
+ sizeof(srv->srv_conf.auth_htpasswd)) {
+ yyerror("password file name too long");
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ srv->srv_conf.flags |= SRVFLAG_AUTH_BASIC;
+ };
+
directory : DIRECTORY dirflags
| DIRECTORY '{' optnl dirflags_l '}'
;
@@ -950,6 +982,7 @@ lookup(char *s)
static const struct keywords keywords[] = {
{ "access", ACCESS },
{ "alias", ALIAS },
+ { "authenticate", AUTHENTICATE},
{ "auto", AUTO },
{ "backlog", BACKLOG },
{ "body", BODY },
@@ -989,7 +1022,8 @@ lookup(char *s)
{ "tcp", TCP },
{ "timeout", TIMEOUT },
{ "tls", TLS },
- { "types", TYPES }
+ { "types", TYPES },
+ { "with", WITH }
};
const struct keywords *p;
diff --git a/usr.sbin/httpd/server_fcgi.c b/usr.sbin/httpd/server_fcgi.c
index d4135aa3925..7305eb00847 100644
--- a/usr.sbin/httpd/server_fcgi.c
+++ b/usr.sbin/httpd/server_fcgi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server_fcgi.c,v 1.46 2015/01/16 06:40:17 deraadt Exp $ */
+/* $OpenBSD: server_fcgi.c,v 1.47 2015/01/18 14:01:17 florian Exp $ */
/*
* Copyright (c) 2014 Florian Obser <florian@openbsd.org>
@@ -259,6 +259,14 @@ server_fcgi(struct httpd *env, struct client *clt)
goto fail;
}
+ if (srv_conf->flags & SRVFLAG_AUTH_BASIC) {
+ if (fcgi_add_param(&param, "REMOTE_USER",
+ clt->clt_fcgi_remote_user, clt) == -1) {
+ errstr = "failed to encode param";
+ goto fail;
+ }
+ }
+
/* Add HTTP_* headers */
if (server_headers(clt, desc, server_fcgi_writeheader, &param) == -1) {
errstr = "failed to encode param";
diff --git a/usr.sbin/httpd/server_http.c b/usr.sbin/httpd/server_http.c
index c5dd4ab5650..5bf6bd517fe 100644
--- a/usr.sbin/httpd/server_http.c
+++ b/usr.sbin/httpd/server_http.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server_http.c,v 1.64 2015/01/16 06:40:17 deraadt Exp $ */
+/* $OpenBSD: server_http.c,v 1.65 2015/01/18 14:01:17 florian Exp $ */
/*
* Copyright (c) 2006 - 2015 Reyk Floeter <reyk@openbsd.org>
@@ -38,6 +38,7 @@
#include <stdio.h>
#include <err.h>
#include <pwd.h>
+#include <resolv.h>
#include <syslog.h>
#include <event.h>
#include <fnmatch.h>
@@ -48,6 +49,8 @@
static int server_httpmethod_cmp(const void *, const void *);
static int server_httperror_cmp(const void *, const void *);
void server_httpdesc_free(struct http_descriptor *);
+int server_http_authenticate(struct server_config *,
+ struct client *);
static struct httpd *env = NULL;
@@ -128,6 +131,82 @@ server_httpdesc_free(struct http_descriptor *desc)
desc->http_chunked = 0;
}
+int
+server_http_authenticate(struct server_config *srv_conf, struct client *clt)
+{
+ FILE *fp = NULL;
+ struct http_descriptor *desc = clt->clt_descreq;
+ struct kv *ba, key;
+ size_t linesize = 0;
+ ssize_t linelen;
+ int ret = -1;
+ char *line = NULL, decoded[1024];
+ char *clt_user = NULL, *clt_pass = NULL, *user = NULL, *pass = NULL;
+
+ memset(decoded, 0, sizeof(decoded));
+ key.kv_key = "Authorization";
+
+ if ((ba = kv_find(&desc->http_headers, &key)) == NULL ||
+ ba->kv_value == NULL)
+ goto done;
+
+ if (strncmp(ba->kv_value, "Basic ", strlen("Basic ")) != 0)
+ goto done;
+
+ if (b64_pton(strchr(ba->kv_value, ' ') + 1, decoded,
+ sizeof(decoded)) <= 0)
+ goto done;
+
+ if ((clt_pass = strchr(decoded, ':')) == NULL)
+ goto done;
+
+ clt_user = decoded;
+ *clt_pass++ = '\0';
+
+ if (clt_pass == NULL)
+ goto done;
+
+ if ((fp = fopen(srv_conf->auth_htpasswd, "r")) == NULL)
+ goto done;
+
+ while ((linelen = getline(&line, &linesize, fp)) != -1) {
+ if (line[linelen - 1] == '\n')
+ line[linelen - 1] = '\0';
+ user = line;
+ pass = strchr(line, ':');
+
+ if (pass == NULL) {
+ explicit_bzero(line, linelen);
+ continue;
+ }
+
+ *pass++ = '\0';
+
+ if (strcmp(clt_user, user) != 0) {
+ explicit_bzero(line, linelen);
+ continue;
+ }
+
+ if (crypt_checkpass(clt_pass, pass) == 0) {
+ explicit_bzero(line, linelen);
+ clt->clt_fcgi_remote_user = strdup(clt_user);
+ if (clt->clt_fcgi_remote_user != NULL)
+ ret = 0;
+ break;
+ }
+ }
+done:
+ if (fp != NULL)
+ fclose(fp);
+
+ if (ba != NULL && ba->kv_value != NULL) {
+ explicit_bzero(ba->kv_value, strlen(ba->kv_value));
+ explicit_bzero(decoded, sizeof(decoded));
+ }
+
+ return (ret);
+}
+
void
server_read_http(struct bufferevent *bev, void *arg)
{
@@ -551,6 +630,8 @@ server_reset_http(struct client *clt)
clt->clt_line = 0;
clt->clt_done = 0;
clt->clt_chunk = 0;
+ free(clt->clt_fcgi_remote_user);
+ clt->clt_fcgi_remote_user = NULL;
clt->clt_bev->readcb = server_read_http;
clt->clt_srv_conf = &srv->srv_conf;
@@ -687,6 +768,13 @@ server_abort_http(struct client *clt, u_int code, const char *msg)
extraheader = NULL;
}
break;
+ case 401:
+ if (asprintf(&extraheader,
+ "WWW-Authenticate: Basic realm=\"%s\"\r\n", msg) == -1) {
+ code = 500;
+ extraheader = NULL;
+ }
+ break;
default:
/*
* Do not send details of the error. Traditionally,
@@ -764,6 +852,8 @@ server_close_http(struct client *clt)
server_httpdesc_free(desc);
free(desc);
clt->clt_descresp = NULL;
+ free(clt->clt_fcgi_remote_user);
+ clt->clt_fcgi_remote_user = NULL;
}
int
@@ -874,7 +964,12 @@ server_response(struct httpd *httpd, struct client *clt)
/* Now search for the location */
srv_conf = server_getlocation(clt, desc->http_path);
- return (server_file(httpd, clt));
+ if (srv_conf->flags & SRVFLAG_AUTH_BASIC &&
+ server_http_authenticate(srv_conf, clt) == -1) {
+ server_abort_http(clt, 401, srv_conf->auth_realm);
+ return (-1);
+ } else
+ return (server_file(httpd, clt));
fail:
server_abort_http(clt, 400, "bad request");
return (-1);