auth-bozo.c revision 1.1.1.5 1 /* $eterna: auth-bozo.c,v 1.15 2010/05/10 02:51:28 mrg Exp $ */
2
3 /*
4 * Copyright (c) 1997-2010 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(bozohttpd_t *httpd, bozo_httpreq_t *request, const char *file)
55 {
56 struct stat sb;
57 char dir[MAXPATHLEN], authfile[MAXPATHLEN], *basename;
58 char user[BUFSIZ], *pass;
59 FILE *fp;
60 int len;
61
62 /* get dir=dirname(file) */
63 snprintf(dir, sizeof(dir), "%s", file);
64 if ((basename = strrchr(dir, '/')) == NULL)
65 strcpy(dir, ".");
66 else {
67 *basename++ = '\0';
68 /* ensure basename(file) != AUTH_FILE */
69 if (bozo_check_special_files(httpd, request, basename))
70 return 1;
71 }
72 request->hr_authrealm = bozostrdup(httpd, dir);
73
74 snprintf(authfile, sizeof(authfile), "%s/%s", dir, AUTH_FILE);
75 if (stat(authfile, &sb) < 0) {
76 debug((httpd, DEBUG_NORMAL,
77 "bozo_auth_check realm `%s' dir `%s' authfile `%s' missing",
78 dir, file, authfile));
79 return 0;
80 }
81 if ((fp = fopen(authfile, "r")) == NULL)
82 return bozo_http_error(httpd, 403, request,
83 "no permission to open authfile");
84 debug((httpd, DEBUG_NORMAL,
85 "bozo_auth_check realm `%s' dir `%s' authfile `%s' open",
86 dir, file, authfile));
87 if (request->hr_authuser && request->hr_authpass) {
88 while (fgets(user, sizeof(user), fp) != NULL) {
89 len = strlen(user);
90 if (len > 0 && user[len-1] == '\n')
91 user[--len] = '\0';
92 if ((pass = strchr(user, ':')) == NULL)
93 continue;
94 *pass++ = '\0';
95 debug((httpd, DEBUG_NORMAL,
96 "bozo_auth_check authfile `%s':`%s' "
97 "client `%s':`%s'",
98 user, pass, request->hr_authuser,
99 request->hr_authpass));
100 if (strcmp(request->hr_authuser, user) != 0)
101 continue;
102 if (strcmp(crypt(request->hr_authpass, pass),
103 pass) != 0)
104 break;
105 fclose(fp);
106 return 0;
107 }
108 }
109 fclose(fp);
110 return bozo_http_error(httpd, 401, request, "bad auth");
111 }
112
113 void
114 bozo_auth_cleanup(bozo_httpreq_t *request)
115 {
116
117 if (request == NULL)
118 return;
119 if (request->hr_authuser)
120 free(request->hr_authuser);
121 if (request->hr_authpass)
122 free(request->hr_authpass);
123 if (request->hr_authrealm)
124 free(request->hr_authrealm);
125 }
126
127 int
128 bozo_auth_check_headers(bozohttpd_t *httpd, bozo_httpreq_t *request, char *val, char *str, ssize_t len)
129 {
130 if (strcasecmp(val, "authorization") == 0 &&
131 strncasecmp(str, "Basic ", 6) == 0) {
132 char authbuf[BUFSIZ];
133 char *pass = NULL;
134 ssize_t alen;
135
136 alen = base64_decode((unsigned char *)str + 6,
137 (size_t)(len - 6),
138 (unsigned char *)authbuf,
139 sizeof(authbuf) - 1);
140 if (alen != -1)
141 authbuf[alen] = '\0';
142 if (alen == -1 ||
143 (pass = strchr(authbuf, ':')) == NULL)
144 return bozo_http_error(httpd, 400, request,
145 "bad authorization field");
146 *pass++ = '\0';
147 request->hr_authuser = bozostrdup(httpd, authbuf);
148 request->hr_authpass = bozostrdup(httpd, pass);
149 debug((httpd, DEBUG_FAT,
150 "decoded authorization `%s' as `%s':`%s'",
151 str, request->hr_authuser, request->hr_authpass));
152 /* don't store in request->headers */
153 return 1;
154 }
155 return 0;
156 }
157
158 int
159 bozo_auth_check_special_files(bozohttpd_t *httpd, bozo_httpreq_t *request,
160 const char *name)
161 {
162 if (strcmp(name, AUTH_FILE) == 0)
163 return bozo_http_error(httpd, 403, request,
164 "no permission to open authfile");
165 return 0;
166 }
167
168 void
169 bozo_auth_check_401(bozohttpd_t *httpd, bozo_httpreq_t *request, int code)
170 {
171 if (code == 401)
172 bozo_printf(httpd,
173 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
174 (request && request->hr_authrealm) ?
175 request->hr_authrealm : "default realm");
176 }
177
178 #ifndef NO_CGIBIN_SUPPORT
179 void
180 bozo_auth_cgi_setenv(bozohttpd_t *httpd, bozo_httpreq_t *request,
181 char ***curenvpp)
182 {
183 if (request->hr_authuser && *request->hr_authuser) {
184 bozo_setenv(httpd, "AUTH_TYPE", "Basic", (*curenvpp)++);
185 bozo_setenv(httpd, "REMOTE_USER", request->hr_authuser,
186 (*curenvpp)++);
187 }
188 }
189
190 int
191 bozo_auth_cgi_count(bozo_httpreq_t *request)
192 {
193 return (request->hr_authuser && *request->hr_authuser) ? 2 : 0;
194 }
195 #endif /* NO_CGIBIN_SUPPORT */
196
197 /*
198 * Decode len bytes starting at in using base64 encoding into out.
199 * Result is *not* NUL terminated.
200 * Written by Luke Mewburn <lukem (at) NetBSD.org>
201 */
202 const unsigned char decodetable[] = {
203 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
204 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
205 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
206 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, 255, 255,
207 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
208 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
209 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
210 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255,
211 };
212
213 static ssize_t
214 base64_decode(const unsigned char *in, size_t ilen, unsigned char *out,
215 size_t olen)
216 {
217 unsigned char *cp;
218 size_t i;
219
220 cp = out;
221 for (i = 0; i < ilen; i += 4) {
222 if (cp + 3 > out + olen)
223 return (-1);
224 #define IN_CHECK(x) \
225 if ((x) > sizeof(decodetable) || decodetable[(x)] == 255) \
226 return(-1)
227
228 IN_CHECK(in[i + 0]);
229 /*LINTED*/
230 *(cp++) = decodetable[in[i + 0]] << 2
231 | decodetable[in[i + 1]] >> 4;
232 IN_CHECK(in[i + 1]);
233 /*LINTED*/
234 *(cp++) = decodetable[in[i + 1]] << 4
235 | decodetable[in[i + 2]] >> 2;
236 IN_CHECK(in[i + 2]);
237 *(cp++) = decodetable[in[i + 2]] << 6
238 | decodetable[in[i + 3]];
239 #undef IN_CHECK
240 }
241 while (in[i - 1] == '=')
242 cp--,i--;
243 return (cp - out);
244 }
245 #endif /* DO_HTPASSWD */
246