Index: head/www/fcgiwrap/Makefile =================================================================== --- head/www/fcgiwrap/Makefile (revision 529748) +++ head/www/fcgiwrap/Makefile (revision 529749) @@ -1,35 +1,35 @@ # $FreeBSD$ PORTNAME= fcgiwrap PORTVERSION= 1.1.0 -PORTREVISION= 9 +PORTREVISION= 10 CATEGORIES= www MAINTAINER= rodrigo@FreeBSD.org COMMENT= Simple FastCGI wrapper for CGI scripts LICENSE= MIT LIB_DEPENDS= libfcgi.so:www/fcgi USES= autoreconf gmake pkgconfig USE_GITHUB= yes GH_ACCOUNT= gnosek USE_RC_SUBR= fcgiwrap CPPFLAGS+= -I${LOCALBASE}/include LDFLAGS+= -L${LOCALBASE}/lib GNU_CONFIGURE= yes PLIST_FILES= sbin/fcgiwrap man/man8/fcgiwrap.8.gz post-patch: @${REINPLACE_CMD} -e 's/@prefix@@mandir@/@mandir@/'\ ${WRKSRC}/Makefile.in post-install: ${STRIP_CMD} ${STAGEDIR}${PREFIX}/sbin/fcgiwrap .include Index: head/www/fcgiwrap/files/fcgiwrap.in =================================================================== --- head/www/fcgiwrap/files/fcgiwrap.in (revision 529748) +++ head/www/fcgiwrap/files/fcgiwrap.in (revision 529749) @@ -1,177 +1,177 @@ #!/bin/sh # $FreeBSD$ # # fcgiwrap startup script # # PROVIDE: fcgiwrap # REQUIRE: LOGIN # KEYWORD: shutdown # # Add the following to /etc/rc.conf[.local] to enable this service # # fcgiwrap_enable="YES" # # You can fine tune others variables too: # fcgiwrap_socket="unix:/var/run/fcgiwrap.sock" # this could also be: # - tcp:[ipv4_addr]:port (for ipv4) # - tcp6:[ipv6_addr]:port (for ipv6) # fcgiwrap_flags= # Use fcgiwrap_user to run fcgiwrap as user # Use fcgiwrap_group to run fcgiwrap as group # Use fcgiwrap_socket_mode to change the mode of the socket # Use fcgiwrap_socket_owner to change the owner of the socket # Use fcgiwrap_socket_group to change the group of the socket # fcgiwrap rc.d script supports multiple profiles (a-la rc.d/nginx) # When profiles are specified, the non-profile specific parameters become defaults. # You need to make sure that no two profiles have the same socket parameter. # # Example: # # fcgiwrap_enable="YES" # fcgiwrap_profiles="myserver myotherserver" # fcgiwrap_flags="-c 4" # fcgiwrap_socket_owner="www" # fcgiwrap_myserver_socket="unix:/var/run/fcgiwrap.myserver.socket" # fcgiwrap_myserver_user="myuser" # fcgiwrap_myserver_group="mygroup" # fcgiwrap_myotherserver_socket="unix:/var/run/fcgiwrap.myotherserver.socket" # fcgiwrap_myotherserver_user="myotheruser" -# fcgiwrap_myotherserver_grup="myothergroup" +# fcgiwrap_myotherserver_group="myothergroup" # fcgiwrap_myserver_socket_mode="0775" # fcgiwrap_myotherserver_flags="" # No flags for this profile. . /etc/rc.subr name="fcgiwrap" rcvar=fcgiwrap_enable fcgiwrap_setfib() { if command -v check_namevarlist > /dev/null 2>&1; then check_namevarlist fib && return 0 fi ${SYSCTL} net.fibs >/dev/null 2>&1 || return 0 fcgiwrap_fib=${fcgiwrap_fib:-"NONE"} case "$fcgiwrap_fib" in [Nn][Oo][Nn][Ee]) ;; *) command="setfib -F ${fcgiwrap_fib} ${command}" ;; esac } fcgiwrap_precmd() { fcgiwrap_setfib install -d -o root -g wheel -m 1777 /var/run/fcgiwrap } fcgiwrap_postcmd() { # This is only for unix sockets case "${fcgiwrap_socket}" in unix:*) ;; *) return ;; esac if [ -n "${fcgiwrap_socket_mode}" ]; then chmod ${fcgiwrap_socket_mode} ${fcgiwrap_socket#unix:} fi if [ "${fcgiwrap_socket_owner}" != "${fcgiwrap_user}" ]; then chown ${fcgiwrap_socket_owner} ${fcgiwrap_socket#unix:} fi if [ "${fcgiwrap_socket_group}" != "${fcgiwrap_group}" ]; then chgrp ${fcgiwrap_socket_group} ${fcgiwrap_socket#unix:} fi } fcgiwrap_stop() { if [ -s ${pidfile} ]; then fcgiwrap_pgrp=$(/bin/ps -o ppid= $(cat ${pidfile})) fi if [ -z "$fcgiwrap_pgrp" -o "${fcgiwrap_pgrp:-0}" -le 1 ] || ! kill -0 $fcgiwrap_pgrp; then [ -n "$rc_fast" ] && return 0 _run_rc_notrunning return 1 fi fcgiwrap_pgrp_pids=$(/bin/pgrep -d ' ' -g ${fcgiwrap_pgrp}) echo "Stopping ${name}." kill -TERM -- -${fcgiwrap_pgrp} wait_for_pids ${fcgiwrap_pgrp_pids} # Workaround the fact that fcgiwrap doesn't cleanup his socket at stopping case ${fcgiwrap_socket} in unix*) test -S ${fcgiwrap_socket#unix:} && rm -f ${fcgiwrap_socket#unix:} ;; esac rm -f $pidfile } pidprefix="/var/run/fcgiwrap/fcgiwrap" pidfile="${pidprefix}.pid" # May be a different path if profiles are in use procname="%%PREFIX%%/sbin/${name}" command="/usr/sbin/daemon" start_precmd="fcgiwrap_precmd" start_postcmd="fcgiwrap_postcmd" stop_cmd="fcgiwrap_stop" load_rc_config $name # These are just the defaults, they might get overriden for a specific profile. fcgiwrap_enable=${fcgiwrap_enable:-"NO"} fcgiwrap_user=${fcgiwrap_user:-"nobody"} fcgiwrap_group=${fcgiwrap_group:-"nogroup"} fcgiwrap_socket=${fcgiwrap_socket:-"unix:/var/run/fcgiwrap/fcgiwrap.sock"} fcgiwrap_socket_mode=${fcgiwrap_socket_mode:-"0775"} fcgiwrap_socket_owner=${fcgiwrap_socket_owner:-"nobody"} fcgiwrap_socket_group=${fcgiwrap_socket_group:-"nogroup"} # This handles profile specific vars. if [ -n "$2" ]; then profile="$2" if [ -n "${fcgiwrap_profiles}" ]; then pidfile="${pidprefix}.${profile}.pid" eval fcgiwrap_enable="\${fcgiwrap_${profile}_enable:-${fcgiwrap_enable}}" eval fcgiwrap_fib="\${fcgiwrap_${profile}_fib:-${fcgiwrap_fib}}" eval fcgiwrap_user="\${fcgiwrap_${profile}_user:-${fcgiwrap_user}}" eval fcgiwrap_group="\${fcgiwrap_${profile}_group:-${fcgiwrap_group}}" eval fcgiwrap_socket="\${fcgiwrap_${profile}_socket:?}" eval fcgiwrap_socket_mode="\${fcgiwrap_${profile}_socket_mode:-${fcgiwrap_socket_mode}}" eval fcgiwrap_socket_owner="\${fcgiwrap_${profile}_socket_owner:-${fcgiwrap_socket_owner}}" eval fcgiwrap_socket_group="\${fcgiwrap_${profile}_socket_group:-${fcgiwrap_socket_group}}" eval fcgiwrap_flags="\${fcgiwrap_${profile}_flags:-${fcgiwrap_flags}}" else echo "$0: extra argument ignored" fi else if [ -n "${fcgiwrap_profiles}" -a -n "$1" ]; then for profile in ${fcgiwrap_profiles}; do echo "===> fcgiwrap profile: ${profile}" /usr/local/etc/rc.d/fcgiwrap $1 ${profile} retcode="$?" if [ "0${retcode}" -ne 0 ]; then failed="${profile} (${retcode}) ${failed:-}" else success="${profile} ${success:-}" fi done # It exits so that non-profile rc.d is not started when there are profiles. exit 0 fi fi # run_rc_command would send ${name}_flags as parameters to $command (daemon) # This ensures they are actually passed to fcgiwrap instead. actual_fcgiwrap_flags="${fcgiwrap_flags}" fcgiwrap_flags="" command_args="-f -p ${pidfile} ${procname} -s ${fcgiwrap_socket} ${actual_fcgiwrap_flags}" fcgiwrap_saved_umask=$(umask) run_rc_command "$1" Index: head/www/fcgiwrap/files/patch-fcgiwrap.c =================================================================== --- head/www/fcgiwrap/files/patch-fcgiwrap.c (revision 529748) +++ head/www/fcgiwrap/files/patch-fcgiwrap.c (revision 529749) @@ -1,160 +1,168 @@ --- fcgiwrap.c.orig 2013-02-03 13:25:17 UTC +++ fcgiwrap.c @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -58,6 +59,8 @@ extern char **environ; static char * const * inherited_environ; +static const char **allowed_programs; +static size_t allowed_programs_count; static const char * blacklisted_env_vars[] = { "AUTH_TYPE", @@ -485,6 +488,19 @@ static void inherit_environment(void) } } +static bool is_allowed_program(const char *program) { + size_t i; + if (!allowed_programs_count) + return true; + + for (i = 0; i < allowed_programs_count; i++) { + if (!strcmp(allowed_programs[i], program)) + return true; + } + + return false; +} + static void cgi_error(const char *message, const char *reason, const char *filename) { printf("Status: %s\r\nContent-Type: text/plain\r\n\r\n%s\r\n", @@ -541,6 +557,9 @@ static void handle_fcgi_request(void) if (!filename) cgi_error("403 Forbidden", "Cannot get script name, are DOCUMENT_ROOT and SCRIPT_NAME (or SCRIPT_FILENAME) set and is the script executable?", NULL); + if (!is_allowed_program(filename)) + cgi_error("403 Forbidden", "The given script is not allowed to execute", filename); + last_slash = strrchr(filename, '/'); if (!last_slash) cgi_error("403 Forbidden", "Script name must be a fully qualified path", filename); @@ -587,14 +606,29 @@ err_pipein: FCGI_puts("System error"); } +static volatile sig_atomic_t sigint_received; +static void sigint_handler(int __attribute__((__unused__))dummy) +{ + sigint_received = 1; + FCGX_ShutdownPending(); // Or we could send SIGUSR1 +} + static void fcgiwrap_main(void) { + struct sigaction a; signal(SIGCHLD, SIG_IGN); signal(SIGPIPE, SIG_IGN); + // Use sigaction for SIGINT so we can avoid SA_RESTART and actually react + a.sa_handler = sigint_handler; + a.sa_flags = 0; + sigemptyset(&a.sa_mask); + sigaction(SIGINT, &a, NULL); + sigaction(SIGTERM, &a, NULL); + inherited_environ = environ; - while (FCGI_Accept() >= 0) { + while (FCGI_Accept() >= 0 && !sigint_received) { handle_fcgi_request(); } } @@ -671,7 +705,7 @@ static int listen_on_fd(int fd) { return 0; } -static int setup_socket(char *url) { +static int setup_socket(char *url, int *fd_out) { char *p = url; char *q; int fd; -@@ -751,6 +785,7 @@ invalid_url: +@@ -685,6 +719,7 @@ static int setup_socket(char *url) { + struct sockaddr_in6 sa_in6; + } sa; + ++ memset(&sa, 0, sizeof(sa)); + if (!strncmp(p, "unix:", sizeof("unix:") - 1)) { + p += sizeof("unix:") - 1; + +@@ -751,6 +786,7 @@ invalid_url: return -1; } + *fd_out = fd; return listen_on_fd(fd); } -@@ -758,9 +793,10 @@ int main(int argc, char **argv) +@@ -758,9 +794,10 @@ int main(int argc, char **argv) { int nchildren = 1; char *socket_url = NULL; + int fd = 0; int c; - while ((c = getopt(argc, argv, "c:hfs:")) != -1) { + while ((c = getopt(argc, argv, "c:hfs:p:")) != -1) { switch (c) { case 'f': stderr_to_fastcgi++; -@@ -773,6 +809,7 @@ int main(int argc, char **argv) +@@ -773,6 +810,7 @@ int main(int argc, char **argv) " -c \t\tNumber of processes to prefork\n" " -s \tSocket to bind to (say -s help for help)\n" " -h\t\t\tShow this help message and exit\n" + " -p \t\tRestrict execution to this script. (repeated options will be merged)\n" "\nReport bugs to Grzegorz Nosek <"PACKAGE_BUGREPORT">.\n" PACKAGE_NAME" home page: \n", argv[0] -@@ -784,8 +821,14 @@ int main(int argc, char **argv) +@@ -784,8 +822,14 @@ int main(int argc, char **argv) case 's': socket_url = strdup(optarg); break; + case 'p': + allowed_programs = realloc(allowed_programs, (allowed_programs_count + 1) * sizeof (char *)); + if (!allowed_programs) + abort(); + allowed_programs[allowed_programs_count++] = strdup(optarg); + break; case '?': - if (optopt == 'c' || optopt == 's') + if (optopt == 'c' || optopt == 's' || optopt == 'p') fprintf(stderr, "Option -%c requires an argument.\n", optopt); else if (isprint(optopt)) fprintf(stderr, "Unknown option `-%c'.\n", optopt); -@@ -808,13 +851,24 @@ int main(int argc, char **argv) +@@ -808,13 +852,24 @@ int main(int argc, char **argv) } else #endif if (socket_url) { - if (setup_socket(socket_url) < 0) { + if (setup_socket(socket_url, &fd) < 0) { return 1; } - free(socket_url); } prefork(nchildren); fcgiwrap_main(); + if(fd) { + const char *p = socket_url; + close(fd); + + if(socket_url) { + if (!strncmp(p, "unix:", sizeof("unix:") - 1)) { + p += sizeof("unix:") - 1; + unlink(p); + } + free(socket_url); + } + } return 0; }