Solved

Lock Routine to protect files

Posted on 1998-02-05
22
207 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
  • 11
  • 9
  • 2
22 Comments
 
LVL 32

Accepted Solution

by:
jhance earned 80 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
 

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
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 
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

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

Suggested Solutions

I hope you'll find this tutorial useful and interesting. So let's try to extend Tcl with a new package.  For anyone more deeply interested please check out the book "Practical Programming in Tcl and Tk". It's really one of the best written books abo…
It is a general practice to get rid of old user profiles on a computer  in a LAN environment. As I have been working with a company in a LAN environment where users move from one place to some other place at times. This will make many user profil…
The viewer will learn how to count occurrences of each item in an array.
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.

762 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

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now