xref: /netbsd-src/libexec/httpd/auth-bozo.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: auth-bozo.c,v 1.12 2014/01/02 08:21:38 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, dir);
76 
77 	snprintf(authfile, sizeof(authfile), "%s/%s", dir, AUTH_FILE);
78 	if (stat(authfile, &sb) < 0) {
79 		debug((httpd, DEBUG_NORMAL,
80 		    "bozo_auth_check realm `%s' dir `%s' authfile `%s' missing",
81 		    dir, file, authfile));
82 		return 0;
83 	}
84 	if ((fp = fopen(authfile, "r")) == NULL)
85 		return bozo_http_error(httpd, 403, request,
86 			"no permission to open authfile");
87 	debug((httpd, DEBUG_NORMAL,
88 	    "bozo_auth_check realm `%s' dir `%s' authfile `%s' open",
89 	    dir, file, authfile));
90 	if (request->hr_authuser && request->hr_authpass) {
91 		while (fgets(user, sizeof(user), fp) != NULL) {
92 			len = strlen(user);
93 			if (len > 0 && user[len-1] == '\n')
94 				user[--len] = '\0';
95 			if ((pass = strchr(user, ':')) == NULL)
96 				continue;
97 			*pass++ = '\0';
98 			debug((httpd, DEBUG_NORMAL,
99 			    "bozo_auth_check authfile `%s':`%s' "
100 			    	"client `%s':`%s'",
101 			    user, pass, request->hr_authuser,
102 			    request->hr_authpass));
103 			if (strcmp(request->hr_authuser, user) != 0)
104 				continue;
105 			if (strcmp(crypt(request->hr_authpass, pass),
106 					pass) != 0)
107 				break;
108 			fclose(fp);
109 			return 0;
110 		}
111 	}
112 	fclose(fp);
113 	return bozo_http_error(httpd, 401, request, "bad auth");
114 }
115 
116 void
117 bozo_auth_cleanup(bozo_httpreq_t *request)
118 {
119 
120 	if (request == NULL)
121 		return;
122 	free(request->hr_authuser);
123 	free(request->hr_authpass);
124 	free(request->hr_authrealm);
125 }
126 
127 int
128 bozo_auth_check_headers(bozo_httpreq_t *request, char *val, char *str, ssize_t len)
129 {
130 	bozohttpd_t *httpd = request->hr_httpd;
131 
132 	if (strcasecmp(val, "authorization") == 0 &&
133 	    strncasecmp(str, "Basic ", 6) == 0) {
134 		char	authbuf[BUFSIZ];
135 		char	*pass = NULL;
136 		ssize_t	alen;
137 
138 		alen = base64_decode((unsigned char *)str + 6,
139 					(size_t)(len - 6),
140 					(unsigned char *)authbuf,
141 					sizeof(authbuf) - 1);
142 		if (alen != -1)
143 			authbuf[alen] = '\0';
144 		if (alen == -1 ||
145 		    (pass = strchr(authbuf, ':')) == NULL)
146 			return bozo_http_error(httpd, 400, request,
147 			    "bad authorization field");
148 		*pass++ = '\0';
149 		request->hr_authuser = bozostrdup(httpd, authbuf);
150 		request->hr_authpass = bozostrdup(httpd, pass);
151 		debug((httpd, DEBUG_FAT,
152 		    "decoded authorization `%s' as `%s':`%s'",
153 		    str, request->hr_authuser, request->hr_authpass));
154 			/* don't store in request->headers */
155 		return 1;
156 	}
157 	return 0;
158 }
159 
160 int
161 bozo_auth_check_special_files(bozo_httpreq_t *request,
162 				const char *name)
163 {
164 	bozohttpd_t *httpd = request->hr_httpd;
165 
166 	if (strcmp(name, AUTH_FILE) == 0)
167 		return bozo_http_error(httpd, 403, request,
168 				"no permission to open authfile");
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 && 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 	cp = out;
229 	for (i = 0; i < ilen; i += 4) {
230 		if (cp + 3 > out + olen)
231 			return (-1);
232 #define IN_CHECK(x) \
233 		if ((x) > sizeof(decodetable) || decodetable[(x)] == 255) \
234 			    return(-1)
235 
236 		IN_CHECK(in[i + 0]);
237 		/*LINTED*/
238 		*(cp++) = decodetable[in[i + 0]] << 2
239 			| decodetable[in[i + 1]] >> 4;
240 		IN_CHECK(in[i + 1]);
241 		/*LINTED*/
242 		*(cp++) = decodetable[in[i + 1]] << 4
243 			| decodetable[in[i + 2]] >> 2;
244 		IN_CHECK(in[i + 2]);
245 		*(cp++) = decodetable[in[i + 2]] << 6
246 			| decodetable[in[i + 3]];
247 #undef IN_CHECK
248 	}
249 	while (in[i - 1] == '=')
250 		cp--,i--;
251 	return (cp - out);
252 }
253 #endif /* DO_HTPASSWD */
254