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