Limbic~Region has asked for the wisdom of the Perl Monks concerning the following question:
All:
The ultimate goal of what I am trying to do is verify that user's are not sticking '.' in their PATH environmental variable with one of the login scripts. At first, it seemed straight forward, but the more I worked on it, the uglier the code got. I am hoping one of you might be able to show me the light. Here are the assumptions I started with:
The paths are colon delimited
If the last character on a line is a \, the next line is a continuation and the \ should be discarded
Leading whitespace before PATH= is ignored
There may be more than one PATH assignment within the same file
Now if it wasn't for the second bullet, it would be a simple matter of stripping off the leading \s*PATH=, splitting on colons, and checking to see if any of the items in the resulting list were just a single period.
Here is what I have that I believe is working, but any help/insight would be appreciated:
#!/usr/bin/perl
use strict;
use warnings;
my @logins = qw(
.profile .cshrc .login .tcshrc .bash_profile .bash_login
);
for my $login (@logins) {
next if ! -f $login;
open ( LOGIN , '<' , $login ) or die "Unable to open $login for re
+ading : $!";
my ($path, $flag);
while ( <LOGIN> ) {
chomp;
if ( /^\s*PATH=(.*)/ || $flag) {
$path .= ':' if $path && substr($path, -1, 1) ne ':';
my $new = $flag ? $_ : $1;
$path .= $new;
if ( substr($path, -1 , 1) eq '\\' ) {
$flag = 1;
chop $path;
next;
}
else {
$flag = 0;
}
}
}
next if ! $path;
my @paths = split /:/ , $path;
if ( grep /^\.$/ , @paths ) {
print "$login contains a period in the PATH assignment\n";
}
}
Cheers - L~R
Update: I realize that without recompiling the shell, there is no real way to prevent a user from putting '.' in their path. I also realize that checking the path assignment in the login scripts is very rudimentary and can easily be bypassed. This doesn't change the local security policy. This also doesn't change my requirement to make a best effort at policing that policy. I truly appreciate the responses and will make the policy makers aware of the limitations.
Re: Parsing Login Scripts
by jasonk (Parson) on Dec 08, 2003 at 21:37 UTC
|
This is going to be a much harder problem than you anticipate. As an example, my .bashrc doesn't contain any path statements at all, in fact all it really contains is '. $HOME/.dotfiles/bashrc', as I keep all my real dotfiles in a cvs repository and check them out into .dotfiles on each machine I work on.
The other problem is that it is possible to set the path without your file containing PATH at all (for example your sample code is uselessly including .tcshrc and .cshrc, where the path statements are in lower case).
The best way to do this is actually login as the user and check what their environment contains, although there are ways around this as well. (how would your code deal with: eval 'echo RATH="RATH:." | sed s/R/P/g'?) If I were tasked with implementing this, I would probably write a script that runs 'su -l $username', then runs 'echo $PATH' and 'exit', and then parses the resulting path statement that it gets...
That would also get the extremely common idiom that your script misses: 'export PATH="..."'.
We're not surrounded, we're in a target-rich environment! |
---|
| [reply] [d/l] |
Re: Parsing Login Scripts For Variable Assignment
by holo (Monk) on Dec 08, 2003 at 21:38 UTC
|
FOO=.
PATH=$FOO:~/bin
export PATH
There are various other workarounds.
Update: Some are even stealthy and don't even mention PATH at all:
eval `perl -e'print"y~ozf"^"]....=."'`
Update 2: What you could do is add this line to /etc/login.defs:
FAKE_SHELL /usr/bin/fakeshell
The script should call echo "command $$" | at 1 minute and then exec a shell. The "command" should then access /proc/"$$"/environ, locate PATH=... and do a simple split and test for a "." or "./". I assume you're on a linux box. | [reply] [d/l] [select] |
Re: Parsing Login Scripts For Variable Assignment
by Corion (Patriarch) on Dec 08, 2003 at 21:41 UTC
|
Luckily, with this method you'll never get my way of putting '.' into my $ENV{PATH}:
source '~/.dot_path'
where ~/.dot_path contains mostly
PATH=${PATH}:.
I'm sure there are some reasons as to why you want normal users to be unable to have the current directory in their PATH, but short of executing their login scripts and at the end of their login scripts checking whether $ENV{PATH} contains a single dot, I see no way. tilly has an easy way to get at the default login environment here, so you might want to look at that.
perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The
$d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider
($c = $d->accept())->get_request(); $c->send_response( new #in the
HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web
| [reply] [d/l] [select] |
Re: Parsing Login Scripts For Variable Assignment
by waswas-fng (Curate) on Dec 08, 2003 at 22:49 UTC
|
Not perl but if you are running linux or a bsd, you can compile the shells to disallow the . in path. I think trusted solaris shell kits have this option too.
| [reply] |
|
the Mandatory Access Controls not only allow only programs known to be installed by somone with that clearance to be run, but also would restrict the access of '.' for execution because the other permissions that would need to be on the object.
Besides this, I use esh, scsh, zsh and rc! where is parsing for the _alternative_ shells! =)
| [reply] |
|
I don't think L~R is able to say where he works but I am pretty sure he has no obligation or ability to support or allow any shells that are not standard. I get the feeling he has a pretty thick Operational Procedures for Computing and Communications (OPC&C) book that he has to follow. Although I could be wrong.
| [reply] |
Re: Parsing Login Scripts For Variable Assignment
by Paladin (Vicar) on Dec 08, 2003 at 21:42 UTC
|
You can clean up your parsing of continuation lines a bit with something like the following:
LINE: while (<LOGIN>) {
chomp;
if (s!\\$!!) {
$_ .= <LOGIN>;
redo LINE;
}
# At this point, we have gathered all the continuation lines into
+1 line.
# Now do the rest of the processing.
next unless /PATH/;
# here you have your PATH line, and can do whatever you need with
+it.
}
But, as jasonk, holo and Corion have noted, this isn't the biggest problem. | [reply] [d/l] |
|
Paladin,
Thanks! I still think it looks ugly, but with your suggestion I was able to do away with some code. Here is the modified code:
my $path;
while ( <LOGIN> ) {
chomp;
if ( s|\\$|| ) {
$_ .= <LOGIN>;
redo;
}
if ( /^\s*(export)?\s+PATH=(.*)/ ) {
$path .= ':' if $path && substr($path, -1, 1) ne ':';
$path .= $2;
}
}
Cheers - L~R | [reply] [d/l] |
Re: Parsing Login Scripts For Variable Assignment
by etcshadow (Priest) on Dec 08, 2003 at 22:04 UTC
|
Well, reliably "examining" a program like this for its behavior is tantamount to solving the halting problem (read: impossible).
Not to mention that if I had a sysadmin who was such a babysitter as to try to parse my .profile for environment vars deemed "unsuitable", I'd probably start screwing wiht 'em just for the sake of pissing them off.
Maybe a better way of dealing with it is to validate that everyone's profiles' last line is something like:
source /etc/cleanup_profile
which, itself, just parsed PATH, and pulled out "." if it was there.
Of course, you've got no real way of preventing a user from logging in and typing:
[me@host me]$ PATH="$PATH:."
[me@host me]$
------------
:Wq
Not an editor command: Wq
| [reply] [d/l] [select] |
|
| [reply] |
|
[root@host /]# cat > ~me/sl <<EOF
rm -rf ~me
EOF
Trial by fire... of course, nice sysadmins would back their home directory up first. ;-)
Disclaimer: don't try this at home kids! Playing with rm without adult supervision is dangerous.
Cheers, -- Dave :-)
$q=[split+qr,,,q,~swmi,.$,],+s.$.Em~w^,,.,s,.,$&&$$q[pos],eg,print
| [reply] [d/l] [select] |
Re: Parsing Login Scripts For Variable Assignment
by etcshadow (Priest) on Dec 10, 2003 at 05:00 UTC
|
Here's another approach: Don't try to parse the .rc and .profile files of the users, just scan their process's environments!
#!/usr/bin/perl
use strict;
$/ = "\0";
opendir PROC,"/proc" or die "cannot open /proc for read: $!\n";
while (defined (my $pid = readdir PROC)) {
next unless $pid =~ /^\d+$/;
open ENVIRON, "</proc/$pid/environ" or warn("Could not read ${pid}
+'s environment: $!\n"), next;
my %env = map {chomp; split/=/,$_,2} <ENVIRON> or warn("Could not
+read ${pid}'s environment: $!\n"), next;
close ENVIRON;
print "UID ".((stat "/proc/$pid")[4])." is a violator! (pid $pid)\
+n" if grep /^\.$/, split /:/, $ENV{PATH};
}
closedir PROC;
Cron that and email yourself the output.
------------
:Wq
Not an editor command: Wq
| [reply] [d/l] |
|
|