?
Solved

Lock Routine to protect files

Posted on 1998-02-05
22
Medium Priority
?
218 Views
Last Modified: 2013-12-25
Is the following lock routine secure? Yesterday I have lost
the .htpasswd file.. now I am unsure!

(lock routine is included in all scripts that writes to
.htpasswd and userdata file)

######LOCK
$endtime = 20;
$endtime = time + $endtime;
while (-e "/web/domain/html/datafiles/lock" && time < $endtime)
  {
  sleep(1);
  }
open(LOCK_FILE, ">/web/domain/html/datafiles/lock")  || die ("Could not create lock file");
close(LOCK_FILE);
######

write modified files back.....

######UNLOCK
   unlink("/web/domain/html/datafiles/lock");
######
0
Comment
Question by:falco
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 11
  • 9
  • 2
22 Comments
 
LVL 32

Accepted Solution

by:
jhance earned 320 total points
ID: 1831929
No, this is not a valid way to lock a file to prevent simultaneous.  In PERL, you need to use the flock() function to lock and unlock the file.  In your example above, another process can (and will) get in between the time when you check for the existence of the lock file and when you create it.  Using flock() causes the check/lock to happen in one indivisible operation that is multi-processing safe.
0
 

Author Comment

by:falco
ID: 1831930
Please could you modify the above snippet? Thanks
0
 
LVL 32

Expert Comment

by:jhance
ID: 1831931
#
# This will ONLY work on a platform that supports
# file locking and that has the flock() function
# implemented in it's port of PERL.  Note that some
# Windows PERL ports do not support this call (even though
# they should!)
#

# Open the .htpasswd file
open(PASSWD, ">>.htpasswd") || die "Error: $!";

# Lock for for exclusive access, flock will either lock and
# return, or will wait and then return when the file can be
# locked
flock(PASSWD, $LOCK_EX);

# Write stuff to the file here
print PASSWD "Stuff written to file\n";
#

# We're done, unlock it now
flock(PASSWD, $LOCK_UN);

# Close it
close(PASSWD);
0
How to Create Failover DNS Record Sets in Route 53

Route 53 has the ability to easily configure DNS record sets specifically for failover scenarios. These failover record sets can be configured to failover to full-blown deployments in other regions or to a static HTML page that informs your customers of the issue.

 

Author Comment

by:falco
ID: 1831932
Have I to set the variables "$LOCK_EX" and "$LOCK_UN"?
Should the "flock(PASSWD, $LOCK_EX);" not be before we
open the file?

open(PASSWD, ">>.htpasswd") || die "Error: $!";
flock(PASSWD, $LOCK_EX);
print PASSWD "Stuff written to file\n";
flock(PASSWD, $LOCK_UN);
close(PASSWD);

open(USERFILE, ">>userdata.log") || die "Error: $!";
flock(USERFILE, $LOCK_EX);
print USERFILE "Stuff written to file\n";
flock(USERFILE, $LOCK_UN);
close(USERFILE);
                        # Close it

0
 
LVL 32

Expert Comment

by:jhance
ID: 1831933
The flock() comes AFTER the open() call as you must have the file handle (PASSWD) before you can flock() it.  The variables $LOCK_EX and $LOCK_UN are predefined in PERL.
0
 

Author Comment

by:falco
ID: 1831934
Thanks! I'll try it now
0
 
LVL 84

Expert Comment

by:ozo
ID: 1831935
 use Fcntl ':flock';
will define LOCK_EX and LOCK_UN, (not $LOCK_EX and $LOCK_UN)
otherwise, they won't be predefined

Also, if you're appending, you should
  seek PASSWD,0,2;
after acquireing the lock, in case someone else appended while
you were waiting for the lock.
0
 

Author Comment

by:falco
ID: 1831936
------------------------------
open(HTPASSWD, "|$htpasswd $passfile $contents{'username'} >/dev/null 2>&1");
flock(HTPASSWD, $LOCK_EX);
print HTPASSWD "$passwort\n";
flock(HTPASSWD, $LOCK_UN);
close(HTPASSWD);

#print "Content-type: text/plain\n\n ";
open(USERINFO, ">>$pwlog");
flock(USERINFO, $LOCK_EX);
print USERINFO "......................\n";
flock(USERINFO, $LOCK_UN);
close (USERINFO);
------------------------------
Have I to alter above or is it secure??
0
 
LVL 32

Expert Comment

by:jhance
ID: 1831937
It looks OK to me but I would heed ozo's suggestion to insert a:

seek PASSWD,0,2;

after any $LOCK_EX flock() call.  This will make sure that you are positioned at the end of the file after the flock() returns.

For example:

