xref: /netbsd-src/libexec/httpd/auth-bozo.c (revision 1da23bf4be3aad2f5707c4132145a68f9cfc876b)
1 /*	$NetBSD: auth-bozo.c,v 1.28 2023/09/19 07:51:43 shm Exp $	*/
2 
3 /*	$eterna: auth-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $	*/
4 
5 /*
6  * Copyright (c) 1997-2021 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[BOZO_MINBUFSIZE], *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 
71 	/* we might be called from cgi code again with the hr_authrealm
72 	 * already set */
73 	if (request->hr_authrealm)
74 		free(request->hr_authrealm);
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 
114 #ifndef NO_BLOCKLIST_SUPPORT
115 			pfilter_notify(BLOCKLIST_AUTH_OK, 200);
116 #endif /* !NO_BLOCKLIST_SUPPORT */
117 
118 			return 0;
119 		}
120 	}
121 	fclose(fp);
122 	return bozo_http_error(httpd, 401, request, "bad auth");
123 }
124 
125 void
bozo_auth_init(bozo_httpreq_t * request)126 bozo_auth_init(bozo_httpreq_t *request)
127 {
128 	request->hr_authuser = NULL;
129 	request->hr_authpass = NULL;
130 	request->hr_authrealm = NULL;
131 }
132 
133 void
bozo_auth_cleanup(bozo_httpreq_t * request)134 bozo_auth_cleanup(bozo_httpreq_t *request)
135 {
136 
137 	if (request == NULL)
138 		return;
139 	free(request->hr_authuser);
140 	free(request->hr_authpass);
141 	free(request->hr_authrealm);
142 }
143 
144 int
bozo_auth_check_headers(bozo_httpreq_t * request,char * val,char * str,ssize_t len)145 bozo_auth_check_headers(bozo_httpreq_t *request, char *val, char *str,
146 			ssize_t len)
147 {
148 	bozohttpd_t *httpd = request->hr_httpd;
149 
150 	if (strcasecmp(val, "authorization") == 0 &&
151 	    strncasecmp(str, "Basic ", 6) == 0) {
152 		char	authbuf[BOZO_MINBUFSIZE];
153 		char	*pass = NULL;
154 		ssize_t	alen;
155 
156 		/* free prior entries. */
157 		free(request->hr_authuser);
158 		free(request->hr_authpass);
159 
160 		alen = base64_decode((unsigned char *)str + 6,
161 					(size_t)(len - 6),
162 					(unsigned char *)authbuf,
163 					sizeof(authbuf) - 1);
164 		if (alen != -1)
165 			authbuf[alen] = '\0';
166 		if (alen == -1 ||
167 		    (pass = strchr(authbuf, ':')) == NULL)
168 			return bozo_http_error(httpd, 400, request,
169 			    "bad authorization field");
170 		*pass++ = '\0';
171 		request->hr_authuser = bozostrdup(httpd, request, authbuf);
172 		request->hr_authpass = bozostrdup(httpd, request, pass);
173 		debug((httpd, DEBUG_FAT,
174 		    "decoded authorization `%s' as `%s':`%s'",
175 		    str, request->hr_authuser, request->hr_authpass));
176 			/* don't store in request->headers */
177 		return 1;
178 	}
179 	return 0;
180 }
181 
182 void
bozo_auth_check_401(bozo_httpreq_t * request,int code)183 bozo_auth_check_401(bozo_httpreq_t *request, int code)
184 {
185 	bozohttpd_t *httpd = request->hr_httpd;
186 
187 	if (code == 401)
188 		bozo_printf(httpd,
189 			"WWW-Authenticate: Basic realm=\"%s\"\r\n",
190 			request->hr_authrealm ?
191 			request->hr_authrealm : "default realm");
192 }
193 
194 #ifndef NO_CGIBIN_SUPPORT
195 void
bozo_auth_cgi_setenv(bozo_httpreq_t * request,char *** curenvpp)196 bozo_auth_cgi_setenv(bozo_httpreq_t *request,
197 			char ***curenvpp)
198 {
199 	bozohttpd_t *httpd = request->hr_httpd;
200 
201 	if (request->hr_authuser && *request->hr_authuser) {
202 		bozo_setenv(httpd, "AUTH_TYPE", "Basic", (*curenvpp)++);
203 		bozo_setenv(httpd, "REMOTE_USER", request->hr_authuser,
204 				(*curenvpp)++);
205 	}
206 }
207 
208 int
bozo_auth_cgi_count(bozo_httpreq_t * request)209 bozo_auth_cgi_count(bozo_httpreq_t *request)
210 {
211 	return (request->hr_authuser && *request->hr_authuser) ? 2 : 0;
212 }
213 #endif /* NO_CGIBIN_SUPPORT */
214 
215 /*
216  * Decode len bytes starting at in using base64 encoding into out.
217  * Result is *not* NUL terminated.
218  * Written by Luke Mewburn <lukem@NetBSD.org>
219  */
220 const unsigned char decodetable[] = {
221 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
222 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
223 	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
224 	 52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255,   0, 255, 255,
225 	255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
226 	 15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,
227 	255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
228 	 41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255,
229 };
230 
231 static ssize_t
base64_decode(const unsigned char * in,size_t ilen,unsigned char * out,size_t olen)232 base64_decode(const unsigned char *in, size_t ilen, unsigned char *out,
233 	      size_t olen)
234 {
235 	unsigned char *cp;
236 	size_t	 i;
237 
238 	if (ilen == 0) {
239 		if (olen)
240 			*out = '\0';
241 		return 0;
242 	}
243 
244 	cp = out;
245 	for (i = 0; i < ilen; i += 4) {
246 		if (cp + 3 > out + olen)
247 			return (-1);
248 #define IN_CHECK(x) \
249 		if ((x) > sizeof(decodetable) || decodetable[(x)] == 255) \
250 			    return(-1)
251 
252 		IN_CHECK(in[i + 0]);
253 		/*LINTED*/
254 		*(cp++) = decodetable[in[i + 0]] << 2
255 			| decodetable[in[i + 1]] >> 4;
256 		IN_CHECK(in[i + 1]);
257 		/*LINTED*/
258 		*(cp++) = decodetable[in[i + 1]] << 4
259 			| decodetable[in[i + 2]] >> 2;
260 		IN_CHECK(in[i + 2]);
261 		*(cp++) = decodetable[in[i + 2]] << 6
262 			| decodetable[in[i + 3]];
263 #undef IN_CHECK
264 	}
265 	while (i > 0 && in[i - 1] == '=')
266 		cp--,i--;
267 	return (cp - out);
268 }
269 #endif /* DO_HTPASSWD */
270