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