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