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 45static ssize_t base64_decode(const unsigned char *, size_t, 46 unsigned char *, size_t); 47 48/* 49 * Check if HTTP authentication is required 50 */ 51int 52bozo_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 125void 126bozo_auth_init(bozo_httpreq_t *request) 127{ 128 request->hr_authuser = NULL; 129 request->hr_authpass = NULL; 130 request->hr_authrealm = NULL; 131} 132 133void 134bozo_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 144int 145bozo_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 182void 183bozo_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 195void 196bozo_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 208int 209bozo_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 */ 220const 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 231static ssize_t 232base64_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