How to open Perl file handle to write data via sudo (or as another user) - perl

I'd like to write data to a file, but the file handle should be opened with access permissions for a specific user.
Thus, the following statement:
open (FH, "> $filename") or die "$#\n";
would allow writing to a file as that particular user.
Is there a way to do this within a Perl script, without the entire script being run with sudo -u $username?

There are two established ways. Stackers, you are invited to edit this answer to fill in the drawbacks for each.
Run the program with sudo. The first thing you do in the program is to open the files you need and keep the handles, and then immediately afterwards drop the root privileges. Any further processing must take place with low privileges. The Apache httpd works likes this, it opens the log files as root, but continues running as nobody or similar.
If you don't like that way, run the program normally, and when you need to elevate, create a new process and have it run with a user configured sudo, su -, kdesu/gksu or whatnot. The CPAN client works likes this, it fetches, unpacks, builds and tests a module as a normal user, but calls sudo make install etc. when it's time to install.

An alternative to daxim's suggestions is to have the script owned by the specific user and have the script permissions include the setuid and/or setgid bits.

Related

get user machines current working directory from perl cgi

i am trying to get the current working directory path using Perl
when i execute from ubuntu: $root#ubuntu:/var/test/geek# firefox http:/localhost/test.html, i get /var/cgi-bin as output in perl cgi page instead of /var/test/geek.
used perl code:
my $pwd=cwd();
bla bla
print "<h1> pwd </h1>";
above code gives path of test.pl not users working directory path
Edit: When i run the script alone from the terminal it works fine. for example:
$root#ubuntu:/var/test/geek# /var/cgi-bin/test.pl
i get /var/test/geek. but when i call the script in html page using submit button it gives path of perl script.
Each process has its own working directory that it inherits from its parent when it gets created.
cwd() returns the current process's working directory.
For a CGI script, the browser doesn't pass its working directory to the server as part of the request. To obtain that, you need to have code running on the client system that submits it. That might be an application that the user download, or possibly, but unlikely, some in-browser code, like Javascript / a Java applet (This info is likely hidden from in-browser code for security reasons though).
(The rest assumes Linux, it will likely differ on other operating systems)
The part below assumes that you are looking for the working directory of a user on the server:
In order to get a specific shell for a specific user's working directory, you would need to identify the PID for the shell and get the working directory from the /proc/<pid>/cwd symlink (To read these, the process must belong to the user running the code, or the code must run as root (Which is a bad idea for a CGI script)...). To get the PID of the shell, you likely need to start from the w command output, or its data source, /var/run/utmp. Sys::Utmp might be useful for this... You might then also need to retreive a whole lot of extra info to find all the processes that might have the working directory that you are looking for.
I think you are mixing the web server and the local user. The web server has a working directory when you run the script, and that is the one that cwd() returns.

Is it safe to write a FTP password on a Perl script?

I am writing a Perl script that should connect to a FTP server at some point.
I use Net::FTP module and the login() method to connect, but I am wondering if it is safe to write the password directly in the script. I will chmod the file access to 711, but I am not sure that's enough.
Is there a way to pass the password to the method in a safer way?
If you don't want others on the machine to execute it, chmod 700. There's a number of reasons why it's a good idea for the password to be in a separate file. If you move the password to a config file, that file should be chmod 600.
If you want others on the machine to execute it, chmod 711 isn't going to work. perl must be able to read the script to execute it, so you'd need chmod 755, which means they'll be able to see the password.
The only solution that comes to mind involves file permissions and a set-uid script.
Move the password in a config file.
Create a user to own this application. Let's call it scriptuser. (You could your existing user, but keep in mind the script will be executing as that user.)
chown scriptuser script.pl script.conf.
chmod 600 script.conf
chmod 4755 script.pl
Use script.pl (not perl script.pl) to execute it.
There's no way to securely provide a password in a script, yet allow automatic execution.
You have to store the password somewhere.
It might be better to store it to an external file, so that you do not have to hide away your whole script. For example you might want to have your script reviewed and put to code repository, without the password.

How to create a file in the root dir with perl?

