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