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