Skip to content

Commit

Permalink
Run changepassword in a PTY
Browse files Browse the repository at this point in the history
  • Loading branch information
jcameron committed Jun 12, 2023
1 parent 4dd1370 commit 5394d9f
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 11 deletions.
11 changes: 5 additions & 6 deletions index.cgi
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ if (&virtual_server::master_admin() && &needs_mailman_list()) {
print &ui_form_end();
}

# Show a form to create a superuser
# Show form to create a superuser
if (&virtual_server::master_admin() && &get_mailman_version() >= 3) {
print &ui_hr();
my @supes = &list_django_superusers();
Expand All @@ -159,21 +159,20 @@ if (&virtual_server::master_admin() && &get_mailman_version() >= 3) {
}

print &ui_form_start("super.cgi");
print &ui_table_start($text{'index_suheader'}, undef, 2);
print &ui_table_start($text{'index_suheader'}, undef, 4);

print &ui_table_row($text{'index_suser'},
&ui_textbox("suser", undef, 30));

print &ui_table_row($text{'index_semail'},
&ui_textbox("semail", undef, 30));

print &ui_table_row($text{'index_spass'},
&ui_textbox("spass", undef, 30));

print &ui_table_row($text{'index_semail'},
&ui_textbox("semail", undef, 60), 3);

print &ui_table_end();
print &ui_submit($text{'create'});
print &ui_form_end();

}

# Form to search for members
Expand Down
3 changes: 2 additions & 1 deletion lang/en
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ index_fixurls=Fix Mailman URLs
index_fixurlsdesc=Virtualmin has detected that $1 virtual servers have mailman redirect URLs that do not match the Virtualmin base URL like <tt>$2</tt>. Click this button to correct them.
index_sugot=Mailman administrator logins $1 already exist and can be used to manage lists via the web UI. But you can use the form below to create a new administrator or update an existing one.
index_suneed=No Mailman administrator logins have been created yet. Use the form below to create one, so you can manage lists via the web UI.
index_suheader=New Mailman administrator details
index_suheader=Create Mailman administrator
index_suser=Administrator login
index_semail=Administrator email address
index_spass=Administrator password
Expand Down Expand Up @@ -174,5 +174,6 @@ super_esuser=Missing or invalid-looking username
super_esemail=Missing or invalid-looking email address
super_espass=Missing password
super_ewcmd=Mailman web command not installed!
super_epasslen=Password must be at least $1 characters long

__norefs=1
179 changes: 179 additions & 0 deletions pty.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#!/usr/local/bin/perl
# Run some command in a PTY. Used for Xen console.
# Only works on Linux!

use POSIX;

# Delay before each input line
if ($ARGV[0] eq "--sleep") {
shift(@ARGV);
$sleeptime = shift(@ARGV);
}

($ptyfh, $ttyfh, $pty, $tty) = &get_new_pty();
$ptyfh || die "Failed to create new PTY";
$pid = fork();
if (!$pid) {
&close_controlling_pty();
setsid();

if (!$ttyfh) {
# Needs to be opened, as get_new_pty on linux cannot do
# this so soon
$ttyfh = "TTY";
open($ttyfh, "+<$tty") || die "Failed to open $tty : $!";
}

&open_controlling_pty($ptyfh, $ttyfh, $pty, $tty);
close(STDIN); close(STDOUT); close(STDERR);
open(STDIN, "<$tty") || die "Failed to reset STDIN : $!";
open(STDOUT, ">&$ttyfh") || die "Failed to reset STDOUT : $!";
open(STDERR, ">&STDOUT");
close($ptyfh);
exec(@ARGV);
die "Exec failed : $!\n";
}
print "PTY PID: $pid\n";
$| = 1;
select($ptyfh); $| = 1; select(STDOUT);
&forward_connection_sockets(STDIN, STDOUT, $ptyfh, $ptyfh);
waitpid($pid, 0);
exit($? / 256);

sub forward_connection_sockets
{
my ($in1, $out1, $in2, $out2) = @_;
my ($closed1, $closed2);
while(1) {
my $rmask = undef;
vec($rmask, fileno($in1), 1) = 1 if (!$closed1);
vec($rmask, fileno($in2), 1) = 1 if (!$closed2);
my $sel = select($rmask, undef, undef, 10);
if (vec($rmask, fileno($in1), 1)) {
# Got something from stdin
my $buf;
$ok = sysread($in1, $buf, 1024);
if ($ok <= 0) {
$closed1 = 1;
}
else {
if ($sleeptime) {
# Split into lines, sleep between each one
sleep($sleeptime);
my @lines = split(/\n/, $buf);
foreach my $l (@lines) {
sleep($sleeptime);
syswrite($out2, $l."\n", length($l)+1)
|| last;
}
}
else {
syswrite($out2, $buf, length($buf)) || last;
}
}
}
if (vec($rmask, fileno($in2), 1)) {
# Got something from command
my $buf;
$ok = sysread($in2, $buf, 1024);
if ($ok <= 0) {
$closed2 = 1;
}
else {
syswrite($out1, $buf, length($buf)) || last;
}
}
last if ($closed1 && $closed2);
}
}



