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