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