Skip to content

Add apparmor change hat functionality to fpm #373

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 17, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion run-tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ function save_or_mail_results()
$ignored_by_ext = 0;
sort($exts_to_test);
$test_dirs = array();
$optionals = array('tests', 'ext', 'Zend', 'ZendEngine2', 'sapi/cli', 'sapi/cgi');
$optionals = array('tests', 'ext', 'Zend', 'ZendEngine2', 'sapi/cli', 'sapi/cgi', 'sapi/fpm');

foreach($optionals as $dir) {
if (@filetype($dir) == 'dir') {
Expand Down
17 changes: 17 additions & 0 deletions sapi/fpm/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,22 @@ AC_DEFUN([AC_FPM_SELECT],
])
dnl }}}

AC_DEFUN([AC_FPM_APPARMOR],
[
AC_MSG_CHECKING([for apparmor])

SAVED_LIBS="$LIBS"
LIBS="$LIBS -lapparmor"

AC_TRY_LINK([ #include <sys/apparmor.h> ], [change_hat("test", 0);], [
AC_DEFINE([HAVE_APPARMOR], 1, [do we have apparmor support?])
AC_MSG_RESULT([yes])
], [
LIBS="$SAVED_LIBS"
AC_MSG_RESULT([no])
])
])


AC_MSG_CHECKING(for FPM build)
if test "$PHP_FPM" != "no"; then
Expand All @@ -555,6 +571,7 @@ if test "$PHP_FPM" != "no"; then
AC_FPM_EPOLL
AC_FPM_POLL
AC_FPM_SELECT
AC_FPM_APPARMOR

PHP_ARG_WITH(fpm-user,,
[ --with-fpm-user[=USER] Set the user for php-fpm to run as. (default: nobody)], nobody, no)
Expand Down
4 changes: 3 additions & 1 deletion sapi/fpm/fpm/fpm.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ struct fpm_globals_s fpm_globals = {
.test_successful = 0,
.heartbeat = 0,
.run_as_root = 0,
.force_stderr = 0,
.send_config_pipe = {0, 0},
};

