Writing or copying file to another directory


P

Paul E. Schoen

I have a simple form mailing script that I've added to. I can get the
results formatted in HTML sent to stdout to appear on the new web page in
the IE8 browser, and I can write the same to a file in the cgi-bin
directory, which is "output.htm" and chdir 777. But I want to write the file
in the directory where the HTML for the submit form is located, and nothing
seems to work. Here is the script with things I tried. I have searched the
docs and FAQs and online but nothing seems to work for a different
directory, which also has the files with 777 permissions.

The HTML with JavaScript is live at
http://www.smart.net/~pstech/SCGBG/EventSubmitJS.htm It checks the Full Name
entry as a simple password. The correct name as coded below will actually
send an email to me.

Any help will be appreciated. Thanks!

Paul

----------------------------------------------------------------------

#!/usr/bin/perl
#
# mailer.pl-- A simple program to mail form data to an email address
#
# Written in 1997 by James Marshall, (e-mail address removed)
# For the latest, see http://www.jmarshall.com/easy/cgi/
#

# IMPORTANT: MAKE SURE THESE TWO VALUES ARE SET CORRECTLY FOR YOU!
# This is the location in smart.net
$mailprog= "/usr/bin/sendmail" ;

$recipient= "paul\@peschoen.com" ; # make sure to \ escape the @

# Get the CGI input variables
%in= &getcgivars ;

if($in{'Full_Name'} ne 'Paul E. Schoen') {
&HTMLdie("Unauthorized user: $in{'Full_Name'}");}

# Open the mailing process
open(MAIL, "|$mailprog $recipient")
|| &HTMLdie("Couldn't send the mail (couldn't run $mailprog).") ;

# Print the header information
$ENV{'HTTP_REFERER'} || ($ENV{'HTTP_REFERER'}= "www.peschoen.com") ;
print MAIL "From: $in{'Email'}\n",
"Subject: Form data from $in{'Full_Name'}\n\n",
"The following data was entered at $ENV{'HTTP_REFERER'}:\n\n" ;

# Find length of longest field name, for formatting; include space for colon
$maxlength= 0 ;
foreach (keys %in) {
$maxlength= length if length > $maxlength ;
}
$maxlength++ ;

# Print each CGI variable received by the script, one per line.
# This just prints the fields in alphabetical order. To define your own
# order, use something like
# foreach ('firstname', 'lastname', 'phone', 'address1', ... ) {
foreach ('Full_Name', 'Email', 'Event_Title','Event_Date','Event_Time',
'Event_Description') {

# If a field has newlines, it's probably a block of text; indent it.
if ($in{$_}=~ /\n/) {
$in{$_}= "\n" . $in{$_} ;
$in{$_}=~ s/\n/\n /g ;
$in{$_}.= "\n" ;
}

# comma-separate multiple selections
$in{$_}=~ s/\0/, /g ;

# Print fields, aligning columns neatly
printf MAIL "%-${maxlength}s %s\n", "$_:", $in{$_} ;
}


# Close the process and mail the data

close(MAIL) ;

# Print an HTML response to the user
$eTitle=$in{'Event_Title'};
$eDate=$in{'Event_Date'};
$eTime=$in{'Event_Time'};
$eDescr=$in{'Event_Description'};
print <<EOF ;
Content-type: text/html

<html>
<body>
<h3>Your data has been sent.</h3>
<p><h3>$eTitle</h3>
<h4>Date: $eDate</h4>
<h4>Time: $eTime</h4>
$eDescr</p>
</body>
</html>
EOF

########## Here's where the problems are; the rest seems to work OK
#################

# Print to the HTML file (write, append, create)
#chdir('/home/pstech/www/SCGBG/'); #still writes to cgi-bin
#open DATA1, '>', "/home/pstech/www/SCGBG/output.txt" or HTMLdie ("File
error: $!"); #No such file/dir
#open DATA1, '>', "output.txt" or HTMLdie ("File error: $!"); #Writes to
cgi-bin OK
open DATA1, '>', "output.htm" or HTMLdie ("File error: $!"); #Writes to
cgi-bin OK
#open my DATA1, '>', "output.htm" or HTMLdie ("File error: $!"); #Internal
server error
#open DATA1, '>', "//home//pstech//www//SCGBG//output.txt" or HTMLdie ("File
error: $!"); #No such file/dir
#copy("output.htm","/home/pstech/www/SCGBG/output.htm") or HTMLdie ("File
error: $!"); #doesn't work, no error
#copy("output.htm","/www/SCGBG/output.htm") or HTMLdie ("File error: $!");
#doesn't work, no error
open DATA1, '>', "output.htm" or HTMLdie ("File error: $!"); #Writes to
cgi-bin OK

print DATA1 <<EOF ;
Content-type: text/html

