diff -dru thttpd-2.23beta1/config.h thttpd-2.23beta1.fjo/config.h --- thttpd-2.23beta1/config.h Tue Mar 19 00:07:29 2002 +++ thttpd-2.23beta1.fjo/config.h Wed Sep 10 18:43:53 2003 @@ -128,6 +128,17 @@ */ #define AUTH_FILE ".htpasswd" +/* CONFIGURE: The file to use for restricting access on an ip basis. If +** this is defined then thttpd checks for this file in the local +** directory before every fetch. If the file exists then thttpd checks +** whether the client's ip address is allowed to fetch this file, otherwise +** the fetch proceeds as usual. +** +** If you undefine this then thttpd will not implement access checks +** at all and will not check for access files, which saves a bit of CPU time. +*/ +#define ACCESS_FILE ".htaccess" + /* CONFIGURE: The default character set name to use with text MIME types. ** This gets substituted into the MIME types where they have a "%s". ** diff -dru thttpd-2.23beta1/libhttpd.c thttpd-2.23beta1.fjo/libhttpd.c --- thttpd-2.23beta1/libhttpd.c Mon May 27 03:22:26 2002 +++ thttpd-2.23beta1.fjo/libhttpd.c Wed Sep 10 18:19:33 2003 @@ -128,6 +128,9 @@ static int auth_check( httpd_conn* hc, char* dirname ); static int auth_check2( httpd_conn* hc, char* dirname ); #endif /* AUTH_FILE */ +#ifdef ACCESS_FILE +static int access_check (httpd_conn* hc, char* dirname); +#endif /* ACCESS_FILE */ static void send_dirredirect( httpd_conn* hc ); static int hexit( char c ); static void strdecode( char* to, char* from ); @@ -909,6 +912,116 @@ } #endif /* ERR_DIR */ +#ifdef ACCESS_FILE +static int err_accessfile (httpd_conn* hc, char* accesspath, char* err, FILE* f) +{ + fclose(f); + + syslog(LOG_ERR, "%.80s access file %.80s: invalid line: %s", + httpd_ntoa(&hc->client_addr), accesspath, err); + + httpd_send_err(hc, 403, err403title, "", ERROR_FORM(err403form, + "The requested URL '%.80s' is protected by an access file, but the " + "access file contains garbage.\n"), hc->encodedurl); + + return -1; +} + +/* Returns -1 == unauthorized, 0 == no access file, 1 = authorized. */ +static int access_check (httpd_conn* hc, char* dirname) +{ + static char* accesspath; + static size_t maxaccesspath = 0; + struct in_addr ipv4_addr, ipv4_mask = { 0xffffffff }; + FILE* fp; + char line[500]; + struct stat sb; + char *addr, *mask; + unsigned long l; + + /* Construct access filename. */ + httpd_realloc_str(&accesspath, &maxaccesspath, strlen(dirname) + 1 + + sizeof(ACCESS_FILE)); + + my_snprintf(accesspath, maxaccesspath, "%s/%s", dirname, ACCESS_FILE); + + /* Does this directory have an access file? */ + if (stat(accesspath, &sb) < 0) + /* Nope, let the request go through. */ + return 0; + + /* Open the access file. */ + fp = fopen(accesspath, "r"); + if (!fp) { + /* The file exists but we can't open it? Disallow access. */ + syslog(LOG_ERR, "%.80s access file %.80s could not be opened - %m", + httpd_ntoa(&hc->client_addr), accesspath); + + httpd_send_err(hc, 403, err403title, "", ERROR_FORM(err403form, + "The requested URL '%.80s' is protected by an access file, but " + "the access file cannot be opened.\n"), hc->encodedurl); + + return -1; + } + + /* Read it. */ + while (fgets(line, sizeof(line), fp)) { + /* Nuke newline. */ + l = strlen(line); + if (line[l - 1] == '\n') line[l - 1] = '\0'; + + addr = strrchr(line, ' '); + if (!addr) { + addr = strrchr(line, '\t'); + if (!addr) continue; + } + + mask = strchr(++addr, '/'); + if (mask) { + *mask++ = '\0'; + if (!strchr(mask, '.')) { + l = atol(mask); + if (l > 32) return err_accessfile(hc, accesspath, line, fp); + ipv4_mask.s_addr = 0; + for (l = 32 - l; l <= 31; ++l) ipv4_mask.s_addr |= 1 << l; + } + else { + if (!inet_aton(mask, &ipv4_mask)) + return err_accessfile(hc, accesspath, line, fp); + } + } + + if (!inet_aton(addr, &ipv4_addr)) + return err_accessfile(hc, accesspath, line, fp); + + /* Does client addr match this rule? */ + if ((hc->client_addr.sa_in.sin_addr.s_addr & ipv4_mask.s_addr) == + (ipv4_addr.s_addr & ipv4_mask.s_addr)) { + /* Yes. */ + switch (line[0]) { + case 'd': + case 'D': + httpd_send_err(hc, 403, err403title, "", ERROR_FORM(err403form, + "The requested URL '%.80s' is protected by an address " + "restriction."), hc->encodedurl); + fclose(fp); + return -1; + + case 'a': + case 'A': + fclose(fp); + return 1; + + default: + return err_accessfile(hc, accesspath, line, fp); + } + } + } + + fclose(fp); + return 0; +} +#endif /* ACCESS_FILE */ #ifdef AUTH_FILE @@ -3656,6 +3769,11 @@ hc->encodedurl ); return -1; } +#ifdef ACCESS_FILE + /* Check access file for this directory. */ + if ( access_check( hc, hc->expnfilename ) == -1 ) + return -1; +#endif /* ACCESS_FILE */ #ifdef AUTH_FILE /* Check authorization for this directory. */ if ( auth_check( hc, hc->expnfilename ) == -1 ) @@ -3705,6 +3823,50 @@ return -1; } } + +#ifdef ACCESS_FILE + /* Check access for this directory. */ + httpd_realloc_str( &dirname, &maxdirname, expnlen ); + (void) strcpy( dirname, hc->expnfilename ); + cp = strrchr( dirname, '/' ); + if ( cp == (char*) 0 ) + (void) strcpy( dirname, "." ); + else + *cp = '\0'; + if ( access_check( hc, dirname ) == -1 ) + return -1; + + /* Check if the filename is the ACCESS_FILE itself - that's verboten. */ + if ( expnlen == sizeof(ACCESS_FILE) - 1 ) + { + if ( strcmp( hc->expnfilename, ACCESS_FILE ) == 0 ) + { + syslog( + LOG_NOTICE, + "%.80s URL \"%.80s\" tried to retrieve an access file", + httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' is an access file, retrieving it is not permitted.\n" ), + hc->encodedurl ); + return -1; + } + } + else if ( expnlen >= sizeof(ACCESS_FILE) && + strcmp( &(hc->expnfilename[expnlen - sizeof(ACCESS_FILE) + 1]), ACCESS_FILE ) == 0 && + hc->expnfilename[expnlen - sizeof(ACCESS_FILE)] == '/' ) + { + syslog( + LOG_NOTICE, + "%.80s URL \"%.80s\" tried to retrieve an access file", + httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' is an access file, retrieving it is not permitted.\n" ), + hc->encodedurl ); + return -1; + } +#endif /* ACCESS_FILE */ #ifdef AUTH_FILE /* Check authorization for this directory. */ diff -dru thttpd-2.23beta1/thttpd.8 thttpd-2.23beta1.fjo/thttpd.8 --- thttpd-2.23beta1/thttpd.8 Wed Apr 3 07:13:52 2002 +++ thttpd-2.23beta1.fjo/thttpd.8 Thu Sep 11 01:14:16 2003 @@ -263,6 +263,40 @@ modify .htpasswd files. .PP Relevant config.h option: AUTH_FILE +.SH "ACCESS RESTRICTION" +.PP +Access restriction is available as an option at compile time. +If enabled, it uses an access file in the directory to be protected, +called .htaccess by default. +This file consists of a rule and a host address or a network range per +line. Valid rules are: +.TP +.B allow from +The following host address or network range is allowed to access the requested +directory and its files. +.TP +.B deny from +The following host address or network range is not allowed to access the +requested directory and its files. +.PP +The protection does not carry over to subdirectories. +There are three ways to specify a valid host address or network range: +.TP +.B IPv4 host address +eg. 10.2.3.4 +.TP +.B IPv4 network with subnet mask +eg. 10.0.0.0/255.255.0.0 +.TP +.B IPv4 network using CIDR notation +eg. 10.0.0.0/16 +.PP +Note that rules are processed in the same order as they are listed in the +access file and that the first rule which matches the client's address is +applied (there is no order clause). So if there is no deny from 0.0.0.0/0 at +the end of the file the default action is to allow access. +.PP +Relevant config.h option: ACCESS_FILE .SH "THROTTLING" .PP The throttle file lets you set maximum byte rates on URLs or URL groups.