| 201 | | net_write("# Unknown command '$command'\n"); |
|---|
| 202 | | return undef; |
|---|
| 203 | | } |
|---|
| 204 | | |
|---|
| 205 | | # Untaint $service, we know it's safe (it's in the %services hash) |
|---|
| 206 | | $service =~ /^(.*)$/; |
|---|
| 207 | | $service = $1; |
|---|
| 208 | | |
|---|
| 209 | | if ($child = open (CHILD, "-|")) { |
|---|
| 210 | | eval { |
|---|
| 211 | | local $SIG{ALRM} = sub { $timed_out=1; die "$!\n"}; |
|---|
| 212 | | alarm($timeout); |
|---|
| 213 | | while(<CHILD>) { |
|---|
| 214 | | push @lines,$_; |
|---|
| 215 | | } |
|---|
| 216 | | }; |
|---|
| 217 | | if( $timed_out ) { |
|---|
| 218 | | reap_children($child, "$service $command: $@"); |
|---|
| 219 | | close (CHILD); |
|---|
| 220 | | return (); |
|---|
| 221 | | } |
|---|
| 222 | | unless (close CHILD) |
|---|
| 223 | | { |
|---|
| 224 | | # If Net::Server::Fork is currently taking care of reaping, |
|---|
| 225 | | # we get false errors. Filter them out. |
|---|
| 226 | | if ($! and not $autoreap) |
|---|
| 227 | | { |
|---|
| 228 | | logger ("Error while executing plugin \"$service\": $!"); |
|---|
| 229 | | } |
|---|
| 230 | | else |
|---|
| 231 | | { |
|---|
| 232 | | logger ("Plugin \"$service\" exited with status $?. --@lines--"); |
|---|
| 233 | | } |
|---|
| 234 | | } |
|---|
| 235 | | } |
|---|
| 236 | | else { |
|---|
| 237 | | if ($child == 0) { |
|---|
| 238 | | # New process group... |
|---|
| 239 | | POSIX::setsid(); |
|---|
| 240 | | # Setting environment |
|---|
| 241 | | $sconf{$service}{user} = get_var (\%sconf, $service, 'user'); |
|---|
| 242 | | $sconf{$service}{group} = get_var (\%sconf, $service, 'group'); |
|---|
| 243 | | $sconf{$service}{command} = get_var (\%sconf, $service, 'command'); |
|---|
| 244 | | get_var (\%sconf, $service, 'env', \%{$sconf{$service}{env}}); |
|---|
| 245 | | |
|---|
| 246 | | if ($< == 0) # If root... |
|---|
| 247 | | { |
|---|
| 248 | | # Giving up gid egid uid euid |
|---|
| 249 | | my $u = (defined $sconf{$service}{'user'}? |
|---|
| 250 | | $sconf{$service}{'user'}: |
|---|
| 251 | | $defuser); |
|---|
| 252 | | my $g = $defgroup; |
|---|
| 253 | | my $gs = "$g $g" . |
|---|
| 254 | | (defined $sconf{$service}{'group'} ? |
|---|
| 255 | | " $sconf{$service}{group}" : ""); |
|---|
| 256 | | |
|---|
| 257 | | debug ("Want to run as euid/egid $u/$g\n"); |
|---|
| 258 | | |
|---|
| 259 | | $( = $g unless $g == 0; |
|---|
| 260 | | $) = $gs unless $g == 0; |
|---|
| 261 | | $< = $u unless $u == 0; |
|---|
| 262 | | $> = $u unless $u == 0; |
|---|
| 263 | | |
|---|
| 264 | | if ($> != $u or $g != (split (' ', $)))[0]) |
|---|
| 265 | | { |
|---|
| 266 | | # net_write ("# Can't drop privileges. Bailing out. (wanted uid=", |
|---|
| 267 | | # ($sconf{$service}{'user'} || $defuser), " gid=\"", |
|---|
| 268 | | # $gs, "\"($g), got uid=$> gid=\"$)\"(", |
|---|
| 269 | | # (split (' ', $)))[0], ").\n"); |
|---|
| 270 | | logger ("Plugin \"$service\" Can't drop privileges. ". |
|---|
| 271 | | "Bailing out. (wanted uid=". |
|---|
| 272 | | ($sconf{$service}{'user'} || $defuser). " gid=\"". |
|---|
| 273 | | $gs. "\"($g), got uid=$> gid=\"$)\"(". |
|---|
| 274 | | (split (' ', $)))[0]. ").\n"); |
|---|
| 275 | | exit 1; |
|---|
| 276 | | } |
|---|
| 277 | | } |
|---|
| 278 | | debug ("Running as uid/gid/euid/egid $</$(/$>/$)\n"); |
|---|
| 279 | | if (!check_perms ("$servicedir/$service")) |
|---|
| 280 | | { |
|---|
| 281 | | net_write ("# Error: unsafe permissions. Bailing out."); |
|---|
| 282 | | logger ("Error: unsafe permissions. Bailing out."); |
|---|
| 283 | | exit 2; |
|---|
| 284 | | } |
|---|
| 285 | | |
|---|
| 286 | | # Setting environment... |
|---|
| 287 | | if (exists $sconf{$service}{'env'} and |
|---|
| 288 | | defined $sconf{$service}{'env'}) |
|---|
| 289 | | { |
|---|
| 290 | | foreach my $key (keys %{$sconf{$service}{'env'}}) |
|---|
| 291 | | { |
|---|
| 292 | | debug ("Setting environment $key=$sconf{$service}{env}{$key}\n"); |
|---|
| 293 | | $ENV{"$key"} = $sconf{$service}{'env'}{$key}; |
|---|
| 294 | | } |
|---|
| 295 | | } |
|---|
| 296 | | if (exists $sconf{$service}{'command'} and |
|---|
| 297 | | defined $sconf{$service}{'command'}) |
|---|
| 298 | | { |
|---|
| 299 | | my @run = (); |
|---|
| 300 | | foreach my $t (@{$sconf{$service}{'command'}}) |
|---|
| 301 | | { |
|---|
| 302 | | if ($t =~ /^%c$/) |
|---|
| 303 | | { |
|---|
| 304 | | push (@run, "$servicedir/$service", $command); |
|---|
| 305 | | } |
|---|
| 306 | | else |
|---|
| 307 | | { |
|---|
| 308 | | push (@run, $t); |
|---|
| 309 | | } |
|---|
| 310 | | } |
|---|
| 311 | | debug ("About to run \"", join (' ', @run), "\"\n"); |
|---|
| 312 | | exec (@run) if @run; |
|---|
| 313 | | } |
|---|
| 314 | | else |
|---|
| 315 | | { |
|---|
| 316 | | debug ("Execing...\n"); |
|---|
| 317 | | exec ("$servicedir/$service", $command); |
|---|
| 318 | | } |
|---|
| 319 | | } |
|---|
| 320 | | else { |
|---|
| 321 | | net_write ("# Unable to fork.\n"); |
|---|
| 322 | | logger ("Unable to fork."); |
|---|
| 323 | | } |
|---|
| 324 | | } |
|---|
| 325 | | wait; |
|---|
| 326 | | alarm(0); |
|---|
| 327 | | } |
|---|
| 328 | | else { |
|---|
| 329 | | return "# Unknown service\n"; |
|---|
| 330 | | } |
|---|
| 331 | | return (@lines); |
|---|
| | 207 | logger("Unknown command '$command'"); |
|---|
| | 208 | return "# Unknown command '$command'\n"; |
|---|
| | 209 | } |
|---|
| | 210 | |
|---|
| | 211 | my @run; |
|---|
| | 212 | if (defined $sconf{$service}{'command'}) { |
|---|
| | 213 | @run = map { $_ eq '%c' ? "$servicedir/$service" : $_ } |
|---|
| | 214 | @{$sconf{$service}{'command'}} |
|---|
| | 215 | } else { |
|---|
| | 216 | @run = ("$servicedir/$service", $command); |
|---|
| | 217 | } |
|---|
| | 218 | |
|---|
| | 219 | eval { |
|---|
| | 220 | run(\@run, |
|---|
| | 221 | '>', new_chunker("\n"), sub { push @lines, @_ }, |
|---|
| | 222 | '2>', new_chunker("\n"), sub { logger("$service: $_[0]") }, |
|---|
| | 223 | init => sub { setup_environment($service) }, |
|---|
| | 224 | timeout($timeout, exception => "plugin timed out"), |
|---|
| | 225 | ); |
|---|
| | 226 | }; |
|---|
| | 227 | |
|---|
| | 228 | if (@!) { |
|---|
| | 229 | logger("Error running $service: @!"); |
|---|
| | 230 | return "# Error running $service: @!\n"; |
|---|
| | 231 | } |
|---|
| | 232 | return @lines; |
|---|
| | 233 | } |
|---|
| | 234 | |
|---|
| | 235 | # Called from the plugin child before exec. |
|---|
| | 236 | sub setup_environment { |
|---|
| | 237 | my $service = shift or die "internal error: service required"; |
|---|
| | 238 | POSIX::setsid(); |
|---|
| | 239 | # Setting environment |
|---|
| | 240 | $sconf{$service}{user} = get_var (\%sconf, $service, 'user'); |
|---|
| | 241 | $sconf{$service}{group} = get_var (\%sconf, $service, 'group'); |
|---|
| | 242 | $sconf{$service}{command} = get_var (\%sconf, $service, 'command'); |
|---|
| | 243 | get_var (\%sconf, $service, 'env', \%{$sconf{$service}{env}}); |
|---|
| | 244 | |
|---|
| | 245 | if ($< == 0) # If root... |
|---|
| | 246 | { |
|---|
| | 247 | # Giving up gid egid uid euid |
|---|
| | 248 | my $u = (defined $sconf{$service}{'user'}? |
|---|
| | 249 | $sconf{$service}{'user'}: |
|---|
| | 250 | $defuser); |
|---|
| | 251 | my $g = $defgroup; |
|---|
| | 252 | my $gs = "$g $g" . |
|---|
| | 253 | (defined $sconf{$service}{'group'} ? |
|---|
| | 254 | " $sconf{$service}{group}" : ""); |
|---|
| | 255 | |
|---|
| | 256 | debug ("Want to run as euid/egid $u/$g\n"); |
|---|
| | 257 | |
|---|
| | 258 | $( = $g unless $g == 0; |
|---|
| | 259 | $) = $gs unless $g == 0; |
|---|
| | 260 | $< = $u unless $u == 0; |
|---|
| | 261 | $> = $u unless $u == 0; |
|---|
| | 262 | |
|---|
| | 263 | if ($> != $u or $g != (split (' ', $)))[0]) { |
|---|
| | 264 | # net_write ("# Can't drop privileges. Bailing out. (wanted uid=", |
|---|
| | 265 | # ($sconf{$service}{'user'} || $defuser), " gid=\"", |
|---|
| | 266 | # $gs, "\"($g), got uid=$> gid=\"$)\"(", |
|---|
| | 267 | # (split (' ', $)))[0], ").\n"); |
|---|
| | 268 | logger ("Plugin \"$service\" Can't drop privileges. ". |
|---|
| | 269 | "Bailing out. (wanted uid=". |
|---|
| | 270 | ($sconf{$service}{'user'} || $defuser). " gid=\"". |
|---|
| | 271 | $gs. "\"($g), got uid=$> gid=\"$)\"(". |
|---|
| | 272 | (split (' ', $)))[0]. ")."); |
|---|
| | 273 | exit 1; |
|---|
| | 274 | } |
|---|
| | 275 | } |
|---|
| | 276 | debug ("Running as uid/gid/euid/egid $</$(/$>/$)\n"); |
|---|
| | 277 | if (!check_perms ("$servicedir/$service")) { |
|---|
| | 278 | net_write ("# Error: unsafe permissions. Bailing out."); |
|---|
| | 279 | logger ("Error: unsafe permissions. Bailing out."); |
|---|
| | 280 | exit 2; |
|---|
| | 281 | } |
|---|
| | 282 | |
|---|
| | 283 | # Setting environment... |
|---|
| | 284 | if (defined $sconf{$service}{'env'}) { |
|---|
| | 285 | foreach my $key (keys %{$sconf{$service}{'env'}}) { |
|---|
| | 286 | debug ("Setting environment $key=$sconf{$service}{env}{$key}\n"); |
|---|
| | 287 | $ENV{"$key"} = $sconf{$service}{'env'}{$key}; |
|---|
| | 288 | } |
|---|
| | 289 | } |
|---|