3

I would like to be able to run certain Perl scripts on my system as root, even though the "user" calling them is not running as root.

For each script I can write a C wrapper, setting setuid root for that wrapper; the wrapper would change the UID to 0 and then call the Perl script, which itself would not have the setuid bit set. This avoids unfortunate impediments while attempting to run setuid root scripts.

But I don't want to write a C wrapper for each script. I just want one C wrapper to do the job for the whole system. I also don't want just any script to be able to use this C wrapper; the C wrapper itself should be able to check some specific characteristic of the Perl script to see whether changing the UID to root is acceptable.

I don't see any other Stack Overflow question yet which addresses this issue.

I know the risks, I own the system, and I don't want something arbitrarily babysitting me by standing in my way.

Bill Evans at Mariposa
  • 3,590
  • 1
  • 18
  • 22

3 Answers3

4

What you are trying to do is very hard, even by experts. The setuid wrapper that used to come with perl no longer exists because of that, and because it's no longer needed these days. Linux and I presume other modern unix systems support setuid scripts, so you don't need highly-fragile and complex wrappers.

If you really need a wrapper, don't re-invent the wheel; just use sudo!

ikegami
  • 367,544
  • 15
  • 269
  • 518
1

So use a single wrapper, take the perl script to execute as an argument, and have the C wrapper compare the length of the script and a SHA-3 or SHA-2 hash of the script contents to expected values.

ysth
  • 96,171
  • 6
  • 121
  • 214
  • This proposed solution is good, in that it requires something to be done by root to bless the proposed script so that the C wrapper knows to execute it as root. But I'd like the blessing (still doable by root only) to be somehow a part of the Perl script itself, so that a separate list of hashes need not be maintained every time a script is added, removed, or modified. – Bill Evans at Mariposa Feb 06 '17 at 01:00
  • Then you want the perl script to turn around and invoke the wrapper to invoke itself if it isn't already root. But then any perl script can do that, and you have essentially no security at all. – ysth Feb 06 '17 at 01:08
  • The way I see it, the command-line user would need no knowledge that any sort of C wrapper was involved. He would just run the Perl script, and yes, it would invoke the wrapper. The trick is to tag the Perl script somehow (in a way that only root could do) so that the C wrapper would know that the Perl script is eligible to run as UID 0. – Bill Evans at Mariposa Feb 06 '17 at 01:28
  • if you can make the effort to tag the perl script in some way, you can make the effort to run something to recompile the wrapper with an updated hash – ysth Feb 06 '17 at 02:19
  • @BillEvansatMariposa But to "_tag the Perl script_" the root has to take action when a script is added, otherwise "_any script_" can pass. So why not identify the script properly? Updating can be automated too, and perhaps also triggered by the script itself. Or just have them be "registered" by root in a flat file, but that's very rudimentary. – zdim Feb 06 '17 at 03:01
  • 1
    Don't forget to avoid passing the script name to `perl`, using the virtual path to the file handle used to read the file for your checks instead (e.g. `/proc/####/fd/####` on Linux). – ikegami Feb 06 '17 at 03:25
  • @ysth the trick is to cut long-term maintenance costs to the absolute minimum. More effort could be made (and even automated), but I think simplicity might be a virtue here. – Bill Evans at Mariposa Feb 06 '17 at 03:34
  • @zdim Indeed root will have to do something, but I'm hoping to avoid adding something to something else; I'm looking for just a way to change the Perl script itself, something that only root can do. Simplicity aids long-term maintenance. – Bill Evans at Mariposa Feb 06 '17 at 03:37
  • @ikegami Good advice. But it won't work easily on FreeBSD. (One of the few regrets of leaving Linux behind is its splendiferous use of /proc.) – Bill Evans at Mariposa Feb 06 '17 at 03:38
  • If FreeBSD doesn't have something similar, then you might not be able to write a safe wrapper. What's `$0` in a setuid *script* in FreeBSD? – ikegami Feb 06 '17 at 04:12
1

After some brainstorming:

All the requirements can be met through the following steps. First we show steps which are done just once.

Step one. Since each script which should be run as root has to be marked by user root somehow (otherwise, just any user could do this), the system administrator takes advantage of his privileged status to choose a user ID which will never actually be assigned to anyone. In this example, we use user ID 9999.

Step two. Compile a particular C wrapper and let the object code run suid root. The source code for that wrapper can be found here.

Then, the following two steps are done once for each Perl script to be run as root.

Step one. Begin each Perl script with the following code.

if($>)
{
  exec { "/path-to-wrapper-program" }
       ( "/path-to-wrapper-program",$0,@ARGV);
}

Step two. As root (obviously), change the owner of the Perl script to user 9999. That's it. No updating of databases or text files. All requirements for running a Perl script as root reside with the script itself.

Comment on step one: I actually place the above Perl snippet after these lines:

use strict;
use warnings FATAL=>"all";

... but suit yourself.

Bill Evans at Mariposa
  • 3,590
  • 1
  • 18
  • 22