# Blosxom Plugin:Login # Author: Fletcher T. Penney # Version: 0.4 # NOTE: a functional cookies plugin is required. Apparently, it has to be named something like # 9999cookies to work properly. # # Also, this plugin works by modifying the filter subroutine. You may need to rename your plugins to insure # the proper order, but it works properly with exclude, and menu to my testing. I am sure there are possible # conflicts with other plugins though. package login; # --- Configurable variables ----- # Where is the passwd file? Should not be in a public directory nor group/world writeable # This file is created with the htpasswd command (Do a man htpasswd to figure it out - please don't ask # me to explain how to do this - there are plenty of references on the web. # I will work on updating this to allow a cgi program to maintain this file, but can't guarantee # when that will occur. In the meantime, this provides for better security. # # my $passwd_file = "$blosxom::datadir/passwd"; # Where is your excludefile - this controls which user is able to see which files # the excludefile is built using regular expressions in the format: # user=page # user is a regexp to match allowed user (.* matches all VALIDATED users) # page is a regexp to match pages or directories (.* matches all pages) # # Examples: # A blank or nonexistent file allows full access to anyone, logged in or not # myusername=.* # Only someone logged in as myusername can access any files on the web site # (myuser1|myuser2)=private # Only these two people can access a file or directory named private, private1, privatestuff, etc # .*=priv$ # Any VALIDATED user can view the directory named priv, but this doesn't affect one named private # # my $excludefile = "$blosxom::datadir/requireuser"; my $default_message = "Please log in."; # This section for gui creation of accounts my $pending_file = "$blosxom::plugin_state_dir/pendingaccounts"; # -------------------------------- # Ideas for improvement # Option to log all succesful logins # Ability to set a sort of exclude file to filter out entries by login name use CGI qw/:standard/; $username = ""; # users login name, if any - set only after password is validated $path_noflavour = ""; $message = ""; my $passphrase =""; my $validated = 0; sub validate { my ($submituser, $submitpass) = @_; if (open(PASSWD, $passwd_file)) { $message = "Password file open."; while () { chop; ($testuser, $testpass) = split(':',$_); if ($testuser eq $submituser) { if (crypt($submitpass,$testpass) eq $testpass) { $message = "Welcome, $submituser."; $username = $submituser; $validated = 1; $loginform = $logoutform; } else { warn "blosxom : login plugin > Incorrect password for user $submituser."; } } } if ($validated eq 0) { $message = "Login for user $submituser failed."; warn "blosxom : login plugin > $message"; $username = ""; } close PASSWD; } else { $message = "Unable to access password file.\n"; warn "blosxom : login plugin > $message"; $username = ""; } return $validated; } sub start { $path_noflavour = $blosxom::path_info; if ($path_noflavour !~ s/\.[^\.]*$//) { $path_noflavour =~ s/\/$//; $path_noflavour .= "\/index"; $path_noflavour =~ s/^([^\/])/$1/; } $path_noflavour =~ s/^\/*//; open (REQUIRE, $excludefile); @requiredlist = ; close REQUIRE; # HTML code for the login form to be displayed # Converts to a logout button as well? $loginform = qq!
Login:
Password:
!; $logoutform = qq!
!; $signupform = qq!
Login(no spaces):
Email:
Password:
Verify:
!; 1; } sub filter { my ($pkg, $files_ref) = @_; my @files_list = keys %$files_ref; # This handles login requests and creates a cookie, if valid if ( request_method() eq 'POST' and (param('plugin') eq 'login')) { if (param('task') eq 'logout') { &cookies::remove('login'); # These don't seem to work &cookies::clear('login'); # So I overwrite the cookie below &cookies::add( cookie( -name=>'login', -value=>{ }, -domain=>$cookies::domain, -expires=>'now' ) ); $username=""; $validated=-1; } if (param('task') eq 'login') { my $user = param('user'); my $pass = param('pass'); if ((validate($user,$pass)) and $blosxom::plugins{cookies} > 0) { # Create a cookie &cookies::add( cookie( -name=>'login', -value=>{ 'user' => $user, 'pass' => $pass}, -domain=>$cookies::domain, -expires=>$cookies::expires ) ); } } if (param('task') eq 'signup') { my $user = param('user'); my $pass = param('pass'); my $verify = param('verifypass'); my $email = param('email'); $validated=-1; $message= ""; if ($pass ne $verify) { $message = "Your passwords were different. Please try again."; } if (length($pass) < 5) { $message = "Come on.... Your password has to be at least 5 characters."; } if (length($email) <5) { $message = "That's not a real email address... Try again."; } if ($message eq "") { open(PENDING,">>$pending_file") || ($message = "Error submitting information. Please try again."); if ($message eq "") { my @salts = (a..z,A..Z,0..9,'.','/'); my $salt=$salts[rand @salts]; $salt.=$salts[rand @salts]; my $encrypted=crypt($pass,$salt); my ($dw, $mo, $mo_num, $da, $ti, $yr) = &blosxom::nice_date(time()); print PENDING "$yr $mo $da $ti $user $email $encrypted\n"; close PENDING; $message="Request submitted. Don't call us... We'll call you... ;)"; } } } } # Now, read cookies to see if user is logged in if ($blosxom::plugins{cookies} > 0 and my $cookie = &cookies::get('login') and $validated eq 0) { my $user = $cookie->{'user'}; my $pass = $cookie->{'pass'}; validate($user,$pass); } $message = $default_message if ($message eq ""); # Now filter the files foreach $file (@files_list) { $localfile = $file; $localfile =~ s/\/\//\//g; #An improperly formatted url such as: # /my/document/dir/secret//files would slip through foreach $required (@requiredlist) { if ($required =~ /(.*)=(.*)/) { $requser=$1; $reqfile=$2; if ($localfile =~ /^$blosxom::datadir(\/)?$reqfile/) { delete $files_ref->{$file} if (($username !~ /$requser/) or $username eq ""); } } } } 1; } 1; __END__ =head1 NAME Blosxom Plug-in: login =head1 DESCRIPTION Login allows you to create a passwd file that defines usernames and passwords for your web site. Another file, $excludefile, defines which users can access certain parts of your site. The format for the files is explained above. $login::username provides the users login, once it has been validated against the database. It remains "" if someone is not logged in. $login::loginform provides the login/logout box. Place it in your templates wherever you like. $login::message gives certain status messages. By default, login provides logging messages when certain errors occur, and when a failed login attempt happens. $login::signupform provides html code for a simple account sign up form. Once a user submits the information, it is added to a text file defined by $pending_file. If you want to add the account, simply copy the user and password hash to the defined password file in the format "user:hash" as defined by the htpasswd command. PLEASE NOTE: I am not a security expert. I can't guarantee that there isn't a huge loophole in this somewhere. And note - any malicious plug-in could expose a whole by tampering with the $username variable - verify that other plugins do not attempt to manipulate $login::username or access the cookies. But this is more a matter of being careful which plugins you install. Also, this plugin does not pretend to offer serious security. Someone could read a restricted file if they have read access to the location that the file is stored. This could occur if you datadir is in your web servers documentroot. There are many issues to consider when securing a web site, and I don't pretend to address them all, nor do I guarantee that this plugin even works properly. It is simply a means to provide some form of control without much effort on your part. You get what you pay for. But I will continue to work with the community to improve it as security holes are noted. =head1 AUTHOR Fletcher T. Penney - http://fletcher.freeshell.org =head1 LICENSE This source is submitted to the public domain. Feel free to use and modify it. If you like, a comment in your modified source attributing credit for my original work would be appreciated. THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND. USE AT YOUR OWN RISK!