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