Viewing File: /usr/local/cpanel/whostmgr/docroot/cgi/imunify/handlers/Imunify/Wrapper.pm

package Imunify::Wrapper;

use strict;
use warnings FATAL => 'all';
use Encode;
use Data::Dumper;
use File::HomeDir;
use File::Basename;
use IO::Handle;

use Imunify::Exception;
use Imunify::Render;
use Imunify::Utils;
use Imunify::Config;
use Imunify::Acls;
use Cpanel::JSON;

#use CGI::Carp qw(fatalsToBrowser); # uncomment to debug 500 error

# When the WHM caller is a reseller (not root admin), drop privileges
# to their UID so the agent's per-user scope filter applies (same path
# used by cPanel end-users today). Returning the guard from the
# entry-points keeps the priv-drop in scope for the lifetime of the
# Imunify::Utils::execute call; cPanel restores root automatically when
# the guard goes out of scope.
#
# Imunify::Wrapper::execute is shared between the WHM CGI (sendRequest.cgi,
# runs as root with init_acls()) and the cPanel end-user CGI
# (imunify.live.pl, dispatched by cpsrvd as the cPanel user, no ACL init).
# The end-user case is already non-root and already routes onto the
# non-root socket via the existing flow — no priv-drop needed (and would
# fail anyway because setuid requires CAP_SETUID). Short-circuit it.
sub _maybe_drop_privileges {
    return undef if $> != 0;                       # already non-root (cPanel end-user)
    return undef if Imunify::Acls::isEffectiveAdmin();   # admin or hoster opt-out
    my $remote_user = $ENV{REMOTE_USER} // '';
    die Imunify::Exception->new('Permission denied: empty REMOTE_USER')
        unless length $remote_user;
    my $guard;
    eval {
        require Cpanel::AccessIds::ReducedPrivileges;
        $guard = Cpanel::AccessIds::ReducedPrivileges->new($remote_user);
    };
    die Imunify::Exception->new('Permission denied: priv-drop failed')
        if $@ || !$guard;
    return $guard;
}

sub execute {
    my ($request, $isWHM) = @_;
    my %data = ();

    $data{'command'} = \@{$request->{'method'}};
    $data{'params'} = $request->{'params'};
    $data{'params'}{'remote_addr'} = $ENV{REMOTE_ADDR};

    my $priv_guard = _maybe_drop_privileges();
    return Imunify::Utils::execute('execute', \%data, $isWHM);
}

sub request {
    my ($request, $isWHM) = @_;
    my $response = execute($request, $isWHM);
    Imunify::Render::JSONHeader(Imunify::Render->HTTP_STATUS_OK);
    print $response;
}

sub imunfyEmailRequest {
    my ($request, $isWHM) = @_;
    my %data = ();
    $data{'username'} = $ENV{REMOTE_USER};
    $data{'command'} = \@{$request->{'method'}};
    $data{'params'} = $request->{'params'};
    my $priv_guard = _maybe_drop_privileges();
    my $response = Imunify::Utils::execute('imunifyEmail', \%data, $isWHM);
    Imunify::Render::JSONHeader(Imunify::Render->HTTP_STATUS_OK);
    print $response;
}

sub upload {
    my ($cgi, $isWHM) = @_;
    my ($tmpPath);
    my %data = (
        'files' => {},
    );

    foreach my $file ($cgi->param('files[]')) {
        $tmpPath = $cgi->tmpFileName($file);
        $data{'files'}{$tmpPath} = "$file";
    }

    # Bug-report file storage only — moves CGI temp files into
    # /var/imunify360/uploads (root-only, mode 0700) and chowns root:root.
    # No agent socket involved. Priv-drop here would break the storage
    # write and close no leak.
    return Imunify::Utils::execute('uploadFile', \%data, $isWHM);
}

1;
Back to Directory File Manager