1 /* $NetBSD: auth-bozo.c,v 1.12 2014/01/02 08:21:38 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-2014 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 #ifndef AUTH_FILE 46 #define AUTH_FILE ".htpasswd" 47 #endif 48 49 static ssize_t base64_decode(const unsigned char *, size_t, 50 unsigned char *, size_t); 51 52 /* 53 * Check if HTTP authentication is required 54 */ 55 int 56 bozo_auth_check(bozo_httpreq_t *request, const char *file) 57 { 58 bozohttpd_t *httpd = request->hr_httpd; 59 struct stat sb; 60 char dir[MAXPATHLEN], authfile[MAXPATHLEN], *basename; 61 char user[BUFSIZ], *pass; 62 FILE *fp; 63 int len; 64 65 /* get dir=dirname(file) */ 66 snprintf(dir, sizeof(dir), "%s", file); 67 if ((basename = strrchr(dir, '/')) == NULL) 68 strcpy(dir, "."); 69 else { 70 *basename++ = '\0'; 71 /* ensure basename(file) != AUTH_FILE */ 72 if (bozo_check_special_files(request, basename)) 73 return 1; 74 } 75 request->hr_authrealm = bozostrdup(httpd, dir); 76 77 snprintf(authfile, sizeof(authfile), "%s/%s", dir, AUTH_FILE); 78 if (stat(authfile, &sb) < 0) { 79 debug((httpd, DEBUG_NORMAL, 80 "bozo_auth_check realm `%s' dir `%s' authfile `%s' missing", 81 dir, file, authfile)); 82 return 0; 83 } 84 if ((fp = fopen(authfile, "r")) == NULL) 85 return bozo_http_error(httpd, 403, request, 86 "no permission to open authfile"); 87 debug((httpd, DEBUG_NORMAL, 88 "bozo_auth_check realm `%s' dir `%s' authfile `%s' open", 89 dir, file, authfile)); 90 if (request->hr_authuser && request->hr_authpass) { 91 while (fgets(user, sizeof(user), fp) != NULL) { 92 len = strlen(user); 93 if (len > 0 && user[len-1] == '\n') 94 user[--len] = '\0'; 95 if ((pass = strchr(user, ':')) == NULL) 96 continue; 97 *pass++ = '\0'; 98 debug((httpd, DEBUG_NORMAL, 99 "bozo_auth_check authfile `%s':`%s' " 100 "client `%s':`%s'", 101 user, pass, request->hr_authuser, 102 request->hr_authpass)); 103 if (strcmp(request->hr_authuser, user) != 0) 104 continue; 105 if (strcmp(crypt(request->hr_authpass, pass), 106 pass) != 0) 107 break; 108 fclose(fp); 109 return 0; 110 } 111 } 112 fclose(fp); 113 return bozo_http_error(httpd, 401, request, "bad auth"); 114 } 115 116 void 117 bozo_auth_cleanup(bozo_httpreq_t *request) 118 { 119 120 if (request == NULL) 121 return; 122 free(request->hr_authuser); 123 free(request->hr_authpass); 124 free(request->hr_authrealm); 125 } 126 127 int 128 bozo_auth_check_headers(bozo_httpreq_t *request, char *val, char *str, ssize_t len) 129 { 130 bozohttpd_t *httpd = request->hr_httpd; 131 132 if (strcasecmp(val, "authorization") == 0 && 133 strncasecmp(str, "Basic ", 6) == 0) { 134 char authbuf[BUFSIZ]; 135 char *pass = NULL; 136 ssize_t alen; 137 138 alen = base64_decode((unsigned char *)str + 6, 139 (size_t)(len - 6), 140 (unsigned char *)authbuf, 141 sizeof(authbuf) - 1); 142 if (alen != -1) 143 authbuf[alen] = '\0'; 144 if (alen == -1 || 145 (pass = strchr(authbuf, ':')) == NULL) 146 return bozo_http_error(httpd, 400, request, 147 "bad authorization field"); 148 *pass++ = '\0'; 149 request->hr_authuser = bozostrdup(httpd, authbuf); 150 request->hr_authpass = bozostrdup(httpd, pass); 151 debug((httpd, DEBUG_FAT, 152 "decoded authorization `%s' as `%s':`%s'", 153 str, request->hr_authuser, request->hr_authpass)); 154 /* don't store in request->headers */ 155 return 1; 156 } 157 return 0; 158 } 159 160 int 161 bozo_auth_check_special_files(bozo_httpreq_t *request, 162 const char *name) 163 { 164 bozohttpd_t *httpd = request->hr_httpd; 165 166 if (strcmp(name, AUTH_FILE) == 0) 167 return bozo_http_error(httpd, 403, request, 168 "no permission to open authfile"); 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 && 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 cp = out; 229 for (i = 0; i < ilen; i += 4) { 230 if (cp + 3 > out + olen) 231 return (-1); 232 #define IN_CHECK(x) \ 233 if ((x) > sizeof(decodetable) || decodetable[(x)] == 255) \ 234 return(-1) 235 236 IN_CHECK(in[i + 0]); 237 /*LINTED*/ 238 *(cp++) = decodetable[in[i + 0]] << 2 239 | decodetable[in[i + 1]] >> 4; 240 IN_CHECK(in[i + 1]); 241 /*LINTED*/ 242 *(cp++) = decodetable[in[i + 1]] << 4 243 | decodetable[in[i + 2]] >> 2; 244 IN_CHECK(in[i + 2]); 245 *(cp++) = decodetable[in[i + 2]] << 6 246 | decodetable[in[i + 3]]; 247 #undef IN_CHECK 248 } 249 while (in[i - 1] == '=') 250 cp--,i--; 251 return (cp - out); 252 } 253 #endif /* DO_HTPASSWD */ 254