# hashword plugin # v 0.01 2004-09-05 # As a plugin, hashword provides a form to generate hashed # passwords from plaintext. As a module, hashword can be # called by other plugins to verify plaintext against a # hashed value. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # This is all the documentation there is, so read up ;-) # # This plugin is mainly just an exercise in using blosxom's # skip() routine, so don't expect too much from it. # The premise for hashword is that passwords stored inside # plugins (as with wikieditish, entriescache, etc.) should be # "more secure" -- that is, someone should be able to get # their hands on your working copy of a plugin and still not # have access to your password. Silly, perhaps, but you gotta # start somewhere.... # # hashword provides an easy means of generating encrypted # ("hashed") passwords for use in other plugins. You call # hashword from a URL and get a form; you type in your plaintext # password and get a hashed password back; you can then paste # this hashed password into another plugin's password config var, # and that plugin can call hashword to verify your plaintext # password against the stored hashed version. Net effect: your # 'en clair' password is never exposed in a plugin file. # # Obviously, for this to be effective, other plugins must call # hashword to do the verifying, or else provide their own # verification method. Since no plugins do this, a companion # plugin called 'hashword_tester' provides a demonstration of # password verification. (By itself, however, hashword is still # a great plugin for generating enrypted passwords ;-) # # USAGE # You can just drop hashword into your plugins folder and it's # ready to go. Ditto for the 'hashword_tester' plugin; they're # both preconfigured to just run. # # To use hashword as a plugin (that is, to have hashword make # encrypted versions of plaintext passwords), you must supply # it with a password. To do this, add a 'hashword' parameter to # a blosxom URL; the value of this parameter is the password # you've chosen for hashword operation. For example, let's say # you've chosen 'pass' as hashword's password. You would then # invoke hashword with something like the following URL: # http://www.example.com/cgi-bin/blosxom.cgi?hashword=pass # # The first time hashword runs, it will accept whatever pass- # word you feed it. It will hash that password, and write it to # a file for future reference. On subsequent invocations, the # plugin will check its reference file to see if the password # you supplied in the 'hashword' parameter matches; if it doesn't, # the plugin stops running. (To change your hashword password, # just throw away the reference file, named 'pwmstr'.) # To use hashword as a module to create or verify hashes, just # drop it in your plugins folder, and call it from within your # plugin. There are two routines available: # $hashword::make_hash() # which takes one parameter, a plaintext str to hash. Output # is either 0 (failure) or the hashed plaintext. # The other routine is: # $hashword::verify_hash() # which takes two parameters: a plaintext str to verify, # and the hashed str against which it is checked. Output # is either 0 (failure, no match) or 1 (match). # # To demonstrate hashword as a plugin, just enter a blosxom # URL as above. To demonstrate hashword as a module, follow # the simple steps in the 'hashword_tester' plugin. # # REMEMBER -- if you forget the plaintext of a password # that's been hashed, that's it; it's gone. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - package hashword; use strict; use FileHandle; use CGI qw/:standard :netscape/; # --- Configurable variables ----- # Password for hashword # location of the file that holds the hashed password for hashword # (leave blank to use your normal plugin state directory) my $master_password_dir = ""; # -------------------------------- use vars qw/$response $hashed_pw/; $response; # success/failure message $hashed_pw; # our output my $err_state; # index to error strings my @err_strs = ('', 'password cannot be empty', 'the two input passwords didn\'t match', 'hashing failed!', 'couldn\'t open master password file!', 'couldn\'t write master password file!'); my $run_skip; # flag for the skip() sub my $done; # flag for the respond-ing to a set my $master_pw; # password to run hashword as a plugin # this routine generates a hashed pw by creating a common # salt, then calling the crypt() function to make a hash sub make_hash { my $enclair_pw = shift; $enclair_pw or return 0; my $salt = join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z') [rand 64, rand 64]; return crypt($enclair_pw, $salt); } # this routine verifies a plaintext pw by hashing # the incoming pw using the known hash as salt, and # then comparing against the known hash sub verify_hash { my $enclair_pw = shift; $enclair_pw or return 0; $hashed_pw = shift; $hashed_pw or return 0; return (crypt($enclair_pw,$hashed_pw) eq $hashed_pw); } sub start { # only act like a plugin if someone calls us explicitly -- # values coming back from the form: if (request_method() eq 'POST' and param('plugin') eq 'hashword') { my $plaintxt_pw1 = param('password_text1'); my $plaintxt_pw2 = param('password_text2'); $plaintxt_pw1 or $err_state = 1; (! $err_state) and (($plaintxt_pw1 eq $plaintxt_pw2) or $err_state = 2); (! $err_state) and ($hashed_pw = make_hash($plaintxt_pw1)); (! $err_state) and ($hashed_pw or $err_state = 3); (! $err_state) and ($done = 1) and ($response = "Here's your hashed password: $hashed_pw"); $err_state and $response = "Sorry; no hashed password was produced:
$err_strs[$err_state]"; $run_skip = 1; # permission for skip() sub to run } # initial invocation: $master_password_dir or $master_password_dir = "$blosxom::plugin_state_dir"; if (param('hashword')) { $master_pw = param('hashword'); $master_pw or return 0; my $master_pw_file = "$master_password_dir/pwmstr"; my $master_pw_ref; my $fh = new FileHandle; if (-f "$master_pw_file") { # if we have a master pw on file, get it, $fh->open("< $master_pw_file") or $err_state = 4; chomp($master_pw_ref = <$fh>); $fh->close; verify_hash($master_pw,$master_pw_ref) or # and verify it... ($response = "You did not supply the correct password to operate this plugin" and $done = 1); } else { $master_pw_ref = make_hash($master_pw); # ...else make a master pw file $fh->open("> $master_pw_file") or $err_state = 5; print $fh $master_pw_ref; $fh->close(); } $run_skip = 1; # permission for skip() sub to run } return 1; } # skip() is a fun sub! # When it returns true, blosxom stops dead in its tracks; # this allows you to use blosxom as a "shell" for your own # CGIs. It's overkill, but it allows plugins to generate # their own independent pages without putting users through # the potential hassles of installing yet another cgi script. sub skip { my $pkg = shift; $run_skip or return 0; # only run if we're allowed # first, get rid of blosxom output $blosxom::output = ""; # appears to be superfulous $blosxom::header = ""; # yes -- clear this blosxom var #Now, send our own page -- ## first, make a header: print header( -type => "text/html", -charset => 'iso-8859-1', ); ## then make the form: print <<"_TOP_"; Password Encryptor
_TOP_ if ($done) { print <<"_MID_";
_MID_ } else { print <<"_FORM_";
$hashword::response
_FORM_ } print <<"_BOT_";
Password Hasher

Enter password:
Confirm password:
$response
_BOT_ 1; } 1; __DATA__ __END__