From mboxrd@z Thu Jan  1 00:00:00 1970
From: Michael Tremer <michael.tremer@ipfire.org>
To: development@lists.ipfire.org
Subject: Re: [PATCH 2/2] services.cgi: Fix status/actions on services with
 name != addon name
Date: Tue, 04 Oct 2022 13:49:05 +0100
Message-ID: <51F7DC88-7593-4F97-925D-472EEF48B514@ipfire.org>
In-Reply-To: <50e62161be8c54ca80c2cb711be1a2686545b281.camel@sicho.home>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="===============0982853370413612232=="
List-Id: <development.lists.ipfire.org>

--===============0982853370413612232==
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable

Hello,

> On 4 Oct 2022, at 11:33, Robin Roevens <robin.roevens(a)disroot.org> wrote:
>=20
> Hi
>=20
> Michael Tremer schreef op di 04-10-2022 om 10:51 [+0100]:
>> Hello,
>>=20
>> I am going to start with the second patch, because that is going to
>> be shorter :)
>>=20
>>> On 3 Oct 2022, at 16:27, Robin Roevens <robin.roevens(a)disroot.org>
>>> wrote:
>>>=20
>>> * addonctrl's new functionality to control explicit addon services
>>> was
>>> implemented.
>>> * Change 'Addon' column header to 'Addon Service' to be clear that
>>> it's not addons but services listed here.
>>> * Services not matching the name of the addon now display the addon
>>> name between parentheses, so the user knows where the service
>>> comes
>>> from.
>>> * When no valid runlevel symlink is found by addonctrl for a
>>> service,
>>> the 'enable on boot' checkbox is replaced by a small exclamation
>>> point
>>> with alt-text "No valid runlevel symlink was found for the
>>> initscript of
>>> this service." to inform user why a service can't be enabled.
>>> * Added German and Dutch translation for above message.
>>>=20
>>> Fixes: Bug#12935
>>> Signed-off-by: Robin Roevens <robin.roevens(a)disroot.org>
>>> ---
>>> html/cgi-bin/services.cgi | 103 +++++++++++++++--------------------
>>> ---
>>> langs/de/cgi-bin/de.pl  |  1 +
>>> langs/en/cgi-bin/en.pl  |  1 +
>>> langs/nl/cgi-bin/nl.pl  |  1 +
>>> 4 files changed, 42 insertions(+), 64 deletions(-)
>>>=20
>>> diff --git a/html/cgi-bin/services.cgi b/html/cgi-bin/services.cgi
>>> index 29926ecc3..e91da884b 100644
>>> --- a/html/cgi-bin/services.cgi
>>> +++ b/html/cgi-bin/services.cgi
>>> @@ -142,14 +142,14 @@ END
>>> my $paramstr=3D$ENV{QUERY_STRING};
>>> my @param=3Dsplit(/!/, $paramstr);
>>> if ($param[1] ne ''){
>>> -  &General::system("/usr/local/bin/addonctrl",
>>> "$param[0]", "$param[1]");
>>> +  &General::system("/usr/local/bin/addonctrl",
>>> "$param[0]", "$param[1]", "$param[2]");
>>> }
>>=20
>> Although this has been like this before, I am strongly against
>> passing anything from the request to the setuid binary.
>>=20
>> This will open us up for any shell command injections and so on. I
>> would prefer to check the second argument to be at least on some kind
>> of list as there are only a few keywords allowed (start, stop,
>> restart, =E2=80=A6) and the other parameters should only contain ASCII
>> letters, numbers and maybe dash and underscore. Nothing else should
>> be passed through here.
>=20
> Good point. I was thinking this regexp: /[^a-zA-Z_0-9\-\.]/ or should I
> not include a '.'? I don't think there currently are any addons or
> initscripts that contain a '.', but it could be in the future?=20

I was thinking about the dot as well, and then thought it would not be requir=
ed to include it since we do not have any services like this, and I do not re=
member any of them using a dot on any other distributions.

The regular expression is slightly incorrect though. You want it to be like t=
his:

/^[A-Za-z0-9\-\_]+$/

The ^ and $ indicate that the entire string must match and not only a part. A=
nd the + after the brackets with the list of allowed characters says that we =
need to have at least one character, but could be more. However, the entire s=
tring must only contain those characters.

-Michael

