Home | History | Annotate | Line # | Download | only in httpd
auth-bozo.c revision 1.18
      1 /*	$NetBSD: auth-bozo.c,v 1.18 2015/12/27 10:21:35 mrg Exp $	*/
      2 
      3 /*	$eterna: auth-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $	*/
      4 
      5 /*
      6  * Copyright (c) 1997-2014 Matthew R. Green
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer and
     16  *    dedication in the documentation and/or other materials provided
     17  *    with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  *
     31  */
     32 
     33 /* this code implements "http basic authorisation" for bozohttpd */
     34 
     35 #ifdef DO_HTPASSWD
     36 
     37 #include <sys/param.h>
     38 
     39 #include <string.h>
     40 #include <stdlib.h>
     41 #include <unistd.h>
     42 
     43 #include "bozohttpd.h"
     44 
     45 #ifndef AUTH_FILE
     46 #define AUTH_FILE		".htpasswd"
     47 #endif
     48 
     49 static	ssize_t	base64_decode(const unsigned char *, size_t,
     50 			    unsigned char *, size_t);
     51 
     52 /*
     53  * Check if HTTP authentication is required
     54  */
     55 int
     56 bozo_auth_check(bozo_httpreq_t *request, const char *file)
     57 {
     58 	bozohttpd_t *httpd = request->hr_httpd;
     59 	struct stat sb;
     60 	char dir[MAXPATHLEN], authfile[MAXPATHLEN], *basename;
     61 	char user[BUFSIZ], *pass;
     62 	FILE *fp;
     63 	int len;
     64 
     65 			/* get dir=dirname(file) */
     66 	snprintf(dir, sizeof(dir), "%s", file);
     67 	if ((basename = strrchr(dir, '/')) == NULL)
     68 		strcpy(dir, ".");
     69 	else {
     70 		*basename++ = '\0';
     71 			/* ensure basename(file) != AUTH_FILE */
     72 		if (bozo_check_special_files(request, basename))
     73 			return 1;
     74 	}
     75 	request->hr_authrealm = bozostrdup(httpd, request, dir);
     76 
     77 	if ((size_t)snprintf(authfile, sizeof(authfile), "%s/%s", dir,
     78 			     AUTH_FILE) >= sizeof(authfile)) {
     79 		return bozo_http_error(httpd, 404, request,
     80 			"authfile path too long");
     81 	}
     82 	if (stat(authfile, &sb) < 0) {
     83 		debug((httpd, DEBUG_NORMAL,
     84 		    "bozo_auth_check realm `%s' dir `%s' authfile `%s' missing",
     85 		    dir, file, authfile));
     86 		return 0;
     87 	}
     88 	if ((fp = fopen(authfile, "r")) == NULL)
     89 		return bozo_http_error(httpd, 403, request,
     90 			"no permission to open authfile");
     91 	debug((httpd, DEBUG_NORMAL,
     92 	    "bozo_auth_check realm `%s' dir `%s' authfile `%s' open",
     93 	    dir, file, authfile));
     94 	if (request->hr_authuser && request->hr_authpass) {
     95 		while (fgets(user, sizeof(user), fp) != NULL) {
     96 			len = strlen(user);
     97 			if (len > 0 && user[len-1] == '\n')
     98 				user[--len] = '\0';
     99 			if ((pass = strchr(user, ':')) == NULL)
    100 				continue;
    101 			*pass++ = '\0';
    102 			debug((httpd, DEBUG_NORMAL,
    103 			    "bozo_auth_check authfile `%s':`%s' "
    104 			    	"client `%s':`%s'",
    105 			    user, pass, request->hr_authuser,
    106 			    request->hr_authpass));
    107 			if (strcmp(request->hr_authuser, user) != 0)
    108 				continue;
    109 			if (strcmp(crypt(request->hr_authpass, pass),
    110 					pass) != 0)
    111 				break;
    112 			fclose(fp);
    113 			return 0;
    114 		}
    115 	}
    116 	fclose(fp);
    117 	return bozo_http_error(httpd, 401, request, "bad auth");
    118 }
    119 
    120 void
    121 bozo_auth_init(bozo_httpreq_t *request)
    122 {
    123 	request->hr_authuser = NULL;
    124 	request->hr_authpass = NULL;
    125 }
    126 
    127 void
    128 bozo_auth_cleanup(bozo_httpreq_t *request)
    129 {
    130 
    131 	if (request == NULL)
    132 		return;
    133 	free(request->hr_authuser);
    134 	free(request->hr_authpass);
    135 	free(request->hr_authrealm);
    136 }
    137 
    138 int
    139 bozo_auth_check_headers(bozo_httpreq_t *request, char *val, char *str,
    140 			ssize_t len)
    141 {
    142 	bozohttpd_t *httpd = request->hr_httpd;
    143 
    144 	if (strcasecmp(val, "authorization") == 0 &&
    145 	    strncasecmp(str, "Basic ", 6) == 0) {
    146 		char	authbuf[BUFSIZ];
    147 		char	*pass = NULL;
    148 		ssize_t	alen;
    149 
    150 		alen = base64_decode((unsigned char *)str + 6,
    151 					(size_t)(len - 6),
    152 					(unsigned char *)authbuf,
    153 					sizeof(authbuf) - 1);
    154 		if (alen != -1)
    155 			authbuf[alen] = '\0';
    156 		if (alen == -1 ||
    157 		    (pass = strchr(authbuf, ':')) == NULL)
    158 			return bozo_http_error(httpd, 400, request,
    159 			    "bad authorization field");
    160 		*pass++ = '\0';
    161 		free(request->hr_authuser);
    162 		free(request->hr_authpass);
    163 		request->hr_authuser = bozostrdup(httpd, request, authbuf);
    164 		request->hr_authpass = bozostrdup(httpd, request, pass);
    165 		debug((httpd, DEBUG_FAT,
    166 		    "decoded authorization `%s' as `%s':`%s'",
    167 		    str, request->hr_authuser, request->hr_authpass));
    168 			/* don't store in request->headers */
    169 		return 1;
    170 	}
    171 	return 0;
    172 }
    173 
    174 int
    175 bozo_auth_check_special_files(bozo_httpreq_t *request,
    176 				const char *name)
    177 {
    178 	bozohttpd_t *httpd = request->hr_httpd;
    179 
    180 	if (strcmp(name, AUTH_FILE) == 0)
    181 		return bozo_http_error(httpd, 403, request,
    182 				"no permission to open authfile");
    183 	return 0;
    184 }
    185 
    186 void
    187 bozo_auth_check_401(bozo_httpreq_t *request, int code)
    188 {
    189 	bozohttpd_t *httpd = request->hr_httpd;
    190 
    191 	if (code == 401)
    192 		bozo_printf(httpd,
    193 			"WWW-Authenticate: Basic realm=\"%s\"\r\n",
    194 			request->hr_authrealm ?
    195 			request->hr_authrealm : "default realm");
    196 }
    197 
    198 #ifndef NO_CGIBIN_SUPPORT
    199 void
    200 bozo_auth_cgi_setenv(bozo_httpreq_t *request,
    201 			char ***curenvpp)
    202 {
    203 	bozohttpd_t *httpd = request->hr_httpd;
    204 
    205 	if (request->hr_authuser && *request->hr_authuser) {
    206 		bozo_setenv(httpd, "AUTH_TYPE", "Basic", (*curenvpp)++);
    207 		bozo_setenv(httpd, "REMOTE_USER", request->hr_authuser,
    208 				(*curenvpp)++);
    209 	}
    210 }
    211 
    212 int
    213 bozo_auth_cgi_count(bozo_httpreq_t *request)
    214 {
    215 	return (request->hr_authuser && *request->hr_authuser) ? 2 : 0;
    216 }
    217 #endif /* NO_CGIBIN_SUPPORT */
    218 
    219 /*
    220  * Decode len bytes starting at in using base64 encoding into out.
    221  * Result is *not* NUL terminated.
    222  * Written by Luke Mewburn <lukem (at) NetBSD.org>
    223  */
    224 const unsigned char decodetable[] = {
    225 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    226 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    227 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
    228 	 52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255,   0, 255, 255,
    229 	255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
    230 	 15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,
    231 	255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
    232 	 41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255,
    233 };
    234 
    235 static ssize_t
    236 base64_decode(const unsigned char *in, size_t ilen, unsigned char *out,
    237 	      size_t olen)
    238 {
    239 	unsigned char *cp;
    240 	size_t	 i;
    241 
    242 	if (ilen == 0) {
    243 		if (olen)
    244 			*out = '\0';
    245 		return 0;
    246 	}
    247 
    248 	cp = out;
    249 	for (i = 0; i < ilen; i += 4) {
    250 		if (cp + 3 > out + olen)
    251 			return (-1);
    252 #define IN_CHECK(x) \
    253 		if ((x) > sizeof(decodetable) || decodetable[(x)] == 255) \
    254 			    return(-1)
    255 
    256 		IN_CHECK(in[i + 0]);
    257 		/*LINTED*/
    258 		*(cp++) = decodetable[in[i + 0]] << 2
    259 			| decodetable[in[i + 1]] >> 4;
    260 		IN_CHECK(in[i + 1]);
    261 		/*LINTED*/
    262 		*(cp++) = decodetable[in[i + 1]] << 4
    263 			| decodetable[in[i + 2]] >> 2;
    264 		IN_CHECK(in[i + 2]);
    265 		*(cp++) = decodetable[in[i + 2]] << 6
    266 			| decodetable[in[i + 3]];
    267 #undef IN_CHECK
    268 	}
    269 	while (i > 0 && in[i - 1] == '=')
    270 		cp--,i--;
    271 	return (cp - out);
    272 }
    273 #endif /* DO_HTPASSWD */
    274