auth-bozo.c revision 1.1.1.7 1 /* $eterna: auth-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $ */
2
3 /*
4 * Copyright (c) 1997-2011 Matthew R. Green
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer and
14 * dedication in the documentation and/or other materials provided
15 * with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 */
30
31 /* this code implements "http basic authorisation" for bozohttpd */
32
33 #ifdef DO_HTPASSWD
34
35 #include <sys/param.h>
36
37 #include <string.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40
41 #include "bozohttpd.h"
42
43 #ifndef AUTH_FILE
44 #define AUTH_FILE ".htpasswd"
45 #endif
46
47 static ssize_t base64_decode(const unsigned char *, size_t,
48 unsigned char *, size_t);
49
50 /*
51 * Check if HTTP authentication is required
52 */
53 int
54 bozo_auth_check(bozo_httpreq_t *request, const char *file)
55 {
56 bozohttpd_t *httpd = request->hr_httpd;
57 struct stat sb;
58 char dir[MAXPATHLEN], authfile[MAXPATHLEN], *basename;
59 char user[BUFSIZ], *pass;
60 FILE *fp;
61 int len;
62
63 /* get dir=dirname(file) */
64 snprintf(dir, sizeof(dir), "%s", file);
65 if ((basename = strrchr(dir, '/')) == NULL)
66 strcpy(dir, ".");
67 else {
68 *basename++ = '\0';
69 /* ensure basename(file) != AUTH_FILE */
70 if (bozo_check_special_files(request, basename))
71 return 1;
72 }
73 request->hr_authrealm = bozostrdup(httpd, dir);
74
75 snprintf(authfile, sizeof(authfile), "%s/%s", dir, AUTH_FILE);
76 if (stat(authfile, &sb) < 0) {
77 debug((httpd, DEBUG_NORMAL,
78 "bozo_auth_check realm `%s' dir `%s' authfile `%s' missing",
79 dir, file, authfile));
80 return 0;
81 }
82 if ((fp = fopen(authfile, "r")) == NULL)
83 return bozo_http_error(httpd, 403, request,
84 "no permission to open authfile");
85 debug((httpd, DEBUG_NORMAL,
86 "bozo_auth_check realm `%s' dir `%s' authfile `%s' open",
87 dir, file, authfile));
88 if (request->hr_authuser && request->hr_authpass) {
89 while (fgets(user, sizeof(user), fp) != NULL) {
90 len = strlen(user);
91 if (len > 0 && user[len-1] == '\n')
92 user[--len] = '\0';
93 if ((pass = strchr(user, ':')) == NULL)
94 continue;
95 *pass++ = '\0';
96 debug((httpd, DEBUG_NORMAL,
97 "bozo_auth_check authfile `%s':`%s' "
98 "client `%s':`%s'",
99 user, pass, request->hr_authuser,
100 request->hr_authpass));
101 if (strcmp(request->hr_authuser, user) != 0)
102 continue;
103 if (strcmp(crypt(request->hr_authpass, pass),
104 pass) != 0)
105 break;
106 fclose(fp);
107 return 0;
108 }
109 }
110 fclose(fp);
111 return bozo_http_error(httpd, 401, request, "bad auth");
112 }
113
114 void
115 bozo_auth_cleanup(bozo_httpreq_t *request)
116 {
117
118 if (request == NULL)
119 return;
120 if (request->hr_authuser)
121 free(request->hr_authuser);
122 if (request->hr_authpass)
123 free(request->hr_authpass);
124 if (request->hr_authrealm)
125 free(request->hr_authrealm);
126 }
127
128 int
129 bozo_auth_check_headers(bozo_httpreq_t *request, char *val, char *str, ssize_t len)
130 {
131 bozohttpd_t *httpd = request->hr_httpd;
132
133 if (strcasecmp(val, "authorization") == 0 &&
134 strncasecmp(str, "Basic ", 6) == 0) {
135 char authbuf[BUFSIZ];
136 char *pass = NULL;
137 ssize_t alen;
138
139 alen = base64_decode((unsigned char *)str + 6,
140 (size_t)(len - 6),
141 (unsigned char *)authbuf,
142 sizeof(authbuf) - 1);
143 if (alen != -1)
144 authbuf[alen] = '\0';
145 if (alen == -1 ||
146 (pass = strchr(authbuf, ':')) == NULL)
147 return bozo_http_error(httpd, 400, request,
148 "bad authorization field");
149 *pass++ = '\0';
150 request->hr_authuser = bozostrdup(httpd, authbuf);
151 request->hr_authpass = bozostrdup(httpd, pass);
152 debug((httpd, DEBUG_FAT,
153 "decoded authorization `%s' as `%s':`%s'",
154 str, request->hr_authuser, request->hr_authpass));
155 /* don't store in request->headers */
156 return 1;
157 }
158 return 0;
159 }
160
161 int
162 bozo_auth_check_special_files(bozo_httpreq_t *request,
163 const char *name)
164 {
165 bozohttpd_t *httpd = request->hr_httpd;
166
167 if (strcmp(name, AUTH_FILE) == 0)
168 return bozo_http_error(httpd, 403, request,
169 "no permission to open authfile");
170 return 0;
171 }
172
173 void
174 bozo_auth_check_401(bozo_httpreq_t *request, int code)
175 {
176 bozohttpd_t *httpd = request->hr_httpd;
177
178 if (code == 401)
179 bozo_printf(httpd,
180 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
181 (request && request->hr_authrealm) ?
182 request->hr_authrealm : "default realm");
183 }
184
185 #ifndef NO_CGIBIN_SUPPORT
186 void
187 bozo_auth_cgi_setenv(bozo_httpreq_t *request,
188 char ***curenvpp)
189 {
190 bozohttpd_t *httpd = request->hr_httpd;
191
192 if (request->hr_authuser && *request->hr_authuser) {
193 bozo_setenv(httpd, "AUTH_TYPE", "Basic", (*curenvpp)++);
194 bozo_setenv(httpd, "REMOTE_USER", request->hr_authuser,
195 (*curenvpp)++);
196 }
197 }
198
199 int
200 bozo_auth_cgi_count(bozo_httpreq_t *request)
201 {
202 return (request->hr_authuser && *request->hr_authuser) ? 2 : 0;
203 }
204 #endif /* NO_CGIBIN_SUPPORT */
205
206 /*
207 * Decode len bytes starting at in using base64 encoding into out.
208 * Result is *not* NUL terminated.
209 * Written by Luke Mewburn <lukem (at) NetBSD.org>
210 */
211 const unsigned char decodetable[] = {
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, 255, 255, 255, 255, 255,
214 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
215 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, 255, 255,
216 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
217 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
218 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
219 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255,
220 };
221
222 static ssize_t
223 base64_decode(const unsigned char *in, size_t ilen, unsigned char *out,
224 size_t olen)
225 {
226 unsigned char *cp;
227 size_t i;
228
229 cp = out;
230 for (i = 0; i < ilen; i += 4) {
231 if (cp + 3 > out + olen)
232 return (-1);
233 #define IN_CHECK(x) \
234 if ((x) > sizeof(decodetable) || decodetable[(x)] == 255) \
235 return(-1)
236
237 IN_CHECK(in[i + 0]);
238 /*LINTED*/
239 *(cp++) = decodetable[in[i + 0]] << 2
240 | decodetable[in[i + 1]] >> 4;
241 IN_CHECK(in[i + 1]);
242 /*LINTED*/
243 *(cp++) = decodetable[in[i + 1]] << 4
244 | decodetable[in[i + 2]] >> 2;
245 IN_CHECK(in[i + 2]);
246 *(cp++) = decodetable[in[i + 2]] << 6
247 | decodetable[in[i + 3]];
248 #undef IN_CHECK
249 }
250 while (in[i - 1] == '=')
251 cp--,i--;
252 return (cp - out);
253 }
254 #endif /* DO_HTPASSWD */
255