>=20
> Regards
> Robin
>>=20
>>>=20
>>> print <<END
>>> <div align=3D'center'>
>>> <table width=3D'80%' cellspacing=3D'1' class=3D'tbl'>
>>> <tr>
>>> -  <th align=3D'center'><b>Addon</b></th>
>>> +  <th align=3D'left'><b>Addon $Lang::tr{service}</b></th>
>>> <th align=3D'center'><b>Boot</b></th>
>>> <th align=3D'center'
>>> colspan=3D2><b>$Lang::tr{'action'}</b></th>
>>> <th align=3D'center'><b>$Lang::tr{'status'}</b></th>
>>> @@ -170,33 +170,35 @@ END
>>> foreach my $pak (keys %paklist) {
>>> my %metadata =3D &Pakfire::getmetadata($pak,
>>> "installed");
>>>=20
>>> +  my $service;
>>> +
>>> if ("$metadata{'Services'}") {
>>> -  foreach my $service (split(/ /,
>>> "$metadata{'Services'}")) {
>>> -  push(@addon_services, $service);
>>> -  }
>>> -  }
>>> -  }
>>> +  foreach $service (split(/ /,
>>> "$metadata{'Services'}")) {
>>> +  $lines++;
>>> +  if ($lines % 2) {
>>> +  print "<tr>";
>>> +  $col=3D"bgcolor=3D'$color{'colo
>>> r22'}'";
>>> +  } else {
>>> +  print "<tr>";
>>> +  $col=3D"bgcolor=3D'$color{'colo
>>> r20'}'";
>>> +  }
>>>=20
>>> -  foreach (@addon_services) {
>>> -  $lines++;
>>> -  if ($lines % 2){
>>> -  print "<tr>";
>>> -  $col=3D"bgcolor=3D'$color{'color22'}'";
>>> -  }else{
>>> -  print "<tr>";
>>> -  $col=3D"bgcolor=3D'$color{'color20'}'";
>>> +  # Add addon name to displayname of
>>> service if servicename differs from addon
>>> +  my $displayname =3D ($pak ne
>>> $service) ? "$service ($pak)" : $service;
>>> +  print "<td align=3D'left' $col
>>> width=3D'31%'>$displayname</td> ";
>>> +
>>> +  my $status =3D
>>> isautorun($pak,$service,$col);
>>> +  print "$status ";
>>> +  print "<td align=3D'center' $col
>>> width=3D'8%'><a href=3D'services.cgi?$pak!start!$service'><img
>>> alt=3D'$Lang::tr{'start'}' title=3D'$Lang::tr{'start'}'
>>> src=3D'/images/go-up.png' border=3D'0' /></a></td>";
>>> +  print "<td align=3D'center' $col
>>> width=3D'8%'><a href=3D'services.cgi?$pak!stop!$service'><img
>>> alt=3D'$Lang::tr{'stop'}' title=3D'$Lang::tr{'stop'}' src=3D'/images/go-
>>> down.png' border=3D'0' /></a></td> ";
>>> +  my $status =3D
>>> isrunningaddon($pak,$service,$col);
>>> +  $status =3D~ s/\=1B\[[0-1]\;[0-9]+m//g;
>>> +
>>> +  chomp($status);
>>> +  print "$status";
>>> +  print "</tr>";
>>> +  }
>>> }
>>> -  print "<td align=3D'left' $col width=3D'31%'>$_</td> ";
>>> -  my $status =3D isautorun($_,$col);
>>> -  print "$status ";
>>> -  print "<td align=3D'center' $col width=3D'8%'><a
>>> href=3D'services.cgi?$_!start'><img alt=3D'$Lang::tr{'start'}'
>>> title=3D'$Lang::tr{'start'}' src=3D'/images/go-up.png' border=3D'0'
>>> /></a></td>";
>>> -  print "<td align=3D'center' $col width=3D'8%'><a
>>> href=3D'services.cgi?$_!stop'><img alt=3D'$Lang::tr{'stop'}'
>>> title=3D'$Lang::tr{'stop'}' src=3D'/images/go-down.png' border=3D'0'
>>> /></a></td> ";
>>> -  my $status =3D isrunningaddon($_,$col);
>>> -  $status =3D~ s/\=1B\[[0-1]\;[0-9]+m//g;
>>> -
>>> -  chomp($status);
>>> -  print "$status";
>>> -  print "</tr>";
>>> }
>>>=20
>>> print "</table></div>\n";
>>> @@ -215,51 +217,24 @@ END
>>> }
>>>=20
>>> sub isautorun (@) {
>>> -  my ($cmd, $col) =3D @_;
>>> -
>>> -  # Init directory.
>>> -  my $initdir =3D "/etc/rc.d/rc3.d/";
>>> -
>>> -  my $status =3D "<td align=3D'center' $col></td>";
>>> +  my ($pak, $service, $col) =3D @_;
>>> +  my @testcmd =3D
>>> &General::system_output("/usr/local/bin/addonctrl", "$pak", "boot-
>>> status", "$service");
>>> +  my $testcmd =3D @testcmd[0];
>>> +  my $status =3D "<td align=3D'center' $col><img
>>> alt=3D'$Lang::tr{'service boot setting unavailable'}'
>>> title=3D'$Lang::tr{'service boot setting unavailable'}'
>>> src=3D'/images/dialog-warning.png' border=3D'0' width=3D'16' height=3D'16'
>>> /></td>";
>>>=20
>>> -  # Check if autorun for the given cmd is enabled.
>>> -  if ( &find_init("$cmd", "$initdir") ) {
>>> +  # Check if autorun for the given service is enabled.
>>> +  if ( $testcmd =3D~ /enabled\ on\ boot/ ) {
>>> # Adjust status.
>>> -  $status =3D "<td align=3D'center' $col><a
>>> href=3D'services.cgi?$_!disable'><img alt=3D'$Lang::tr{'deactivate'}'
>>> title=3D'$Lang::tr{'deactivate'}' src=3D'/images/on.gif' border=3D'0'
>>> width=3D'16' height=3D'16' /></a></td>";
>>> -  } else {
>>> +  $status =3D "<td align=3D'center' $col><a
>>> href=3D'services.cgi?$pak!disable!$service'><img
>>> alt=3D'$Lang::tr{'deactivate'}' title=3D'$Lang::tr{'deactivate'}'
>>> src=3D'/images/on.gif' border=3D'0' width=3D'16' height=3D'16'
>>> /></a></td>";
>>> +  } elsif ( $testcmd =3D~ /disabled\ on\ boot/ ) {
>>> # Adjust status.
>>> -  $status =3D "<td align=3D'center' $col><a
>>> href=3D'services.cgi?$_!enable'><img alt=3D'$Lang::tr{'activate'}'
>>> title=3D'$Lang::tr{'activate'}' src=3D'/images/off.gif' border=3D'0'
>>> width=3D'16' height=3D'16' /></a></td>";
>>> +  $status =3D "<td align=3D'center' $col><a
>>> href=3D'services.cgi?$pak!enable!$service'><img
>>> alt=3D'$Lang::tr{'activate'}' title=3D'$Lang::tr{'activate'}'
>>> src=3D'/images/off.gif' border=3D'0' width=3D'16' height=3D'16'
>>> /></a></td>";
>>> }
>>>=20
>>> # Return the status.
>>> return $status;
>>> }
>>>=20
>>> -sub find_init (@) {
>>> -  my ($cmd, $dir) =3D @_;
>>> -
>>> -  # Open given init directory.
>>> -  opendir (INITDIR, "$dir") || die "Cannot opendir $dir: $!";
>>> -
>>> -  # Read-in init files from directory.
>>> -  my @inits =3D readdir(INITDIR);
>>> -
>>> -  # Close directory handle.
>>> -  closedir(INITDIR);
>>> -
>>> -  # Loop through the directory.
>>> -  foreach my $init (@inits) {
>>> -  # Check if the current processed file belongs to
>>> the given command.
>>> -  if ($init =3D~ /S\d+\d+$cmd\z/) {
>>> -  # Found, return "1" - True.
>>> -  return "1";
>>> -  }
>>> -  }
>>> -
>>> -  # Nothing found, return nothing.
>>> -  return;
>>> -}
>>> -
>>> sub isrunning (@) {
>>> my ($cmd, $col) =3D @_;
>>> my $status =3D "<td align=3D'center'
>>> bgcolor=3D'${Header::colourred}'><font
>>> color=3D'white'><b>$Lang::tr{'stopped'}</b></font></td><td
>>> colspan=3D'2' $col></td>";
>>> @@ -313,7 +288,7 @@ sub isrunning (@) {
>>> }
>>>=20
>>> sub isrunningaddon (@) {
>>> -  my ($cmd, $col) =3D @_;
>>> +  my ($pak, $service, $col) =3D @_;
>>>=20
>>> my $status =3D "<td align=3D'center'
>>> bgcolor=3D'${Header::colourred}'><font
>>> color=3D'white'><b>$Lang::tr{'stopped'}</b></font></td><td
>>> colspan=3D'2' $col></td>";
>>> my $pid =3D '';
>>> @@ -321,7 +296,7 @@ sub isrunningaddon (@) {
>>> my $exename;
>>> my @memory;
>>>=20
>>> -  my @testcmd =3D
>>> &General::system_output("/usr/local/bin/addonctrl", "$cmd",
>>> "status");
>>> +  my @testcmd =3D
>>> &General::system_output("/usr/local/bin/addonctrl", "$pak",
>>> "status", "$service");
>>> my $testcmd =3D @testcmd[0];
>>>=20
>>> if ( $testcmd =3D~ /is\ running/ && $testcmd !~ /is\ not\
>>> running/){
>>> diff --git a/langs/de/cgi-bin/de.pl b/langs/de/cgi-bin/de.pl
>>> index 798abcffc..db7d117b0 100644
>>> --- a/langs/de/cgi-bin/de.pl
>>> +++ b/langs/de/cgi-bin/de.pl
>>> @@ -2251,6 +2251,7 @@
>>> 'server string' =3D> 'Server String',
>>> 'service' =3D> 'Dienst',
>>> 'service added' =3D> 'Benutzerdefinierter Netzwerkdienst wurde
>>> hinzugef=C3=BCgt',
>>> +'service boot setting unavailable' =3D> 'F=C3=BCr das Initscript dieses
>>> Dienstes wurde kein g=C3=BCltiger Runlevel-Symlink gefunden.',
>>> 'service name' =3D> 'Name des Dienstes:',
>>> 'service removed' =3D> 'Benutzerdefinierter Netzwerkdienst wurde
>>> entfernt',
>>> 'service updated' =3D> 'Benutzerdefinierter Netzwerkdienst wurde
>>> aktualisiert',
>>> diff --git a/langs/en/cgi-bin/en.pl b/langs/en/cgi-bin/en.pl
>>> index f770e7cd9..60dca5be4 100644
>>> --- a/langs/en/cgi-bin/en.pl
>>> +++ b/langs/en/cgi-bin/en.pl
>>> @@ -2306,6 +2306,7 @@
>>> 'server string' =3D> 'Server String',
>>> 'service' =3D> 'Service',
>>> 'service added' =3D> 'Custom network service added',
>>> +'service boot setting unavailable' =3D> 'No valid runlevel symlink
>>> was found for the initscript of this service.',
>>> 'service name' =3D> 'Service name:',
>>> 'service removed' =3D> 'Custom network service removed',
>>> 'service updated' =3D> 'Custom network service updated',
>>> diff --git a/langs/nl/cgi-bin/nl.pl b/langs/nl/cgi-bin/nl.pl
>>> index 49dabec99..4fd6955cc 100644
>>> --- a/langs/nl/cgi-bin/nl.pl
>>> +++ b/langs/nl/cgi-bin/nl.pl
>>> @@ -1899,6 +1899,7 @@
>>> 'server string' =3D> 'Server String',
>>> 'service' =3D> 'Dienst',
>>> 'service added' =3D> 'Aangepaste netwerkdienst toegevoegd',
>>> +'service boot setting unavailable' =3D> 'Er werd voor het initscript
>>> van deze service geen geldige runlevel symlink gevonden.',
>>> 'service name' =3D> 'Dienstennaam:',
>>> 'service removed' =3D> 'Aangepaste netwerkdienst verwijderd',
>>> 'service updated' =3D> 'Aangepaste netwerkdienst bijgewerkt',
>>> --=20
>>> 2.37.3
>>>=20
>>>=20
>>> --=20
>>> Dit bericht is gescanned op virussen en andere gevaarlijke
>>> inhoud door MailScanner en lijkt schoon te zijn.
>>>=20
>>=20
>>=20
>=20
> --=20
> Dit bericht is gescanned op virussen en andere gevaarlijke
> inhoud door MailScanner en lijkt schoon te zijn.


--===============0982853370413612232==--