>From 2845b3bed86ca649d3206d9b1e0fe30a4ca33110 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Thu, 24 Dec 2020 03:49:20 +0100 Subject: [PATCH 3/3] posix_spawn, posix_spawnp: Fix execution of scripts. * m4/posix_spawn.m4 (gl_POSIX_SPAWN_SECURE): New macro. (gl_POSIX_SPAWN_BODY): Invoke it. Set REPLACE_POSIX_SPAWN if posix_spawn or posix_spawnp allows unsecure execution of scripts. * doc/posix-functions/posix_spawn.texi: Document the script execution problem. * doc/posix-functions/posix_spawnp.texi: Likewise. --- ChangeLog | 10 ++ doc/posix-functions/posix_spawn.texi | 5 + doc/posix-functions/posix_spawnp.texi | 5 + m4/posix_spawn.m4 | 169 +++++++++++++++++++++++++++++----- 4 files changed, 164 insertions(+), 25 deletions(-) diff --git a/ChangeLog b/ChangeLog index bfb7f88..e7e5f11 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ 2020-12-23 Bruno Haible + posix_spawn, posix_spawnp: Fix execution of scripts. + * m4/posix_spawn.m4 (gl_POSIX_SPAWN_SECURE): New macro. + (gl_POSIX_SPAWN_BODY): Invoke it. Set REPLACE_POSIX_SPAWN if posix_spawn + or posix_spawnp allows unsecure execution of scripts. + * doc/posix-functions/posix_spawn.texi: Document the script execution + problem. + * doc/posix-functions/posix_spawnp.texi: Likewise. + +2020-12-23 Bruno Haible + Add unit tests regarding execution of scripts. * tests/executable-script: New file. * tests/executable-shell-script: New file. diff --git a/doc/posix-functions/posix_spawn.texi b/doc/posix-functions/posix_spawn.texi index 7a3a2f6..e0b39b0 100644 --- a/doc/posix-functions/posix_spawn.texi +++ b/doc/posix-functions/posix_spawn.texi @@ -15,6 +15,11 @@ FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, When this function fails, it causes the stdio buffer contents to be output twice on some platforms: AIX 6.1. +@item +When the program to be invoked is an executable script without a @samp{#!} +marker in the first line, this function executes the script as if it were +a shell script, on some platforms: +GNU/Hurd. @end itemize Portability problems not fixed by Gnulib: diff --git a/doc/posix-functions/posix_spawnp.texi b/doc/posix-functions/posix_spawnp.texi index c420db9..2dd9f68 100644 --- a/doc/posix-functions/posix_spawnp.texi +++ b/doc/posix-functions/posix_spawnp.texi @@ -15,6 +15,11 @@ FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, When this function fails, it causes the stdio buffer contents to be output twice on some platforms: AIX 6.1. +@item +When the program to be invoked is an executable script without a @samp{#!} +marker in the first line, this function executes the script as if it were +a shell script, on some platforms: +glibc 2.14, GNU/Hurd, Mac OS X 10.13, FreeBSD 12.0, OpenBSD 6.7, AIX 7.2, Solaris 11.4, Cygwin 2.9. @end itemize Portability problems not fixed by Gnulib: diff --git a/m4/posix_spawn.m4 b/m4/posix_spawn.m4 index 625b2ad..59e56fc 100644 --- a/m4/posix_spawn.m4 +++ b/m4/posix_spawn.m4 @@ -1,4 +1,4 @@ -# posix_spawn.m4 serial 18 +# posix_spawn.m4 serial 19 dnl Copyright (C) 2008-2020 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -54,40 +54,52 @@ AC_DEFUN([gl_POSIX_SPAWN_BODY], if test $REPLACE_POSIX_SPAWN = 0; then gl_POSIX_SPAWN_WORKS case "$gl_cv_func_posix_spawn_works" in - *yes) - dnl Assume that these functions are available if POSIX_SPAWN_SETSCHEDULER - dnl evaluates to nonzero. - dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getschedpolicy]) - dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setschedpolicy]) - AC_CACHE_CHECK([whether posix_spawnattr_setschedpolicy is supported], - [gl_cv_func_spawnattr_setschedpolicy], - [AC_EGREP_CPP([POSIX scheduling supported], [ + *yes) ;; + *) REPLACE_POSIX_SPAWN=1 ;; + esac + fi + if test $REPLACE_POSIX_SPAWN = 0; then + gl_POSIX_SPAWN_SECURE + case "$gl_cv_func_posix_spawn_secure_exec" in + *yes) ;; + *) REPLACE_POSIX_SPAWN=1 ;; + esac + case "$gl_cv_func_posix_spawnp_secure_exec" in + *yes) ;; + *) REPLACE_POSIX_SPAWN=1 ;; + esac + fi + if test $REPLACE_POSIX_SPAWN = 0; then + dnl Assume that these functions are available if POSIX_SPAWN_SETSCHEDULER + dnl evaluates to nonzero. + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getschedpolicy]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setschedpolicy]) + AC_CACHE_CHECK([whether posix_spawnattr_setschedpolicy is supported], + [gl_cv_func_spawnattr_setschedpolicy], + [AC_EGREP_CPP([POSIX scheduling supported], [ #include #if POSIX_SPAWN_SETSCHEDULER POSIX scheduling supported #endif ], - [gl_cv_func_spawnattr_setschedpolicy=yes], - [gl_cv_func_spawnattr_setschedpolicy=no]) - ]) - dnl Assume that these functions are available if POSIX_SPAWN_SETSCHEDPARAM - dnl evaluates to nonzero. - dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getschedparam]) - dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setschedparam]) - AC_CACHE_CHECK([whether posix_spawnattr_setschedparam is supported], - [gl_cv_func_spawnattr_setschedparam], - [AC_EGREP_CPP([POSIX scheduling supported], [ + [gl_cv_func_spawnattr_setschedpolicy=yes], + [gl_cv_func_spawnattr_setschedpolicy=no]) + ]) + dnl Assume that these functions are available if POSIX_SPAWN_SETSCHEDPARAM + dnl evaluates to nonzero. + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getschedparam]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setschedparam]) + AC_CACHE_CHECK([whether posix_spawnattr_setschedparam is supported], + [gl_cv_func_spawnattr_setschedparam], + [AC_EGREP_CPP([POSIX scheduling supported], [ #include #if POSIX_SPAWN_SETSCHEDPARAM POSIX scheduling supported #endif ], - [gl_cv_func_spawnattr_setschedparam=yes], - [gl_cv_func_spawnattr_setschedparam=no]) - ]) - ;; - *) REPLACE_POSIX_SPAWN=1 ;; - esac + [gl_cv_func_spawnattr_setschedparam=yes], + [gl_cv_func_spawnattr_setschedparam=no]) + ]) fi fi if test $ac_cv_func_posix_spawn != yes || test $REPLACE_POSIX_SPAWN = 1; then @@ -417,6 +429,113 @@ main (int argc, char *argv[]) ]) ]) +dnl Test whether posix_spawn and posix_spawnp are secure. +AC_DEFUN([gl_POSIX_SPAWN_SECURE], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + dnl On many platforms, posix_spawn or posix_spawnp allow executing a + dnl script without a '#!' marker as a shell script. This is unsecure. + AC_CACHE_CHECK([whether posix_spawn rejects scripts without shebang], + [gl_cv_func_posix_spawn_secure_exec], + [echo ':' > conftest.scr + chmod a+x conftest.scr + AC_RUN_IFELSE([AC_LANG_SOURCE([[ + #include + #include + #include + #include + #include + int + main () + { + const char *prog_path = "./conftest.scr"; + const char *prog_argv[2] = { prog_path, NULL }; + const char *environment[2] = { "PATH=.", NULL }; + pid_t child; + int status; + int err = posix_spawn (&child, prog_path, NULL, NULL, + (char **) prog_argv, (char **) environment); + if (err == ENOEXEC) + return 0; + if (err != 0) + return 1; + status = 0; + while (waitpid (child, &status, 0) != child) + ; + if (!WIFEXITED (status)) + return 2; + if (WEXITSTATUS (status) != 127) + return 3; + return 0; + } + ]])], + [gl_cv_func_posix_spawn_secure_exec=yes], + [gl_cv_func_posix_spawn_secure_exec=no], + [case "$host_os" in + # Guess no on GNU/Hurd. + gnu*) + gl_cv_func_posix_spawn_secure_exec="guessing no" ;; + # Guess yes on all other platforms. + *) + gl_cv_func_posix_spawn_secure_exec="guessing yes" ;; + esac + ]) + rm -f conftest.scr + ]) + AC_CACHE_CHECK([whether posix_spawnp rejects scripts without shebang], + [gl_cv_func_posix_spawnp_secure_exec], + [echo ':' > conftest.scr + chmod a+x conftest.scr + AC_RUN_IFELSE([AC_LANG_SOURCE([[ + #include + #include + #include + #include + #include + int + main () + { + const char *prog_path = "./conftest.scr"; + const char *prog_argv[2] = { prog_path, NULL }; + const char *environment[2] = { "PATH=.", NULL }; + pid_t child; + int status; + int err = posix_spawnp (&child, prog_path, NULL, NULL, + (char **) prog_argv, (char **) environment); + if (err == ENOEXEC) + return 0; + if (err != 0) + return 1; + status = 0; + while (waitpid (child, &status, 0) != child) + ; + if (!WIFEXITED (status)) + return 2; + if (WEXITSTATUS (status) != 127) + return 3; + return 0; + } + ]])], + [gl_cv_func_posix_spawnp_secure_exec=yes], + [gl_cv_func_posix_spawnp_secure_exec=no], + [case "$host_os" in + # Guess yes on glibc systems (glibc >= 2.15 actually) except GNU/Hurd, + # musl libc, NetBSD. + *-gnu* | *-musl* | netbsd*) + gl_cv_func_posix_spawnp_secure_exec="guessing yes" ;; + # Guess no on GNU/Hurd, macOS, FreeBSD, OpenBSD, AIX, Solaris, Cygwin. + gnu* | darwin* | freebsd* | dragonfly* | openbsd* | aix* | solaris* | cygwin*) + gl_cv_func_posix_spawnp_secure_exec="guessing no" ;; + # If we don't know, obey --enable-cross-guesses. + *) + gl_cv_func_posix_spawnp_secure_exec="$gl_cross_guess_normal" ;; + esac + ]) + rm -f conftest.scr + ]) +]) + # Prerequisites of lib/spawni.c. AC_DEFUN([gl_PREREQ_POSIX_SPAWN_INTERNAL], [ -- 2.7.4