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