1 /*	$NetBSD: auth-bozo.c,v 1.26 2020/10/15 02:19:23 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-2020 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 static	ssize_t	base64_decode(const unsigned char *, size_t,
46 			    unsigned char *, size_t);
47 
48 /*
49  * Check if HTTP authentication is required
50  */
51 int
bozo_auth_check(bozo_httpreq_t * request,const char * file)52 bozo_auth_check(bozo_httpreq_t *request, const char *file)
53 {
54 	bozohttpd_t *httpd = request->hr_httpd;
55 	struct stat sb;
56 	char dir[MAXPATHLEN], authfile[MAXPATHLEN], *basename;
57 	char user[BUFSIZ], *pass;
58 	FILE *fp;
59 	int len;
60 
61 			/* get dir=dirname(file) */
62 	snprintf(dir, sizeof(dir), "%s", file);
63 	if ((basename = strrchr(dir, '/')) == NULL)
64 		strcpy(dir, ".");
65 	else {
66 		*basename++ = '\0';
67 		if (bozo_check_special_files(request, basename, true))
68 			return 1;
69 	}
70 	request->hr_authrealm = bozostrdup(httpd, request, dir);
71 
72 	if ((size_t)snprintf(authfile, sizeof(authfile), "%s/%s", dir,
73 			     AUTH_FILE) >= sizeof(authfile)) {
74 		return bozo_http_error(httpd, 404, request,
75 			"authfile path too long");
76 	}
77 	if (stat(authfile, &sb) < 0) {
78 		debug((httpd, DEBUG_NORMAL,
79 		    "bozo_auth_check realm `%s' dir `%s' authfile `%s' missing",
80 		    dir, file, authfile));
81 		return 0;
82 	}
83 	if ((fp = fopen(authfile, "r")) == NULL)
84 		return bozo_http_error(httpd, 403, request,
85 			"no permission to open authfile");
86 	debug((httpd, DEBUG_NORMAL,
87 	    "bozo_auth_check realm `%s' dir `%s' authfile `%s' open",
88 	    dir, file, authfile));
89 	if (request->hr_authuser && request->hr_authpass) {
90 		while (fgets(user, sizeof(user), fp) != NULL) {
91 			len = strlen(user);
92 			if (len > 0 && user[len-1] == '\n')
93 				user[--len] = '\0';
94 			if ((pass = strchr(user, ':')) == NULL)
95 				continue;
96 			*pass++ = '\0';
97 			debug((httpd, DEBUG_NORMAL,
98 			    "bozo_auth_check authfile `%s':`%s' "
99 			    	"client `%s':`%s'",
100 			    user, pass, request->hr_authuser,
101 			    request->hr_authpass));
102 			if (strcmp(request->hr_authuser, user) != 0)
103 				continue;
104 			if (strcmp(crypt(request->hr_authpass, pass),
105 					pass) != 0)
106 				break;
107 			fclose(fp);
108 
109 #ifndef NO_BLACKLIST_SUPPORT
110 			pfilter_notify(BLACKLIST_AUTH_OK, 200);
111 #endif /* !NO_BLACKLIST_SUPPORT */
112 
113 			return 0;
114 		}
115 	}
116 	fclose(fp);
117 	return bozo_http_error(httpd, 401, request, "bad auth");
118 }
119 
120 void
bozo_auth_init(bozo_httpreq_t * request)121 bozo_auth_init(bozo_httpreq_t *request)
122 {
123 	request->hr_authuser = NULL;
124 	request->hr_authpass = NULL;
125 	request->hr_authrealm = NULL;
126 }
127 
128 void
bozo_auth_cleanup(bozo_httpreq_t * request)129 bozo_auth_cleanup(bozo_httpreq_t *request)
130 {
131 
132 	if (request == NULL)
133 		return;
134 	free(request->hr_authuser);
135 	free(request->hr_authpass);
136 	free(request->hr_authrealm);
137 }
138 
139 int
bozo_auth_check_headers(bozo_httpreq_t * request,char * val,char * str,ssize_t len)140 bozo_auth_check_headers(bozo_httpreq_t *request, char *val, char *str,
141 			ssize_t len)
142 {
143 	bozohttpd_t *httpd = request->hr_httpd;
144 
145 	if (strcasecmp(val, "authorization") == 0 &&
146 	    strncasecmp(str, "Basic ", 6) == 0) {
147 		char	authbuf[BUFSIZ];
148 		char	*pass = NULL;
149 		ssize_t	alen;
150 
151 		/* free prior entries. */
152 		free(request->hr_authuser);
153 		free(request->hr_authpass);
154 
155 		alen = base64_decode((unsigned char *)str + 6,
156 					(size_t)(len - 6),
157 					(unsigned char *)authbuf,
158 					sizeof(authbuf) - 1);
159 		if (alen != -1)
160 			authbuf[alen] = '\0';
161 		if (alen == -1 ||
162 		    (pass = strchr(authbuf, ':')) == NULL)
163 			return bozo_http_error(httpd, 400, request,
164 			    "bad authorization field");
165 		*pass++ = '\0';
166 		request->hr_authuser = bozostrdup(httpd, request, authbuf);
167 		request->hr_authpass = bozostrdup(httpd, request, pass);
168 		debug((httpd, DEBUG_FAT,
169 		    "decoded authorization `%s' as `%s':`%s'",
170 		    str, request->hr_authuser, request->hr_authpass));
171 			/* don't store in request->headers */
172 		return 1;
173 	}
174 	return 0;
175 }
176 
177 void
bozo_auth_check_401(bozo_httpreq_t * request,int code)178 bozo_auth_check_401(bozo_httpreq_t *request, int code)
179 {
180 	bozohttpd_t *httpd = request->hr_httpd;
181 
182 	if (code == 401)
183 		bozo_printf(httpd,
184 			"WWW-Authenticate: Basic realm=\"%s\"\r\n",
185 			request->hr_authrealm ?
186 			request->hr_authrealm : "default realm");
187 }
188 
189 #ifndef NO_CGIBIN_SUPPORT
190 void
bozo_auth_cgi_setenv(bozo_httpreq_t * request,char *** curenvpp)191 bozo_auth_cgi_setenv(bozo_httpreq_t *request,
192 			char ***curenvpp)
193 {
194 	bozohttpd_t *httpd = request->hr_httpd;
195 
196 	if (request->hr_authuser && *request->hr_authuser) {
197 		bozo_setenv(httpd, "AUTH_TYPE", "Basic", (*curenvpp)++);
198 		bozo_setenv(httpd, "REMOTE_USER", request->hr_authuser,
199 				(*curenvpp)++);
200 	}
201 }
202 
203 int
bozo_auth_cgi_count(bozo_httpreq_t * request)204 bozo_auth_cgi_count(bozo_httpreq_t *request)
205 {
206 	return (request->hr_authuser && *request->hr_authuser) ? 2 : 0;
207 }
208 #endif /* NO_CGIBIN_SUPPORT */
209 
210 /*
211  * Decode len bytes starting at in using base64 encoding into out.
212  * Result is *not* NUL terminated.
213  * Written by Luke Mewburn <lukem@NetBSD.org>
214  */
215 const unsigned char decodetable[] = {
216 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
217 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
218 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
219 	 52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255,   0, 255, 255,
220 	255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
221 	 15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,
222 	255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
223 	 41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255,
224 };
225 
226 static ssize_t
base64_decode(const unsigned char * in,size_t ilen,unsigned char * out,size_t olen)227 base64_decode(const unsigned char *in, size_t ilen, unsigned char *out,
228 	      size_t olen)
229 {
230 	unsigned char *cp;
231 	size_t	 i;
232 
233 	if (ilen == 0) {
234 		if (olen)
235 			*out = '\0';
236 		return 0;
237 	}
238 
239 	cp = out;
240 	for (i = 0; i < ilen; i += 4) {
241 		if (cp + 3 > out + olen)
242 			return (-1);
243 #define IN_CHECK(x) \
244 		if ((x) > sizeof(decodetable) || decodetable[(x)] == 255) \
245 			    return(-1)
246 
247 		IN_CHECK(in[i + 0]);
248 		/*LINTED*/
249 		*(cp++) = decodetable[in[i + 0]] << 2
250 			| decodetable[in[i + 1]] >> 4;
251 		IN_CHECK(in[i + 1]);
252 		/*LINTED*/
253 		*(cp++) = decodetable[in[i + 1]] << 4
254 			| decodetable[in[i + 2]] >> 2;
255 		IN_CHECK(in[i + 2]);
256 		*(cp++) = decodetable[in[i + 2]] << 6
257 			| decodetable[in[i + 3]];
258 #undef IN_CHECK
259 	}
260 	while (i > 0 && in[i - 1] == '=')
261 		cp--,i--;
262 	return (cp - out);
263 }
264 #endif /* DO_HTPASSWD */
265