Home | History | Annotate | Line # | Download | only in httpd
auth-bozo.c revision 1.19
      1 /*	$NetBSD: auth-bozo.c,v 1.19 2018/11/19 04:13:09 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 		/* free prior entries. */
    151 		free(request->hr_authuser);
    152 		free(request->hr_authpass);
    153 
    154 		alen = base64_decode((unsigned char *)str + 6,
    155 					(size_t)(len - 6),
    156 					(unsigned char *)authbuf,
    157 					sizeof(authbuf) - 1);
    158 		if (alen != -1)
    159 			authbuf[alen] = '\0';
    160 		if (alen == -1 ||
    161 		    (pass = strchr(authbuf, ':')) == NULL)
    162 			return bozo_http_error(httpd, 400, request,
    163 			    "bad authorization field");
    164 		*pass++ = '\0';
    165 		request->hr_authuser = bozostrdup(httpd, request, authbuf);
    166 		request->hr_authpass = bozostrdup(httpd, request, pass);
    167 		debug((httpd, DEBUG_FAT,
    168 		    "decoded authorization `%s' as `%s':`%s'",
    169 		    str, request->hr_authuser, request->hr_authpass));
    170 			/* don't store in request->headers */
    171 		return 1;
    172 	}
    173 	return 0;
    174 }
    175 
    176 int
    177 bozo_auth_check_special_files(bozo_httpreq_t *request,
    178 				const char *name)
    179 {
    180 	bozohttpd_t *httpd = request->hr_httpd;
    181 
    182 	if (strcmp(name, AUTH_FILE) == 0)
    183 		return bozo_http_error(httpd, 403, request,
    184 				"no permission to open authfile");
    185 	return 0;
    186 }
    187 
    188 void
    189 bozo_auth_check_401(bozo_httpreq_t *request, int code)
    190 {
    191 	bozohttpd_t *httpd = request->hr_httpd;
    192 
    193 	if (code == 401)
    194 		bozo_printf(httpd,
    195 			"WWW-Authenticate: Basic realm=\"%s\"\r\n",
    196 			request->hr_authrealm ?
    197 			request->hr_authrealm : "default realm");
    198 }
    199 
    200 #ifndef NO_CGIBIN_SUPPORT
    201 void
    202 bozo_auth_cgi_setenv(bozo_httpreq_t *request,
    203 			char ***curenvpp)
    204 {
    205 	bozohttpd_t *httpd = request->hr_httpd;
    206 
    207 	if (request->hr_authuser && *request->hr_authuser) {
    208 		bozo_setenv(httpd, "AUTH_TYPE", "Basic", (*curenvpp)++);
    209 		bozo_setenv(httpd, "REMOTE_USER", request->hr_authuser,
    210 				(*curenvpp)++);
    211 	}
    212 }
    213 
    214 int
    215 bozo_auth_cgi_count(bozo_httpreq_t *request)
    216 {
    217 	return (request->hr_authuser && *request->hr_authuser) ? 2 : 0;
    218 }
    219 #endif /* NO_CGIBIN_SUPPORT */
    220 
    221 /*
    222  * Decode len bytes starting at in using base64 encoding into out.
    223  * Result is *not* NUL terminated.
    224  * Written by Luke Mewburn <lukem (at) NetBSD.org>
    225  */
    226 const unsigned char decodetable[] = {
    227 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    228 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    229 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
    230 	 52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255,   0, 255, 255,
    231 	255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
    232 	 15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,
    233 	255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
    234 	 41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255,
    235 };
    236 
    237 static ssize_t
    238 base64_decode(const unsigned char *in, size_t ilen, unsigned char *out,
    239 	      size_t olen)
    240 {
    241 	unsigned char *cp;
    242 	size_t	 i;
    243 
    244 	if (ilen == 0) {
    245 		if (olen)
    246 			*out = '\0';
    247 		return 0;
    248 	}
    249 
    250 	cp = out;
    251 	for (i = 0; i < ilen; i += 4) {
    252 		if (cp + 3 > out + olen)
    253 			return (-1);
    254 #define IN_CHECK(x) \
    255 		if ((x) > sizeof(decodetable) || decodetable[(x)] == 255) \
    256 			    return(-1)
    257 
    258 		IN_CHECK(in[i + 0]);
    259 		/*LINTED*/
    260 		*(cp++) = decodetable[in[i + 0]] << 2
    261 			| decodetable[in[i + 1]] >> 4;
    262 		IN_CHECK(in[i + 1]);
    263 		/*LINTED*/
    264 		*(cp++) = decodetable[in[i + 1]] << 4
    265 			| decodetable[in[i + 2]] >> 2;
    266 		IN_CHECK(in[i + 2]);
    267 		*(cp++) = decodetable[in[i + 2]] << 6
    268 			| decodetable[in[i + 3]];
    269 #undef IN_CHECK
    270 	}
    271 	while (i > 0 && in[i - 1] == '=')
    272 		cp--,i--;
    273 	return (cp - out);
    274 }
    275 #endif /* DO_HTPASSWD */
    276