<html>
<body>
<p><h3>$eTitle</h3>
<h4>Date: $eDate</h4>
<h4>Time: $eTime</h4>
$eDescr</p>
</body>
</html>
EOF

close (DATA1);
#use File::copy; #Internal server error
#copy("output.htm","test.htm") or HTMLdie ("File error: $!"); #doesn't work,
no error msg, clears source file?
#copy("output.htm","../SCGBG/output.htm") or HTMLdie ("File error: $!");
#doesn't work, no error msg
copy("output.htm","output.txt") or HTMLdie ("File error: $!"); #doesn't
work, no error msg

exit ;

....(Subroutines)
 
Ad

Advertisements

P

Paul E. Schoen

Ben Morrow said:
Where is

use warnings;
use strict;

It works with warnings, but strict causes it to fail
? Have you read the Posting Guidelines? (The fact you didn't write this
doesn't excuse you.)

Yes, I have read the guidelines, and I think I followed them to the best of
my ability as a newbie.

Don't call subs with '&'.

It did not work when I removed the &

Use multi-arg open.
Use lexical filehandles.

open(my $MAIL, "|-", $mailprog, $recipient)
|| HTMLdie("...");

It does not work with "my".

You are writing to MAIL, so you need to check the return value of close.

Yes, that is good practice. I will need to check the details of the
function. Boolean?

Does this directory exist? Can the CGI process see it? Is the CGI
process running chrooted?

The directory exists and I can go there using Telnet "cd
/home/pstech/www/SCGBG/". I don't know what "chrooted" means.

...apparently not.

It works in Telnet if I use "pico /home/pstech/www/SCGBG/output.txt"

Case matters. It's File::Copy.

use File::Copy; #was Internal server error - Changed case - PES
$file1="output.htm";
$file2="output.txt"; #this works, copies in same directory
copy($file1, $file2) or HTMLdie ("File error: $file2 $!");
$file2="/home/pstech/www/SCGBG/output.htm"; #this fails, no such file or dir
copy($file1, $file2) or HTMLdie ("File error: $file2 $!");

I made the following changes to a test script, ran it directly using Telnet,
corrected the errors, and now it compiles and works:

--------------------------------------------------------------------------

#!/usr/bin/perl
#
# TestCopy.pl
# Written in 2010 by Paul E. Schoen

use warnings;
use strict;

use File::Copy; #was Internal server error - Changed case - PES
my $file1="output.htm";
my $file2="output.txt"; #this works, copies in same directory
copy($file1, $file2) or HTMLdie ("File error: $file2 $!");
$file2="/home/pstech/www/SCGBG/output.htm"; #this works, copies in other dir
copy($file1, $file2) or HTMLdie ("File error: $file2 $!");

exit ;

# Die, outputting HTML error page
# If no $title, use a default title
sub HTMLdie {
my($msg, $title)= @_ ;
$title= "CGI Error" if $title eq '' ;
print <<EOF ;
Content-type: text/html

<html>
<head>
<title>$title</title>
</head>
<body>
<h1>$title</h1>
<h3>$msg</h3>
</body>
</html>
EOF

exit ;
}

-----------------------------------------------------------

Thanks for your help. I need to learn more about how to debug a script. The
error messages were very helpful. But I'm not sure if I can run the mailer
script from Telnet. Is there a way to see the compiler error messages,
perhaps in an error log?

Paul
 
P

Paul E. Schoen

I found that, after making corrections to the mailer.js script so it would
compile from the Telnet command line, it would perform the file copy to the
other directory where the HTML file is located, outside of cgi-bin. But when
I run it, or the TestCopy.pl, from the browser, it gives the CGI error:

File error: /home/pstech/www/SCGBG/output.htm No such file or directory

That is a confusing message, as the file does exist in that directory. I
think it must be something to do with the server configuration. I do need to
use a different URL:

http://www.smart.net/pstech-cgi-bin/TestCopy.pl

to run the script. If I use the normal URL:

http://www.smart.net/~pstech/cgi-bin/TestCopy.pl

I just get the text of the TestCopy.pl script.

Paul
 
M

Mart van de Wege

Paul E. Schoen said:
It works with warnings, but strict causes it to fail
This is a bad sign.

If the fix is trivial, fix it, but otherwise, if the script isn't built
to run under 'strict', it's a waste of time to try and rewrite it so it
does.[1]

Mart

[1] And before someone chimes in: yes, that's a rule of thumb, I'm sure
you can think of exceptions. That's not the point.
 
S

Steve

But your CGI program is most likely not running as you, so what
you can do is not relevant.
Ummm.... if it is NOT running scripts as the site owner/user, then the
web server is not running SUEXEC.

That in itself is a huge red flag, since anyone on the server can access
his scripts, and more importantly, any authentication values in the
scripts or in configuration files.

In other words, Paul's scripts have no security at all......

Personally, I would never even consider doing CGI on a shared server
that was not running SUEXEC.

my $.02

It's been a few years since I used it, but I believe the below might
tell Paul if the server is running SUEXEC:

#!/usr/bin/perl

use warnings;
use strict;

my $real_uid = $<;
my $uid_name = getpwuid($real_uid);

my $effective_uid = $>;
my $eff_name = getpwuid( $effective_uid );

my $real_group = $(;
my $group_name = getgrgid($real_group);

my $effective_group = $);
my $eff_group = getgrgid( $effective_group );


print "Content-type: text/plain\n\n";


print qq~
Real User: $real_uid / $uid_name
Process User: $effective_uid / $eff_name
Real Group: $real_group / $group_name
Process Group: $effective_group / $eff_group
~;

The output(s) should match... on my local box I get this:

Real User: 502 / testuser
Process User: 502 / testuser
Real Group: 416 416 / apache
Process Group: 416 416 / apache

If Real User and Process User do NOT match... well...


\s
 
S

Steve

(To my mind CGI is in and of itself a security hole. I always use
FastCGI, now, with an independantly-stared FastCGI-process running as a
dedicated user...)


No. This will tell you if you are running setid; but suEXEC does a
'full' setid setting both real and effective IDs, so you won't see it.
(Incidentally, this means perl won't turn on taint mode for you. Make
sure to remember to do that yourself.)

You need to check if $> is your uid, or the webserver's.

Ben

As I say, it's been a number of years since I used that code, but as I
recalled, on a non-SUEXEC Apache setup, it would return different user
names, and the same on SUEXEC.

A quick test shows that you are right and my memory was faulty. (surpirse)

At any rate it will tell you who the CGI is running as....

The *same* virtual host with SUEXEC

Real User: 502 / testuser
Process User: 502 / testuser
Real Group: 416 416 / apache
Process Group: 416 416 / apache

And then without SUEXEC:

Real User: 480 / apache
Process User: 480 / apache
Real Group: 416 416 / apache
Process Group: 416 416 / apache


So... I guess it would be more accurate to say that if the Real and
Process user is the owner of the script as opposed to Apache, then
SUEXEC is turned on/enabled.


thanks for looking over my shoulder Ben.

\s
 
Ad

Advertisements

R

Randal L. Schwartz

Ben> I think the Open Source movement might have an issue with this
Ben> assertion...

Nope. The Open Source movement says the same thing. If bad guys can
see the source, they can break it easier.

The Open Source movement offsets this with, "but if the good guys can
also see it, and FIX it, then the risk is appropriately mitigated."

print "Just another Perl hacker,"; # the original
 
P

Paul E. Schoen

Randal L. Schwartz said:
Ben> I think the Open Source movement might have an issue with this
Ben> assertion...

Nope. The Open Source movement says the same thing. If bad guys can
see the source, they can break it easier.

The Open Source movement offsets this with, "but if the good guys can
also see it, and FIX it, then the risk is appropriately mitigated."

print "Just another Perl hacker,"; # the original

Just to let you know I have fixed the perl script so that it no longer shows
errors using strict and warnings. I am concerned about security especially
now that I have FTP access to the Sierra Club server. For now I just have
links to the web page on my server, as "under construction", and that page
links to the CGI mailer.pl script also on my server which emails the
information to me and also converts it to HTML and copies it to a file.

I have the permissions on these files as 777 but I probably want to remove
the write and possibly the read permissions in the cgi-bin folder. Does an
executable script, accessed by anyone through a link in the web page, have
write permission to files with permission set at, for example, 700? And
should the mailer.pl script have permission 711 to keep anyone other than
myself from reading or writing it?

My problems were mostly because of the host configuration, and the sys admin
told me that it was set up as a "jail" which restricted access. I do believe
it is running SUEXEC. Hopefully now I will not need to deal with those
issues and I will need to balance my efforts between the HTML with
JavaScript on one hand, with the CGI perl script on the other. Since I may
not have the time and enthusiasm to become proficient in perl I may try to
do as much processing as possible in the JavaScript of the HTML document,
although it may be better suited to server side scripting.

Thanks for all your help and patience. I was amused "joey's" thread on the
simple loop.

Paul
www.pstech-inc.com
 
P

Paul E. Schoen

Ben Morrow said:
You can't make a perl script unreadable. The perl interpreter needs to
be able to read the file in order to run the script.

I have added an index.html file to the cgi-bin directory which just displays
"Restricted access" and does not allow public viw of the directory, which at
least gives a bit better security. But I don't fully understand how the UNIX
system determines the identity of the originator of a read, write, or
execute request. If I log on using Telnet or an FTP program, it seems a bit
clearer, but when I use a browser to access a web page which calls a CGI
script, is my identity used to determine the file permissions I have? Is the
perl interpreter assumed to be a public entity? And then when my perl script
reads or writes to a file in the cgi-bin or another directory, are the
permissions based on my original identity as determined by the browser?

I'd like to have things set up with the maximum possible level of security,
but still allow the necessary processes to take place. I found this document
about security: http://www.w3.org/Security/Faq/wwwsf1.html and it answered
some questions. The perl faq was not helpful except to say that the perl
source is not easily hidden.
Jails are a form of chroot, so this may be the cause of your file access
problems.

Yes, I was given a special URL to access a file from the perl script, and
now the files are found and can be written. But could a public or anonymous
user also use that URL to write to the file? I suppose I'm not very worried
about someone using my scripts for their own purposes (especially if they
are not well-written), but I would not want to supply enough clues for a
malicious hacker to cause mayhem. I can probably get good information from
my Smartnet system administrator, but I think these issues are frequently
encountered when adding CGI and perl scripts to a website and it would
probably be worth including in the perl FAQ.

Thanks,

Paul
 
J

Jürgen Exner

Paul E. Schoen said:
But I don't fully understand how the UNIX
system determines the identity of the originator of a read, write, or
execute request.

This really has pretty much nothing to do with Perl. It would be the
same if you used a different programming language or even a totally
different application.
If I log on using Telnet or an FTP program, it seems a bit
clearer, but when I use a browser to access a web page which calls a CGI
script, is my identity used to determine the file permissions I have?

Please define "_my_ identity". Unless the program did a setuid or chown
it will run under whatever user called it. For CGI scripts that's often
'nobody' or 'www' or some other special account with very low
permissions.
Is the perl interpreter assumed to be a public entity?

In general yes because there would be little point in having private
copies of it.
And then when my perl script
reads or writes to a file in the cgi-bin or another directory, are the
permissions based on my original identity as determined by the browser?

Yes, typically they will be the same as the web browser's ID, e.g.
"nobody" or 'www' or whatever the sysadmin determined the cgi server
should run under.
I'd like to have things set up with the maximum possible level of security,
but still allow the necessary processes to take place. I found this document
about security: http://www.w3.org/Security/Faq/wwwsf1.html and it answered
some questions. The perl faq was not helpful except to say that the perl
source is not easily hidden.

Well, that's not surprising considering that Perl doesn't require (or
offers) anything different compared to other programming languages.

You have a CGI question, not a Perl question. Why would a CGI question
be answered in a Perl FAQ?
Yes, I was given a special URL to access a file from the perl script, and
now the files are found and can be written. But could a public or anonymous
user also use that URL to write to the file?

That depends how the web server has been set up. You need to ask in NG
that actually deals with the web server you are using.
but I think these issues are frequently
encountered when adding CGI and perl scripts to a website and it would
probably be worth including in the perl FAQ.

They are just as frequently encountered when adding CGI and C or Fortran
or Cobol or Haskell or $name_your_favourite_programming_lang to a web
site. Therefore they should be answered in a CGI FAQ and not in the Perl
FAQ.

jue
 
J

Jürgen Exner

Tad McClellan said:
Other programming languages have taint checking?

Yes, you are right. There are a few Perl-specific features for
CGI-programming like the taint checking that you mentioned. Those most
certainly belong into a "CGI in Perl" FAQ / document / man-page /
tutorial / .... .

But access rights of the web server do not fall into that category.

jue
 
Ad

Advertisements

P

Paul E. Schoen

Jürgen Exner said:
Yes, you are right. There are a few Perl-specific features for
CGI-programming like the taint checking that you mentioned. Those most
certainly belong into a "CGI in Perl" FAQ / document / man-page /
tutorial / .... .

But access rights of the web server do not fall into that category.

OK, I think I understand it more clearly now. I tried to find a newsgroup
specific to CGI but they appear to be inactive. I just thought it would be
helpful to add a topic in the FAQ about where to look for information on
server-side issues and security, since AFAIK perl is the predominate
language used for that purpose. I have requested that the administrator of
my account look at my website files on the server and let me know of any
security issues.

My next step in this project will be to add an activity to the list based on
the data that someone enters in the form provided for that purpose. It
should be sorted by date, and then converted to HTML for the home page to
display. It seems that a server-side script would be best for this purpose,
and Perl may be the best choice, but I might also look into using C or Java
because I am more familiar with the syntax. I might also be able to use
JavaScript in the HTML form submission document to convert to HTML, but I
would still need a server side script to write the file to the server.

I might do some searching for Perl scripts that already do most of what I
need. But I also need to make sure they are well written, which is nearly
impossible for me to determine since the language is still quite foreign to
me. If you can give me some advice as to where I can find what I need, then
I promise I won't bug you guys any more.

Thanks for all you have done so far.

Paul
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top