1 1.28 shm /* $NetBSD: auth-bozo.c,v 1.28 2023/09/19 07:51:43 shm Exp $ */ 2 1.3 tls 3 1.10 mrg /* $eterna: auth-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $ */ 4 1.1 tls 5 1.1 tls /* 6 1.27 mrg * Copyright (c) 1997-2021 Matthew R. Green 7 1.1 tls * All rights reserved. 8 1.1 tls * 9 1.1 tls * Redistribution and use in source and binary forms, with or without 10 1.1 tls * modification, are permitted provided that the following conditions 11 1.1 tls * are met: 12 1.1 tls * 1. Redistributions of source code must retain the above copyright 13 1.1 tls * notice, this list of conditions and the following disclaimer. 14 1.1 tls * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 tls * notice, this list of conditions and the following disclaimer and 16 1.1 tls * dedication in the documentation and/or other materials provided 17 1.1 tls * with the distribution. 18 1.1 tls * 19 1.1 tls * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 1.1 tls * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 1.1 tls * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 1.1 tls * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 1.1 tls * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 1.1 tls * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 1.1 tls * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 1.1 tls * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 1.1 tls * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 tls * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 tls * SUCH DAMAGE. 30 1.1 tls * 31 1.1 tls */ 32 1.1 tls 33 1.1 tls /* this code implements "http basic authorisation" for bozohttpd */ 34 1.1 tls 35 1.1 tls #ifdef DO_HTPASSWD 36 1.1 tls 37 1.1 tls #include <sys/param.h> 38 1.1 tls 39 1.1 tls #include <string.h> 40 1.6 mrg #include <stdlib.h> 41 1.1 tls #include <unistd.h> 42 1.1 tls 43 1.1 tls #include "bozohttpd.h" 44 1.1 tls 45 1.1 tls static ssize_t base64_decode(const unsigned char *, size_t, 46 1.1 tls unsigned char *, size_t); 47 1.1 tls 48 1.1 tls /* 49 1.1 tls * Check if HTTP authentication is required 50 1.1 tls */ 51 1.5 mrg int 52 1.9 mrg bozo_auth_check(bozo_httpreq_t *request, const char *file) 53 1.1 tls { 54 1.9 mrg bozohttpd_t *httpd = request->hr_httpd; 55 1.1 tls struct stat sb; 56 1.1 tls char dir[MAXPATHLEN], authfile[MAXPATHLEN], *basename; 57 1.27 mrg char user[BOZO_MINBUFSIZE], *pass; 58 1.1 tls FILE *fp; 59 1.1 tls int len; 60 1.1 tls 61 1.1 tls /* get dir=dirname(file) */ 62 1.1 tls snprintf(dir, sizeof(dir), "%s", file); 63 1.1 tls if ((basename = strrchr(dir, '/')) == NULL) 64 1.1 tls strcpy(dir, "."); 65 1.1 tls else { 66 1.1 tls *basename++ = '\0'; 67 1.23 mrg if (bozo_check_special_files(request, basename, true)) 68 1.5 mrg return 1; 69 1.1 tls } 70 1.28 shm 71 1.28 shm /* we might be called from cgi code again with the hr_authrealm 72 1.28 shm * already set */ 73 1.28 shm if (request->hr_authrealm) 74 1.28 shm free(request->hr_authrealm); 75 1.18 mrg request->hr_authrealm = bozostrdup(httpd, request, dir); 76 1.1 tls 77 1.18 mrg if ((size_t)snprintf(authfile, sizeof(authfile), "%s/%s", dir, 78 1.18 mrg AUTH_FILE) >= sizeof(authfile)) { 79 1.13 mrg return bozo_http_error(httpd, 404, request, 80 1.13 mrg "authfile path too long"); 81 1.13 mrg } 82 1.1 tls if (stat(authfile, &sb) < 0) { 83 1.8 mrg debug((httpd, DEBUG_NORMAL, 84 1.8 mrg "bozo_auth_check realm `%s' dir `%s' authfile `%s' missing", 85 1.1 tls dir, file, authfile)); 86 1.6 mrg return 0; 87 1.1 tls } 88 1.1 tls if ((fp = fopen(authfile, "r")) == NULL) 89 1.8 mrg return bozo_http_error(httpd, 403, request, 90 1.8 mrg "no permission to open authfile"); 91 1.8 mrg debug((httpd, DEBUG_NORMAL, 92 1.8 mrg "bozo_auth_check realm `%s' dir `%s' authfile `%s' open", 93 1.1 tls dir, file, authfile)); 94 1.1 tls if (request->hr_authuser && request->hr_authpass) { 95 1.1 tls while (fgets(user, sizeof(user), fp) != NULL) { 96 1.1 tls len = strlen(user); 97 1.1 tls if (len > 0 && user[len-1] == '\n') 98 1.1 tls user[--len] = '\0'; 99 1.1 tls if ((pass = strchr(user, ':')) == NULL) 100 1.1 tls continue; 101 1.1 tls *pass++ = '\0'; 102 1.8 mrg debug((httpd, DEBUG_NORMAL, 103 1.8 mrg "bozo_auth_check authfile `%s':`%s' " 104 1.8 mrg "client `%s':`%s'", 105 1.1 tls user, pass, request->hr_authuser, 106 1.1 tls request->hr_authpass)); 107 1.1 tls if (strcmp(request->hr_authuser, user) != 0) 108 1.1 tls continue; 109 1.8 mrg if (strcmp(crypt(request->hr_authpass, pass), 110 1.8 mrg pass) != 0) 111 1.1 tls break; 112 1.1 tls fclose(fp); 113 1.25 jruoho 114 1.25 jruoho #ifndef NO_BLOCKLIST_SUPPORT 115 1.25 jruoho pfilter_notify(BLOCKLIST_AUTH_OK, 200); 116 1.25 jruoho #endif /* !NO_BLOCKLIST_SUPPORT */ 117 1.25 jruoho 118 1.6 mrg return 0; 119 1.1 tls } 120 1.1 tls } 121 1.1 tls fclose(fp); 122 1.8 mrg return bozo_http_error(httpd, 401, request, "bad auth"); 123 1.5 mrg } 124 1.5 mrg 125 1.5 mrg void 126 1.14 shm bozo_auth_init(bozo_httpreq_t *request) 127 1.14 shm { 128 1.14 shm request->hr_authuser = NULL; 129 1.14 shm request->hr_authpass = NULL; 130 1.22 mrg request->hr_authrealm = NULL; 131 1.14 shm } 132 1.14 shm 133 1.14 shm void 134 1.8 mrg bozo_auth_cleanup(bozo_httpreq_t *request) 135 1.5 mrg { 136 1.5 mrg 137 1.5 mrg if (request == NULL) 138 1.5 mrg return; 139 1.11 mbalmer free(request->hr_authuser); 140 1.11 mbalmer free(request->hr_authpass); 141 1.11 mbalmer free(request->hr_authrealm); 142 1.1 tls } 143 1.1 tls 144 1.1 tls int 145 1.18 mrg bozo_auth_check_headers(bozo_httpreq_t *request, char *val, char *str, 146 1.18 mrg ssize_t len) 147 1.1 tls { 148 1.9 mrg bozohttpd_t *httpd = request->hr_httpd; 149 1.9 mrg 150 1.1 tls if (strcasecmp(val, "authorization") == 0 && 151 1.1 tls strncasecmp(str, "Basic ", 6) == 0) { 152 1.27 mrg char authbuf[BOZO_MINBUFSIZE]; 153 1.2 reed char *pass = NULL; 154 1.1 tls ssize_t alen; 155 1.1 tls 156 1.19 mrg /* free prior entries. */ 157 1.19 mrg free(request->hr_authuser); 158 1.19 mrg free(request->hr_authpass); 159 1.19 mrg 160 1.8 mrg alen = base64_decode((unsigned char *)str + 6, 161 1.8 mrg (size_t)(len - 6), 162 1.8 mrg (unsigned char *)authbuf, 163 1.8 mrg sizeof(authbuf) - 1); 164 1.1 tls if (alen != -1) 165 1.1 tls authbuf[alen] = '\0'; 166 1.1 tls if (alen == -1 || 167 1.1 tls (pass = strchr(authbuf, ':')) == NULL) 168 1.8 mrg return bozo_http_error(httpd, 400, request, 169 1.1 tls "bad authorization field"); 170 1.1 tls *pass++ = '\0'; 171 1.18 mrg request->hr_authuser = bozostrdup(httpd, request, authbuf); 172 1.18 mrg request->hr_authpass = bozostrdup(httpd, request, pass); 173 1.8 mrg debug((httpd, DEBUG_FAT, 174 1.1 tls "decoded authorization `%s' as `%s':`%s'", 175 1.1 tls str, request->hr_authuser, request->hr_authpass)); 176 1.1 tls /* don't store in request->headers */ 177 1.1 tls return 1; 178 1.1 tls } 179 1.1 tls return 0; 180 1.1 tls } 181 1.1 tls 182 1.1 tls void 183 1.9 mrg bozo_auth_check_401(bozo_httpreq_t *request, int code) 184 1.1 tls { 185 1.9 mrg bozohttpd_t *httpd = request->hr_httpd; 186 1.9 mrg 187 1.1 tls if (code == 401) 188 1.8 mrg bozo_printf(httpd, 189 1.8 mrg "WWW-Authenticate: Basic realm=\"%s\"\r\n", 190 1.18 mrg request->hr_authrealm ? 191 1.18 mrg request->hr_authrealm : "default realm"); 192 1.1 tls } 193 1.1 tls 194 1.1 tls #ifndef NO_CGIBIN_SUPPORT 195 1.1 tls void 196 1.9 mrg bozo_auth_cgi_setenv(bozo_httpreq_t *request, 197 1.8 mrg char ***curenvpp) 198 1.1 tls { 199 1.9 mrg bozohttpd_t *httpd = request->hr_httpd; 200 1.9 mrg 201 1.1 tls if (request->hr_authuser && *request->hr_authuser) { 202 1.8 mrg bozo_setenv(httpd, "AUTH_TYPE", "Basic", (*curenvpp)++); 203 1.8 mrg bozo_setenv(httpd, "REMOTE_USER", request->hr_authuser, 204 1.8 mrg (*curenvpp)++); 205 1.1 tls } 206 1.1 tls } 207 1.1 tls 208 1.1 tls int 209 1.8 mrg bozo_auth_cgi_count(bozo_httpreq_t *request) 210 1.1 tls { 211 1.1 tls return (request->hr_authuser && *request->hr_authuser) ? 2 : 0; 212 1.1 tls } 213 1.1 tls #endif /* NO_CGIBIN_SUPPORT */ 214 1.1 tls 215 1.1 tls /* 216 1.1 tls * Decode len bytes starting at in using base64 encoding into out. 217 1.1 tls * Result is *not* NUL terminated. 218 1.1 tls * Written by Luke Mewburn <lukem (at) NetBSD.org> 219 1.1 tls */ 220 1.1 tls const unsigned char decodetable[] = { 221 1.1 tls 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 222 1.1 tls 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 223 1.1 tls 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 224 1.1 tls 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, 255, 255, 225 1.1 tls 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 226 1.1 tls 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 227 1.1 tls 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 228 1.1 tls 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, 229 1.1 tls }; 230 1.1 tls 231 1.1 tls static ssize_t 232 1.1 tls base64_decode(const unsigned char *in, size_t ilen, unsigned char *out, 233 1.1 tls size_t olen) 234 1.1 tls { 235 1.1 tls unsigned char *cp; 236 1.1 tls size_t i; 237 1.1 tls 238 1.15 mrg if (ilen == 0) { 239 1.15 mrg if (olen) 240 1.15 mrg *out = '\0'; 241 1.15 mrg return 0; 242 1.15 mrg } 243 1.15 mrg 244 1.1 tls cp = out; 245 1.1 tls for (i = 0; i < ilen; i += 4) { 246 1.1 tls if (cp + 3 > out + olen) 247 1.1 tls return (-1); 248 1.1 tls #define IN_CHECK(x) \ 249 1.1 tls if ((x) > sizeof(decodetable) || decodetable[(x)] == 255) \ 250 1.1 tls return(-1) 251 1.1 tls 252 1.1 tls IN_CHECK(in[i + 0]); 253 1.8 mrg /*LINTED*/ 254 1.1 tls *(cp++) = decodetable[in[i + 0]] << 2 255 1.1 tls | decodetable[in[i + 1]] >> 4; 256 1.1 tls IN_CHECK(in[i + 1]); 257 1.8 mrg /*LINTED*/ 258 1.1 tls *(cp++) = decodetable[in[i + 1]] << 4 259 1.1 tls | decodetable[in[i + 2]] >> 2; 260 1.1 tls IN_CHECK(in[i + 2]); 261 1.1 tls *(cp++) = decodetable[in[i + 2]] << 6 262 1.1 tls | decodetable[in[i + 3]]; 263 1.1 tls #undef IN_CHECK 264 1.1 tls } 265 1.16 mrg while (i > 0 && in[i - 1] == '=') 266 1.1 tls cp--,i--; 267 1.1 tls return (cp - out); 268 1.1 tls } 269 1.1 tls #endif /* DO_HTPASSWD */ 270