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