MediaWiki  master
FirejailCommand.php
Go to the documentation of this file.
1 <?php
21 namespace MediaWiki\Shell;
22 
23 use RuntimeException;
24 
31 class FirejailCommand extends Command {
32 
36  private $firejail;
37 
41  private $whitelistedPaths = [];
42 
46  public function __construct( string $firejail ) {
47  parent::__construct();
48  $this->firejail = $firejail;
49  }
50 
58  public function params( ...$args ): Command {
59  if ( count( $args ) === 1 && is_array( reset( $args ) ) ) {
60  // If only one argument has been passed, and that argument is an array,
61  // treat it as a list of arguments
62  $args = reset( $args );
63  }
64  foreach ( $args as $arg ) {
65  if ( substr( $arg, 0, 8 ) === '--output' ) {
66  $ex = new RuntimeException(
67  'FirejailCommand does not support parameters that start with --output'
68  );
69  $this->logger->error(
70  'command tried to shell out with a parameter starting with --output',
71  [
72  'arg' => $arg,
73  'exception' => $ex
74  ]
75  );
76  throw $ex;
77  }
78  }
79 
80  return parent::params( ...$args );
81  }
82 
86  public function whitelistPaths( array $paths ): Command {
87  $this->whitelistedPaths = array_merge( $this->whitelistedPaths, $paths );
88  return $this;
89  }
90 
94  protected function buildFinalCommand( string $command ): array {
95  // If there are no restrictions, don't use firejail
96  if ( $this->restrictions === 0 ) {
97  $splitCommand = explode( ' ', $command, 2 );
98  $this->logger->debug(
99  "firejail: Command {$splitCommand[0]} {params} has no restrictions",
100  [ 'params' => $splitCommand[1] ?? '' ]
101  );
102  return parent::buildFinalCommand( $command );
103  }
104 
105  if ( $this->firejail === false ) {
106  throw new RuntimeException( 'firejail is enabled, but cannot be found' );
107  }
108  // quiet has to come first to prevent firejail from adding
109  // any output.
110  $cmd = [ $this->firejail, '--quiet' ];
111  // Use a profile that allows people to add local overrides
112  // if their system is setup in an incompatible manner. Also it
113  // prevents any default profiles from running.
114  // FIXME: Doesn't actually override command-line switches?
115  $cmd[] = '--profile=' . __DIR__ . '/firejail.profile';
116 
117  // By default firejail hides all other user directories, so if
118  // MediaWiki is inside a home directory (/home) but not the
119  // current user's home directory, pass --allusers to whitelist
120  // the home directories again.
121  static $useAllUsers = null;
122  if ( $useAllUsers === null ) {
123  global $IP;
124  // In case people are doing funny things with symlinks
125  // or relative paths, resolve them all.
126  $realIP = realpath( $IP );
127  $currentUser = posix_getpwuid( posix_geteuid() );
128  $useAllUsers = ( strpos( $realIP, '/home/' ) === 0 )
129  && ( strpos( $realIP, $currentUser['dir'] ) !== 0 );
130  if ( $useAllUsers ) {
131  $this->logger->warning( 'firejail: MediaWiki is located ' .
132  'in a home directory that does not belong to the ' .
133  'current user, so allowing access to all home ' .
134  'directories (--allusers)' );
135  }
136  }
137 
138  if ( $useAllUsers ) {
139  $cmd[] = '--allusers';
140  }
141 
142  if ( $this->whitelistedPaths ) {
143  // Always whitelist limit.sh
144  $cmd[] = '--whitelist=' . __DIR__ . '/limit.sh';
145  foreach ( $this->whitelistedPaths as $whitelistedPath ) {
146  $cmd[] = "--whitelist={$whitelistedPath}";
147  }
148  }
149 
150  if ( $this->hasRestriction( Shell::NO_LOCALSETTINGS ) ) {
151  $cmd[] = '--blacklist=' . realpath( MW_CONFIG_FILE );
152  }
153 
154  if ( $this->hasRestriction( Shell::NO_ROOT ) ) {
155  $cmd[] = '--noroot';
156  }
157 
158  $useSeccomp = $this->hasRestriction( Shell::SECCOMP );
159  $extraSeccomp = [];
160 
161  if ( $this->hasRestriction( Shell::NO_EXECVE ) ) {
162  $extraSeccomp[] = 'execve';
163  // Normally firejail will run commands in a bash shell,
164  // but that won't work if we ban the execve syscall, so
165  // run the command without a shell.
166  $cmd[] = '--shell=none';
167  }
168 
169  if ( $useSeccomp ) {
170  $seccomp = '--seccomp';
171  if ( $extraSeccomp ) {
172  // The "@default" seccomp group will always be enabled
173  $seccomp .= '=' . implode( ',', $extraSeccomp );
174  }
175  $cmd[] = $seccomp;
176  }
177 
178  if ( $this->hasRestriction( Shell::PRIVATE_DEV ) ) {
179  $cmd[] = '--private-dev';
180  }
181 
182  if ( $this->hasRestriction( Shell::NO_NETWORK ) ) {
183  $cmd[] = '--net=none';
184  }
185 
186  $builtCmd = implode( ' ', $cmd );
187 
188  // Prefix the firejail command in front of the wanted command
189  return parent::buildFinalCommand( "$builtCmd -- {$command}" );
190  }
191 
192 }
MediaWiki\Shell\Shell\NO_EXECVE
const NO_EXECVE
Deny execve syscall with seccomp.
Definition: Shell.php:83
MediaWiki\Shell\FirejailCommand\whitelistPaths
whitelistPaths(array $paths)
If called, only the files/directories that are whitelisted will be available to the shell command....
Definition: FirejailCommand.php:86
MediaWiki\Shell\Command
Class used for executing shell commands.
Definition: Command.php:36
MediaWiki\Shell\Shell\SECCOMP
const SECCOMP
Use seccomp to block dangerous syscalls.
Definition: Shell.php:60
MediaWiki\Shell\FirejailCommand\params
params(... $args)
Reject any parameters that start with –output to prevent exploitation of a firejail RCE (CVE-2020-173...
Definition: FirejailCommand.php:58
MediaWiki\Shell\FirejailCommand\$whitelistedPaths
string[] $whitelistedPaths
Definition: FirejailCommand.php:41
$args
if( $line===false) $args
Definition: mcc.php:124
MediaWiki\Shell\FirejailCommand
Restricts execution of shell commands using firejail.
Definition: FirejailCommand.php:31
MediaWiki\Shell\Shell\NO_ROOT
const NO_ROOT
Disallow any root access.
Definition: Shell.php:52
MediaWiki\Shell\FirejailCommand\$firejail
string $firejail
Path to firejail.
Definition: FirejailCommand.php:36
MediaWiki\Shell\Command\$command
string $command
Definition: Command.php:40
MediaWiki\Shell\Command\hasRestriction
hasRestriction(int $restriction)
Bitfield helper on whether a specific restriction is enabled.
Definition: Command.php:318
MediaWiki\Shell\FirejailCommand\__construct
__construct(string $firejail)
Definition: FirejailCommand.php:46
MediaWiki\Shell
Copyright (C) 2017 Kunal Mehta legoktm@member.fsf.org
Definition: Command.php:21
MediaWiki\Shell\Shell\PRIVATE_DEV
const PRIVATE_DEV
Create a private /dev.
Definition: Shell.php:67
MediaWiki\Shell\Shell\NO_NETWORK
const NO_NETWORK
Restrict the request to have no network access.
Definition: Shell.php:75
$IP
$IP
Definition: WebStart.php:49
MediaWiki\Shell\FirejailCommand\buildFinalCommand
buildFinalCommand(string $command)
String together all the options and build the final command to execute.Already-escaped command to run...
Definition: FirejailCommand.php:94
MediaWiki\Shell\Shell\NO_LOCALSETTINGS
const NO_LOCALSETTINGS
Deny access to LocalSettings.php (MW_CONFIG_FILE)
Definition: Shell.php:90