Remove dependency on IO::Pty for calling sudo

https://github.com/webmin/webmin/issues/2587
This commit is contained in:
Jamie Cameron
2025-11-18 21:04:48 -08:00
parent ef05252413
commit 0342f06fc8

View File

@@ -227,15 +227,7 @@ if ($rand1 eq $rand2) {
# Check if we can call sudo # Check if we can call sudo
if ($config{'sudo'} && &has_command("sudo")) { if ($config{'sudo'} && &has_command("sudo")) {
eval "use IO::Pty"; $use_sudo = 1;
if (!$@) {
$use_sudo = 1;
}
else {
push(@startup_msg,
"Perl module IO::Pty needed for calling sudo is not ".
"installed : $@");
}
} }
# init days and months for http_date # init days and months for http_date
@@ -6042,7 +6034,7 @@ sub check_sudo_permissions
{ {
local ($user, $pass) = @_; local ($user, $pass) = @_;
# First try the pipes # First try the cache stored by the main process
if ($PASSINw) { if ($PASSINw) {
print DEBUG "check_sudo_permissions: querying cache for $user\n"; print DEBUG "check_sudo_permissions: querying cache for $user\n";
print $PASSINw "readsudo $user\n"; print $PASSINw "readsudo $user\n";
@@ -6054,27 +6046,18 @@ if ($PASSINw) {
} }
} }
local $ptyfh = new IO::Pty; # Setup pipes for communication with the sudo sub-process
print DEBUG "check_sudo_permissions: ptyfh=$ptyfh\n"; pipe(SUDOINr, SUDOINw);
if (!$ptyfh) { pipe(SUDOOUTr, SUDOOUTw);
&log_error("Failed to create new PTY with IO::Pty"); print DEBUG "check_sudo_permissions\n";
return 0;
} my @uinfo = getpwnam($user);
local @uinfo = getpwnam($user);
if (!@uinfo) { if (!@uinfo) {
&log_error("Unix user $user does not exist for sudo"); &log_error("Unix user $user does not exist for sudo");
return 0; return 0;
} }
# Execute sudo in a sub-process, via a pty my $pid = fork();
local $ttyfh = $ptyfh->slave();
print DEBUG "check_sudo_permissions: ttyfh=$ttyfh\n";
local $tty = $ptyfh->ttyname();
print DEBUG "check_sudo_permissions: tty=$tty\n";
chown($uinfo[2], $uinfo[3], $tty);
pipe(SUDOr, SUDOw);
print DEBUG "check_sudo_permissions: about to fork..\n";
local $pid = fork();
print DEBUG "check_sudo_permissions: fork=$pid pid=$$\n"; print DEBUG "check_sudo_permissions: fork=$pid pid=$$\n";
if ($pid < 0) { if ($pid < 0) {
&log_error("fork for sudo failed : $!"); &log_error("fork for sudo failed : $!");
@@ -6089,40 +6072,37 @@ if (!$pid) {
$ENV{'USER'} = $ENV{'LOGNAME'} = $user; $ENV{'USER'} = $ENV{'LOGNAME'} = $user;
$ENV{'HOME'} = $uinfo[7]; $ENV{'HOME'} = $uinfo[7];
$ptyfh->make_slave_controlling_terminal();
close(STDIN); close(STDOUT); close(STDERR); close(STDIN); close(STDOUT); close(STDERR);
untie(*STDIN); untie(*STDOUT); untie(*STDERR); untie(*STDIN); untie(*STDOUT); untie(*STDERR);
close($PASSINw); close($PASSOUTr); close(SUDOINw); close(SUDOOUTr);
close(SUDOw);
close(SOCK); close(SOCK);
close(MAIN); close(MAIN);
open(STDIN, "<&SUDOr"); open(STDIN, "<&SUDOINr");
open(STDOUT, ">$tty"); open(STDOUT, ">&SUDOOUTw");
open(STDERR, ">&STDOUT"); open(STDERR, ">&STDOUT");
close($ptyfh);
exec("sudo -l -S"); exec("sudo -l -S");
print "Exec failed : $!\n"; print "Exec failed : $!\n";
exit 1; exit 1;
} }
print DEBUG "check_sudo_permissions: pid=$pid\n"; print DEBUG "check_sudo_permissions: pid=$pid\n";
close(SUDOr); close(SUDOINr);
$ptyfh->close_slave(); close(SUDOOUTw);
# Send password, and get back response # Send password, and get back response
local $oldfh = select(SUDOw); my $oldfh = select(SUDOINw);
$| = 1; $| = 1;
select($oldfh); select($oldfh);
print DEBUG "check_sudo_permissions: about to send pass\n"; print DEBUG "check_sudo_permissions: about to send pass\n";
local $SIG{'PIPE'} = 'ignore'; # Sometimes sudo doesn't ask for a password local $SIG{'PIPE'} = 'ignore'; # Sometimes sudo doesn't ask for a password
print SUDOw $pass,"\n"; print SUDOINw $pass,"\n";
print DEBUG "check_sudo_permissions: sent pass=$pass\n"; print DEBUG "check_sudo_permissions: sent pass=$pass\n";
close(SUDOw); close(SUDOINw);
local $out; my $out;
while(<$ptyfh>) { while(<SUDOOUTr>) {
print DEBUG "check_sudo_permissions: got $_"; print DEBUG "check_sudo_permissions: got $_";
$out .= $_; $out .= $_;
} }
close($ptyfh); close(SUDOOUTr);
kill('KILL', $pid); kill('KILL', $pid);
waitpid($pid, 0); waitpid($pid, 0);
local ($ok) = ($out =~ /\(ALL\)\s+ALL|\(ALL\)\s+NOPASSWD:\s+ALL|\(ALL\s*:\s*ALL\)\s+ALL|\(ALL\s*:\s*ALL\)\s+NOPASSWD:\s+ALL/ ? 1 : 0); local ($ok) = ($out =~ /\(ALL\)\s+ALL|\(ALL\)\s+NOPASSWD:\s+ALL|\(ALL\s*:\s*ALL\)\s+ALL|\(ALL\s*:\s*ALL\)\s+NOPASSWD:\s+ALL/ ? 1 : 0);