int fpm_init(int argc, char **argv, char *config, char *prefix, char *pid, int test_conf, int run_as_root, int force_daemon) /* {{{ */
int fpm_init(int argc, char **argv, char *config, char *prefix, char *pid, int test_conf, int run_as_root, int force_daemon, int force_stderr) /* {{{ */
{
fpm_globals.argc = argc;
fpm_globals.argv = argv;
Expand All @@ -52,6 +53,7 @@ int fpm_init(int argc, char **argv, char *config, char *prefix, char *pid, int t
fpm_globals.prefix = prefix;
fpm_globals.pid = pid;
fpm_globals.run_as_root = run_as_root;
fpm_globals.force_stderr = force_stderr;

if (0 > fpm_php_init_main() ||
0 > fpm_stdio_init_main() ||
Expand Down
3 changes: 2 additions & 1 deletion sapi/fpm/fpm/fpm.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@


int fpm_run(int *max_requests);
int fpm_init(int argc, char **argv, char *config, char *prefix, char *pid, int test_conf, int run_as_root, int force_daemon);
int fpm_init(int argc, char **argv, char *config, char *prefix, char *pid, int test_conf, int run_as_root, int force_daemon, int force_stderr);

struct fpm_globals_s {
pid_t parent_pid;
Expand All @@ -55,6 +55,7 @@ struct fpm_globals_s {
int test_successful;
int heartbeat;
int run_as_root;
int force_stderr;
int send_config_pipe[2];
};

Expand Down
6 changes: 6 additions & 0 deletions sapi/fpm/fpm/fpm_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ static struct ini_value_parser_s ini_fpm_pool_options[] = {
{ "chdir", &fpm_conf_set_string, WPO(chdir) },
{ "catch_workers_output", &fpm_conf_set_boolean, WPO(catch_workers_output) },
{ "security.limit_extensions", &fpm_conf_set_string, WPO(security_limit_extensions) },
#ifdef HAVE_APPARMOR
{ "apparmor_hat", &fpm_conf_set_string, WPO(apparmor_hat) },
#endif
{ 0, 0, 0 }
};

Expand Down Expand Up @@ -644,6 +647,9 @@ int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc) /* {{{ */
free(wpc->chroot);
free(wpc->chdir);
free(wpc->security_limit_extensions);
#ifdef HAVE_APPARMOR
free(wpc->apparmor_hat);
#endif

for (kv = wpc->php_values; kv; kv = kv_next) {
kv_next = kv->next;
Expand Down
3 changes: 3 additions & 0 deletions sapi/fpm/fpm/fpm_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ struct fpm_worker_pool_config_s {
struct key_value_s *env;
struct key_value_s *php_admin_values;
struct key_value_s *php_values;
#ifdef HAVE_APPARMOR
char *apparmor_hat;
#endif
};

struct ini_value_parser_s {
Expand Down
12 changes: 10 additions & 2 deletions sapi/fpm/fpm/fpm_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ static const opt_struct OPTIONS[] = {
{'R', 0, "allow-to-run-as-root"},
{'D', 0, "daemonize"},
{'F', 0, "nodaemonize"},
{'O', 0, "force-stderr"},
{'-', 0, NULL} /* end of args */
};

Expand Down Expand Up @@ -918,7 +919,7 @@ static void php_cgi_usage(char *argv0)
prog = "php";
}

php_printf( "Usage: %s [-n] [-e] [-h] [-i] [-m] [-v] [-t] [-p <prefix>] [-g <pid>] [-c <file>] [-d foo[=bar]] [-y <file>] [-D] [-F]\n"
php_printf( "Usage: %s [-n] [-e] [-h] [-i] [-m] [-v] [-t] [-p <prefix>] [-g <pid>] [-c <file>] [-d foo[=bar]] [-y <file>] [-D] [-F [-O]]\n"
" -c <path>|<file> Look for php.ini file in this directory\n"
" -n No php.ini file will be used\n"
" -d foo[=bar] Define INI entry foo with value 'bar'\n"
Expand All @@ -937,6 +938,8 @@ static void php_cgi_usage(char *argv0)
" -D, --daemonize force to run in background, and ignore daemonize option from config file\n"
" -F, --nodaemonize\n"
" force to stay in foreground, and ignore daemonize option from config file\n"
" -O, --force-stderr\n"
" force output to stderr in nodaemonize even if stderr is not a TTY\n"
" -R, --allow-to-run-as-root\n"
" Allow pool to run as root (disabled by default)\n",
prog, PHP_PREFIX);
Expand Down Expand Up @@ -1569,6 +1572,7 @@ int main(int argc, char *argv[])
char *fpm_pid = NULL;
int test_conf = 0;
int force_daemon = -1;
int force_stderr = 0;
int php_information = 0;
int php_allow_to_run_as_root = 0;

Expand Down Expand Up @@ -1697,6 +1701,10 @@ int main(int argc, char *argv[])
force_daemon = 0;
break;

case 'O': /* force stderr even on non tty */
force_stderr = 1;
break;

default:
case 'h':
case '?':
Expand Down Expand Up @@ -1824,7 +1832,7 @@ consult the installation file that came with this distribution, or visit \n\
}
}

if (0 > fpm_init(argc, argv, fpm_config ? fpm_config : CGIG(fpm_config), fpm_prefix, fpm_pid, test_conf, php_allow_to_run_as_root, force_daemon)) {
if (0 > fpm_init(argc, argv, fpm_config ? fpm_config : CGIG(fpm_config), fpm_prefix, fpm_pid, test_conf, php_allow_to_run_as_root, force_daemon, force_stderr)) {

if (fpm_globals.send_config_pipe[1]) {
int writeval = 0;
Expand Down
2 changes: 1 addition & 1 deletion sapi/fpm/fpm/fpm_stdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ int fpm_stdio_open_error_log(int reopen) /* {{{ */
} else {
fpm_globals.error_log_fd = fd;
#if HAVE_UNISTD_H
if (fpm_global_config.daemonize || !isatty(STDERR_FILENO)) {
if (fpm_global_config.daemonize || (!isatty(STDERR_FILENO) && !fpm_globals.force_stderr)) {
#else
if (fpm_global_config.daemonize) {
#endif
Expand Down
30 changes: 30 additions & 0 deletions sapi/fpm/fpm/fpm_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
#include <sys/prctl.h>
#endif

#ifdef HAVE_APPARMOR
#include <sys/apparmor.h>
#endif

#include "fpm.h"
#include "fpm_conf.h"
#include "fpm_cleanup.h"
Expand Down Expand Up @@ -222,6 +226,32 @@ int fpm_unix_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
if (0 > fpm_clock_init()) {
return -1;
}

#ifdef HAVE_APPARMOR
if (wp->config->apparmor_hat) {
char *con, *new_con;
if (aa_getcon(&con, NULL) == -1) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to query apparmor confinement. Please check if \"/proc/*/attr/current\" is read and writeable.", wp->config->name);
return -1;
}
new_con = malloc(strlen(con) + strlen(wp->config->apparmor_hat) + 3); // // + 0 Byte
if (!new_con) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to allocate memory for apparmor hat change.", wp->config->name);
return -1;
}
if (0 > sprintf(new_con, "%s//%s", con, wp->config->apparmor_hat)) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to construct apparmor confinement.", wp->config->name);
return -1;
}
if (0 > aa_change_profile(new_con)) {
zlog(ZLOG_SYSERROR, "[pool %s] failed to change to new confinement (%s). Please check if \"/proc/*/attr/current\" is read and writeable and \"change_profile -> %s//*\" is allowed.", wp->config->name, new_con, con);
return -1;
}
free(con);
free(new_con);
}
#endif

return 0;
}
/* }}} */
Expand Down
21 changes: 21 additions & 0 deletions sapi/fpm/tests/001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
FPM: version string
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php

include "include.inc";

$php = get_fpm_path();

var_dump(`$php -n -v`);

echo "Done\n";
?>
--EXPECTF--
string(%d) "PHP %s (fpm%s (built: %s
Copyright (c) 1997-20%s The PHP Group
Zend Engine v%s, Copyright (c) 1998-20%s Zend Technologies
"
Done
53 changes: 53 additions & 0 deletions sapi/fpm/tests/002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
--TEST--
FPM: Startup and connect
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php

include "include.inc";

$logfile = dirname(__FILE__).'/php-fpm.log.tmp';

$cfg = <<<EOT
[global]
error_log = $logfile
[unconfined]
listen = 127.0.0.1:9000
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOT;

$fpm = run_fpm($cfg, $tail);
if (is_resource($fpm)) {
var_dump(fgets($tail));
var_dump(fgets($tail));
$i = 0;
while (($i++ < 30) && !($fp = @fsockopen('127.0.0.1', 9000))) {
usleep(10000);
}
if ($fp) {
echo "Done\n";
fclose($fp);
}
proc_terminate($fpm);
stream_get_contents($tail);
fclose($tail);
proc_close($fpm);
}

?>
--EXPECTF--
string(%d) "[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d
"
string(%d) "[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections
"
Done
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>
54 changes: 54 additions & 0 deletions sapi/fpm/tests/apparmor.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
--TEST--
FPM: Apparmor Test
--DESCRIPTION--
This test tries to launches a pool which tries to change to non existing
apparmor hat a. Test succeeds if apparmor is not running or hat is non
existant.
--SKIPIF--
<?php
include "skipif.inc";
include "skipapparmor.inc";

?>
--FILE--
<?php

include "include.inc";

$logfile = dirname(__FILE__).'/php-fpm.log.tmp';

$cfg = <<<EOT
[global]
error_log = $logfile
[a]
listen = 127.0.0.1:9001
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
apparmor_hat = a
EOT;

/* libapparmor has a bug which can cause SIGSEGV till Version 2.8.0-0ubuntu28
See https://bugs.launchpad.net/apparmor/+bug/1196880
Possible outcomes:

- SIGSEGV|failed to query apparmor confinement
apparmor not running
- failed to change to new confinement
something in apparmor went wrong
- exited with code 70
Change to successful; Hat not existant (Process gets killed by apparmor)
*/
var_dump(run_fpm_till('/(SIGSEGV|failed to query apparmor confinement|failed to change to new confinement|exited with code 70)/', $cfg));

?>
--EXPECTF--
string(%d) "%s
"
--CLEAN--
<?php
$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
@unlink($logfile);
?>
Loading