Skip to content

Commit

Permalink
process: address remarks, add some refactoring for unittesting (to be…
Browse files Browse the repository at this point in the history
… squashed)
  • Loading branch information
stdweird committed Nov 30, 2017
1 parent c134096 commit c2b242b
Show file tree
Hide file tree
Showing 2 changed files with 329 additions and 47 deletions.
106 changes: 61 additions & 45 deletions src/main/perl/Process.pm
Original file line number Diff line number Diff line change
Expand Up @@ -177,103 +177,114 @@ C<<<msg> command: <COMMAND>[ <postmsg>]>>.
=cut

# if mode is user, return uid and primary gid of
# the user in the user attribute ($self->{user}).
# if mode is group, return gid and undef of
# the group in the group attribute ($self->{group}).
sub _get_uid_gid
{
my ($self, $mode) = @_;

my @res;
my $what = $self->{$mode};
if (defined($what)) {
my $is_id = $what =~ m/^\d+$/ ? 1 : 0;
my $target = $self->{$mode};
if (defined($target)) {
my $is_id = $target =~ m/^\d+$/ ? 1 : 0;
my $is_user = $mode eq 'user' ? 1 : 0;
# This is ugly
# But you cannot reference the builtin functions,
# maybe by using simple wrapper like my $fn = sub { builtin(@_) } (eg sub {getpwname($_[0])})
# But the getpw / getgr functions are safe to use (they do not die, just return undef)
# But the getpw / getgr functions are safe to use (they do not die, just return empty list)
# so no _safe_eval and a funcref required
# For the is_id case, strictly not needed to check details, since setuid can change to non-known user
# But we don't allow that here.
my @info = $is_id ?
($is_user ? getpwuid($what) : getgrgid($what)) :
($is_user ? getpwnam($what) : getgrnam($what));
($is_user ? getpwuid($target) : getgrgid($target)) :
($is_user ? getpwnam($target) : getgrnam($target));

# What do we need from info: the IDs, and for users, also the primary groups
if (@info) {
# pwnam/pwuid: uid=2 and gid=3
# grnam/uid: gid=2
@res = ($info[2], $is_user ? $info[3] : undef);
} else {
$self->error("No such $mode $what (is user $is_user; is id $is_id)");
$self->error("No such $mode $target (is user $is_user; is id $is_id)");
}
}

return @res;
}

# minimal functions for mocking
sub _uid {return $UID;};
sub _euid {return $EUID;};
sub _gid {return "$GID";}; # as string
sub _egid {return "$EGID";}; # as string
sub _set_euid {$EUID = $_[0];};
sub _set_egid {$EGID = $_[0];};

# set euid or egid, with verification and reporting
# return 1 or 0
# report error on failure
sub _set_uid_gid
{
my ($self, $target, $set, $get, $name, $suff, $action) = @_;

return 1 if ! defined($target);

my $value = &$get;
my $msg = "$name from $value to $suff";
# stringification to handle numeric case
if ("$value" eq "$target") {
$self->verbose(ucfirst($action)." $msg: no changes required")
} else {
&$set($target);
$value = &$get;
if ("$value" eq "$target") {
$self->verbose(ucfirst($action)." $msg")
} else {
$self->error("Something went wrong $action $msg: new $name $value, reason $!");
return 0;
}
}
return 1;
};


# set euid/egid if user and/or group was set
# returns 1 on success.
# on failure, report error and return undef
sub _set_eff_user_group
{
my ($self, $orig) = @_;

my ($uid, $gid, $pri_gid, $gid_full, $oper);
my ($uid, $gid, $pri_gid, $gid_full, $action);

my $restore = defined($orig) ? 1 : 0;

if ($restore) {
$oper = "restoring";
$action = "restoring";
($uid, $gid) = @$orig;
# We assume the original gid is the original list of groups
$gid_full = "$gid";
} else {
$oper = "changing";
$action = "changing";

# has to be array context
($uid, $pri_gid) = $self->_get_uid_gid('user');
($gid) = $self->_get_uid_gid('group');
($gid, undef) = $self->_get_uid_gid('group');
# use user primary group when no group specified
$gid = $pri_gid if defined $uid && ! defined $gid;
# This is how you set the GID to only the GID (i.e. no other groups)
$gid_full = "$gid $gid" if defined $gid;
}

# return 1 or 0
my $set_user = sub {
return 1 if ! defined $uid;

my $msg = "EUID from $EUID to $uid with UID $UID";
if ($EUID == $uid) {
$self->verbose(ucfirst($oper)." $msg: no changes required")
} else {
$EUID = $uid;
if ($EUID == $uid) {
$self->verbose(ucfirst($oper)." $msg")
} else {
$self->error("Something went wrong $oper $msg: $!");
return 0;
}
}
return 1;
return $self->_set_uid_gid($uid, \&_set_euid, \&_euid, 'EUID', "$uid with UID "._uid, $action);
};

# return 1 or 0
my $set_group = sub {
return 1 if ! defined($gid);

my $msg = "EGID from $EGID to $gid with GID $GID";
if ($EGID eq $gid_full) {
$self->verbose(ucfirst($oper)." $msg: no changes required")
} else {
$EGID = $gid_full;
if ($EGID eq $gid_full) {
$self->verbose(ucfirst($oper)." $msg")
} else {
$self->error("Something went wrong $oper $msg: new EGID $EGID, reason $!");
return 0;
}
}
return 1;
return $self->_set_uid_gid($gid_full, \&_set_egid, \&_egid, 'EGID', "$gid with GID "._gid, $action);
};

my $res = 0;
Expand Down Expand Up @@ -306,10 +317,15 @@ sub _LC_Process
$? = 0;
$res = $noaction_value;
} else {

my $current_user_group = !(defined $self->{user} or defined $self->{group});

# The original GID (as list of groups)
my $orig_user_group = [$UID, "$GID"];
# Only relevant if curent_user_group is false
my $orig_user_group;
$orig_user_group = [_uid, _gid] if !$current_user_group;

if ($self->_set_eff_user_group()) {
if ($current_user_group or $self->_set_eff_user_group()) {
my $funcref = $LC_PROCESS_DISPATCH{$function};
if (defined($funcref)) {
$res = $funcref->(@$args);
Expand All @@ -320,7 +336,7 @@ sub _LC_Process
}

# always try to restore
$self->_set_eff_user_group($orig_user_group);
$self->_set_eff_user_group($orig_user_group) if !$current_user_group;
}

return $res;
Expand Down
Loading

0 comments on commit c2b242b

Please sign in to comment.