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