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