I have an error with perl while trying to CREATE a file called .envfile in the root dir / (only for UNIX). Permission denied, which is understood. But, is there a way to write this file? I need to do it without any modules, just with a built-in functions. I expect for using chmod, but... honestly, have no idea of how to implement it in the same thread SAFELY.
I need this file to write in it my own ENVs for my software (as it is a big project with many dirs and needs to operate with many own ENVs).
Trying simple:
my $filename = '.envfile';
open FH, '>', $filename or die $!;
print FH "some data\n";
close(FH);
Apache says: Permission denied at /var/www/cgi-bin/env.cgi line 41.
Any help appreciated!
Thanks!
If I understand the question correctly, it appears that you also control the software which will ultimately read the file you're trying to create. Is that accurate? If so, change the program to get its environment from somewhere else. Where else? Preferably a new directory, so that you can make it writable by your web server without affecting anything else. I'd probably use /etc/myprogram (because /etc is the standard place for configuration files) or /var/local/myprogram (because /var is the standard place for persistent data files). But not an existing directory which is and should remain writable solely by root.
Short of exploiting a security flaw, Perl does not allow you to sidestep filesystem security (permissions). And that is a Good Thing. If it were allowed, it would mean that anyone who finds an exploit in your Perl code could then change any file on your computer, potentially replacing it with the most malicious code ever written.
Thus, the only way that your Perl can create a file in / is if it runs as root or uses su/suid to run some other program as root. And you really, really, really do not want CGI scripts or web applications running as root because, unless you do everything absolutely perfectly in your code, and there are no exploitable bugs in perl itself, or apache, or the kernel, then, by running your web code as root, you're potentially handing root access to any random script kiddie on the internet.
If you really, truly, absolutely have no choice other than to have web-accessible code write arbitrary files to /, then the least-bad, least-insecure way to do it would be to create a very tiny helper program which takes a file name and file contents as inputs, checks to verify that the named file does not already exist (so that an attacker can't use it to overwrite, say, your kernel), and then creates the named file with the provided contents. Aside from maybe a little additional sanity/security checking, it should do absolutely nothing else because the more complex this helper program is, the more likely it is to contain exploitable flaws. Then have the web code use suid to run the helper program, with suid configured to allow the web user (and only the web user) to run the helper program (and only the helper program) with no password.
But don't do that unless you really, truly, absolutely have no other option. It is not the best way to do it, it is the least bad way. Which means it's still a bad idea.
Create the file 'by hand' and set it's owner to the owner of the apache process, e.g.:
sudo touch /.envfile
sudo chown www-data:www-data /.envfile
sudo chmod u+rw /.envfile
You're executing your Perl program as a user without sufficient privilege. Run the Perl program using a user with sufficient privilege (e.g. using sudo or su).

Can I setuid a perl script?

I made a perl script to change owner of a file owned by some other user. Script is complete. My administrator save that in /sbin directory and set uid for it using chmod u+s name_of_script. But when I run this script it gives me error that chown operation is not permitted. I made a C program and it works by following same steps. So my question is if setuid is working for perl then I should not get that error because C code did not give me any error. So can i setuid for perl script or I should go with c code.
Don't tell me to ask administrator to change owner each time. Actually in server I have user name staging and I am hosting a joomla site in it. Now when I install some plugin then files related to that plugin are owned by www-data. So that's why I do not want to go to admin each time. Or you can give me some other solution also regarding my problem.
Many unix systems (probably most modern ones) ignore the suid bit on interpreter scripts, as it opens up too many security holes.
However, if you are using perl < 5.12.0, you can run perl scripts with setuid set, and they will run as root. How it works is that when the normal perl interpreter runs, and detects that the file you are trying to execute has the setuid bit set, and it then executes a program called suidperl. Suidperl takes care of elevating the user's privileges, and starting up the perl interpreter in a super-secure mode. suidperl is itself running with setuid root.
One of the consequences of this is that taint mode is turned on automatically. Other additional checks are also performed. You will probably see messages like:
Insecure $ENV{PATH} while running setuid at ./foobar.pl line 3.
perlsec provides some good information about securing such scripts.
suidperl is often not installed by default. You may have to install it via a separate package. If it is not installed then you get this message:
Can't do setuid (cannot exec sperl)
Having said all of that - you would be much better off using sudo to execute actions with elevated privileges. It is much more secure as you can specify exactly what is allowed to be executed via the sudoers file.
As of perl 5.12.0, suidperl was dropped. As a result, if you want to run a perl script on perl >= 5.12.0 with setuid set, you would have to write your own C wrapper. Again I recommend sudo as a better alternative.
No, you cannot use setuid aka chmod +s on scripts. The script's interpreter would be the thing that would actually need to be setuid, but doing that is a really bad idea. REALLY bad.
If you absolutely must have something written in Perl as setuid, the typical thing to do would be to make a small C wrapper that is setuid and executes the Perl script after starting. This gives you the best of both worlds in having a small and limited setuid script but still have a scripting language available to do the work.
If you have a sudo configuration that allows it (as most desktop linux distributions do for normal users), you can start your perl script with this line:
#!/usr/bin/env -S -i MYVAR=foo sudo --preserve-env perl -w -T
Then in your script before you use system() or backticks explicitly set your $ENV{PATH} (to de-taint it):
$ENV{PATH} = '/usr/bin';
Other environment variable that your script explicitly mentions or that get implicitly used by perl itself will have to be similarly de-tainted (see man perlsec).
This will probably (again depending on your exact sudo configuration) get you to the point where you only have to type in your root password once (per terminal) to run the script.
To avoid having to type your password at all you can add a line like this to the bottom of /etc/sudoers:
myusername ALL=(ALL) NOPASSWD:ALL
Of course you'd want to be careful with this on a multi-user system.
The -S options to env splits the string into separate arguments (making it possible to use options and combinations of programs like sudo/perl with the shebang mechanism). You can use -vS instead to see what it's doing.
The -i option to env clears the environment entirely.
MYVAR=foo introduces an environment variable definition.
The --preserve-env option to sudo will preserve MYVAR and others.
sudo sets up a minimal environment for you when it finds e.g. PATH to be missing.
The -i option to env and --preserve-env option to sudo may both be omitted and you'll probably end up with a slightly more extensive list of variables from your original environment including some X-related ones (presumably the ones the sudo configuration considers safe). --preserve-env without -i will end up passing along your entire unsanitized environment.
The -w and -T options to perl are generally advisable for scripts running as root.

How to successfully run Perl script with setuid() when used as cgi-bin?

I have a Perl script that is called either via Apache or on the command-line.
For testing purposes, I pass it the username I want the Perl script to operate with, and use POSIX::setuid to set the uid.
If I run the script from the command line, then the uid is set properly:
use CGI::Pretty qw/:standard/;
use POSIX qw(setuid getuid);
...
my ($pwName, $pwCode, $pwUid, $pwGid, $pwQuota, $pwComment,
$pwGcos, $pwHome, $pwLogprog) = getpwnam($username);
if ((defined $pwUid) && (getuid() == $pwUid)) {
setuid($pwUid);
print header;
print Dumper $<;
}
else {
print header(-status => 401);
print "Could not setuid to correct uid (currently: )".getuid()."\n";
}
The command-line output shows the correct uid of the specified $username, instead of the uid of the test account that started running the script.
If I call the script via Apache, then the uid remains set to the id of the apache user, and never changes.
I don't believe I can use suExec here, because, after reading the documentation:
I can't put a copy of this script into http://www.example.com/~username for every $username. The script needs to run from one location, and I need to specify the uid from within the script.
I need to have the script run as the specified username at runtime, and not as a single username specified once in a virtual host directive in an Apache configuration file. Changing this configuration file and restarting Apache every time a new user runs this script is not realistic.
How do I get a Perl script running as a cgi-bin to change the uid correctly, when using setuid()?
The only way you can setuid to an arbitrary uid is to run as root.[1]
I don't know about you, but the idea of a CGI program running as root gives me nightmares.
What is this code supposed to actually do after changing uid? Perhaps there's a way to accomplish this without having to setuid?
[1] Depending on your code and its security model, you may be able to collect the user's password and use su/sudo[2] to run a separate command-line program to run the actual operations outside of the web server environment, but su/sudo are able to do this because they're suid root and it would still open up most/all of the issues associated with running CGI code as root anyhow. Even if you filter out root as an invalid username, being able to masquerade as any arbitrary user opens up plenty of opportunities for abuse.
[2] sudo could even be configured to allow it without requiring a password, but there be dragons down that path. Be sure you know what you're doing if you attempt it, lest you give your users free reign to impersonate each other at will.