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