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