#print "Content-type: text/plain\n\n ";
open(USERINFO, ">>$pwlog");
flock(USERINFO, $LOCK_EX);
seek(USERINFO, 0, 2);
print USERINFO "......................\n";
flock(USERINFO, $LOCK_UN);
close (USERINFO);
0
 

Author Comment

by:falco
ID: 1831938
my last question about flock:

Please could you paste the necessary flocks to secure the following sequence?

  open (COUNTER_FILE, "$counterfile")  || die ("Can't open counterfile");

  while (<COUNTER_FILE>)
    {
    $current_counter = $_;
    }
  close (COUNTER_FILE);

  $current_counter++;
  $new_counter = $current_counter;

  open (COUNTER_FILE, ">$counterfile")  || die ("Can't open counterfile");
  print COUNTER_FILE "$new_counter";
  close (COUNTER_FILE);

  open (DATABASE, "$database")  || die ("Can't open database");
  @Ads = <DATABASE>;
  close DATABASE;
 
  @Ads1 = reverse (@Ads);
  push (@Ads1, "$new_counter|$data1|$data2|$data3|$data4\n");
  @Ads2 = reverse (@Ads1);
  open (DATABASE, ">$database")  || die ("Can't open database");
  print DATABASE @Ads2;
  close DATABASE;
0
 

Author Comment

by:falco
ID: 1831939
Great! Thank you
0
 
LVL 32

Expert Comment

by:jhance
ID: 1831940
open (COUNTER_FILE, "$counterfile") || die ("Can't open counterfile");
flock(COUNTER_FILE, $FLOCK_EX);
seek(COUNTER_FILE, 0, 2);

while (<COUNTER_FILE>)
 {
 $current_counter = $_;
 }

flock(COUNTER_FILE, $FLOCK_UN);
close (COUNTER_FILE);

$current_counter++;
$new_counter = $current_counter;

open (COUNTER_FILE, ">$counterfile") || die ("Can't open counterfile");
flock(COUNTER_FILE, $FLOCK_EX);
seek(COUNTER_FILE, 0, 2);

print COUNTER_FILE "$new_counter";
flock(COUNTER_FILE, $FLOCK_UN);
close (COUNTER_FILE);

open (DATABASE, "$database") || die ("Can't open database");
flock(DATABASE, $FLOCK_EX);
seek(DATABASE, 0, 2);
@Ads = <DATABASE>;
flock(DATABASE, $FLOCK_UN);
close DATABASE;
 
@Ads1 = reverse (@Ads);
push (@Ads1, "$new_counter|$data1|$data2|$data3|$data4\n");
@Ads2 = reverse (@Ads1);
open (DATABASE, ">$database") || die ("Can't open database");
flock(DATABASE, $FLOCK_EX);
seek(DATABASE, 0, 2);
print DATABASE @Ads2;
flock(DATABASE, $FLOCK_UN);
close DATABASE;
0
 

Author Comment

by:falco
ID: 1831941
Dear Top Expert

Really dont know what to say. You are simply great!

Thanks again.

(There are just 80 points on my account. I'll come back to you to
assign some extra points)



0
 
LVL 84

Expert Comment

