auth-bozo.c revision 1.18 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 (at) 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