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