by:ozo
ID: 1831942
If your version of perl is earlier than 5.004,
you should close or FileHandle::flush before you FLOCK_UN,
(Or just close and don't FLOCK_UN)
5.004 does an automatic flush, but it's probably best
not to FLOCK_UN before closing or flushing anyway, so someone who hasn't upgraded
doesn't try to use your code as a model.

Also, I still don't see $FLOCK_EX being defined,
most systems use 2, but you should
 use Fcntl ':flock';
 $FLOCK_EX = LOCK_EX;
to be sure you have the right value for your system
0
 

Author Comment

by:falco
ID: 1831943
I have problems with "seek(HTPASSWD, 0, 2)" in read routines.
write operations works properly.
-----------------
$Userexists = "0";
open (HTPASSWD, "$passfile");
flock(HTPASSWD, $LOCK_EX);
# seek(HTPASSWD, 0, 2);
@Users = <HTPASSWD>;
close HTPASSWD;
.....
------------------

0
 
LVL 32

Expert Comment

by:jhance
ID: 1831944
Yes, my mistake.  seek(FILE, 0, 2) says position for file at the END of the file.  So if you are reading, you wouldn't get anything.  For the read operations, use:

seek(FILE, 0, 0);

This will position your reading to start at the BEGINNING of the file (assuming that's what you want to happen).
0
 
LVL 32

Expert Comment

by:jhance
ID: 1831945
falco,

It ocurred to me that I had not posted the whole thing.  ozo was hinting at it with "use Fcntl ':flock';".  Be sure you define $FLOCK_EX and $FLOCK_UN like this:

$FLOCK_EX = 2;
$FLOCK_UN = 8;

at the beginning of your program.


0
 

Author Comment

by:falco
ID: 1831946
Thank you, but what is ment with "Fcntl ':flock'"?
0
 
LVL 32

Expert Comment

by:jhance
ID: 1831947
It's like an include file in a C or C++ program.  You say:

use Fcntl':flock'

and it will load that package.  It's not in all versions of perl and I tend not to use it as you can't always count on it being there, and you don't always have the luxury of being able to convince people to upgrade their software.
0
 

Author Comment

by:falco
ID: 1831948
ITs unbelievable. I have lost the .htpasswd file again. (filesize 98kb)
Have I to alter the "htpasswd.pl" script too?

$LOCK_EX = 2;
$LOCK_UN = 8;
open(HTPASSWD, "|/path/htpasswd.pl /path/.htpasswd $contents{'username'} >/dev/null 2>&1");
flock(HTPASSWD, $LOCK_EX);
seek(HTPASSWD, 0, 2);
print HTPASSWD "$passwort\n";
flock(HTPASSWD, $LOCK_UN);
close(HTPASSWD);


--------------htpasswd.pl----
#!/usr/bin/perl

&GetArgs;               # Get Command line args or die
&LoadPwFile;            # Load current passwordfile
                        # (I'll add file locking later)
print &Prompt;          # Prompt for password
chop($pass=<>);         # Get new password
&MakeNewPassword;       # Encrypt the newly entered password with "salt".
$list{$name} = $cpw;    # Load new crypt pw into passwordfile array
&WriteNewFile;          # Write the amended array as the passwordfile

sub GetArgs {
        $file=shift(@ARGV);
        $name=shift(@ARGV);
        if ( (!$file) | (!$name) ) {
                die "Usage: $0 htpasswdfile username\n";
        }
}
sub MakeNewPassword {
        srand($$|time);                                 # random seed
        @saltchars=(a..z,A..Z,0..9,'.','/');            # valid salt chars
        $salt=$saltchars[int(rand($#saltchars+1))];     # first random salt char
        $salt.=$saltchars[int(rand($#saltchars+1))];    # second random salt char
        $cpw = crypt($pass,$salt);
}
sub LoadPwFile {
        open(HP, "$file") || open(HP, ">$file");
        while (<HP>) {
                chop;
                ($tname,$tpw) = split(':',$_);
                $list{$tname} = $tpw;
        }
}
sub Prompt {
        local($text);
        if ($list{$name}) {
                $text = "Changing password for $name\nEnter new password:";
        }
        else {
                $text = "Enter password for $name:";
        }
        return($text);
}
sub WriteNewFile {
        open(HTPASSWD, ">$file");
                foreach $key (sort keys(%list)) {
                        print HTPASSWD "$key:$list{$key}\n";  # print it
                }
        close(HTPASSWD);
}
---------------------------------------

0
 
LVL 32

Expert Comment

by:jhance
ID: 1831949
You must assume that ANY file that is being written to in your web by a cgi-bin script is at risk of simultaneous access.  You should flock every file you open for WRITE or UPDATE in a cgi script.  No exceptions.  I am still paranoid, so I usually make periodic backup copies as well.


0
 

Author Comment

by:falco
ID: 1831950
I had a backup but I dont understand the following command:


                    open(HTPASSWD, "|/path/htpasswd.pl /path/.htpasswd $contents{'username'} >/dev/null 2>&1");

and how to set flock in this sub of htpasswd.pl

sub LoadPwFile {
                        open(HP, "$file") || open(HP, ">$file");
                        while (<HP>) {
                        chop;
                        ($tname,$tpw) = split(':',$_);
                        $list{$tname} = $tpw;
                        }
                    }
0

Featured Post

Get real performance insights from real users

Key features:
- Total Pages Views and Load times
- Top Pages Viewed and Load Times
- Real Time Site Page Build Performance
- Users’ Browser and Platform Performance
- Geographic User Breakdown
- And more

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In this tutorial I will show you how to provide a dynamic RTF document on your website generated with data from your database. For this tutorial you will need Microsoft Word or WordPad, WhizBase and Microsoft Access. In this tutorial I will show …
This article is meant to give a basic understanding of how to use R Sweave as a way to merge LaTeX and R code seamlessly into one presentable document.
This tutorial will teach you the core code needed to finalize the addition of a watermark to your image. The viewer will use a small PHP class to learn and create a watermark.
In this fifth video of the Xpdf series, we discuss and demonstrate the PDFdetach utility, which is able to list and, more importantly, extract attachments that are embedded in PDF files. It does this via a command line interface, making it suitable …
Suggested Courses

777 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question