# get_new_pty()
# Returns the filehandles and names for a pty and tty
sub get_new_pty
{
if (-r "/dev/ptmx" && -d "/dev/pts" && open(PTMX, "+>/dev/ptmx")) {
# Can use new-style PTY number allocation device
my $unl;
my $ptn;

# ioctl to unlock the PTY (TIOCSPTLCK)
$unl = pack("i", 0);
ioctl(PTMX, 0x40045431, $unl) || die "Unlock ioctl failed : $!";
$unl = unpack("i", $unl);

# ioctl to request a TTY (TIOCGPTN)
ioctl(PTMX, 0x80045430, $ptn) || die "PTY ioctl failed : $!";
$ptn = unpack("i", $ptn);

my $tty = "/dev/pts/$ptn";
return (*PTMX, undef, $tty, $tty);
}
else {
# Have to search manually through pty files!
my @ptys;
my $devstyle;
if (-d "/dev/pty") {
opendir(DEV, "/dev/pty");
@ptys = map { "/dev/pty/$_" } readdir(DEV);
closedir(DEV);
$devstyle = 1;
}
else {
opendir(DEV, "/dev");
@ptys = map { "/dev/$_" } (grep { /^pty/ } readdir(DEV));
closedir(DEV);
$devstyle = 0;
}
my ($pty, $tty);
foreach $pty (@ptys) {
open(PTY, "+>$pty") || next;
my $tty = $pty;
if ($devstyle == 0) {
$tty =~ s/pty/tty/;
}
else {
$tty =~ s/m(\d+)$/s$1/;
}
my $old = select(PTY); $| = 1; select($old);
if ($< == 0) {
# Don't need to open the TTY file here for root,
# as it will be opened later after the controlling
# TTY has been released.
return (*PTY, undef, $pty, $tty);
}
else {
# Must open now ..
open(TTY, "+>$tty");
select(TTY); $| = 1; select($old);
return (*PTY, *TTY, $pty, $tty);
}
}
return ();
}
}

# close_controlling_pty()
# Disconnects this process from it's controlling PTY, if connected
sub close_controlling_pty
{
if (open(DEVTTY, "/dev/tty")) {
# Special ioctl to disconnect (TIOCNOTTY)
ioctl(DEVTTY, 0x5422, 0);
close(DEVTTY);
}
}

# open_controlling_pty(ptyfh, ttyfh, ptyfile, ttyfile)
# Makes a PTY returned from get_new_pty the controlling TTY (/dev/tty) for
# this process.
sub open_controlling_pty
{
my ($ptyfh, $ttyfh, $pty, $tty) = @_;

# Call special ioctl to attach /dev/tty to this new tty (TIOCSCTTY)
ioctl($ttyfh, 0x540e, 0);
}


11 changes: 7 additions & 4 deletions virtualmin-mailman-lib.pl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use warnings;
our (%text, %config, %gconfig);
our $module_config_directory;
our $module_root_directory;
our $module_name;

BEGIN { push(@INC, ".."); };
Expand Down Expand Up @@ -891,15 +892,17 @@ sub create_django_superuser
sub set_django_superuser_pass
{
my ($user, $pass) = @_;
length($pass) >= 8 || return &text('super_epasslen', 8);
my $wcmd = &get_mailman_web_cmd();
return $text{'super_ewcmd'} if (!$wcmd);
&foreign_require("proc");
my ($fh, $pid) = &proc::pty_process_exec(
"$wcmd changepassword ".quotemeta($user));
my $temp = &transname();
open(my $fh, ">$temp");
print $fh $pass,"\n";
print $fh $pass,"\n";
close($fh);
return undef;
my $perl = &get_perl_path();
my $out = &backquote_logged("$perl $module_root_directory/pty.pl --sleep 1 $wcmd changepassword ".quotemeta($user)." <$temp 2>&1");
return $? ? $out : undef;
}

1;

0 comments on commit 5394d9f

Please sign in to comment.