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