#!/usr/bin/perl -w
# GenPass.pl--Generate random passwords from the CLI or as a Web CGI
# $Id$
# $URL$
my $VERSION = '$Version: 3.0 $';
my $COPYRIGHT = 'Copyright 2001-2005 JP Vossen (http://www.jpsdomain.org/)';
my $LICENSE = 'GNU GENERAL PUBLIC LICENSE';
my $USAGE = ''; # Placeholder for usage info below
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
((my $PROGRAM = $0) =~ s/^.*(\/|\\)//ig); # remove up to last "\" or "/"
# This sub is here for quick documentation purposes. Other subs at bottom.
sub Usage {
# Called like: Usage ({exit code})
my $exit_code = $_[0] || 1; # Default of 1 if exit code not specified
# Unlike sh, Perl does not have a built in way to skip leading
# TABs (but not spaces) to allow indenting in HERE docs. So we cheat.
($USAGE = sprintf <<"EoN") =~ s/^\t//gm;
NAME
${PROGRAM}--Generate random passwords from the CLI or as a Web CGI
SYNOPSIS
* If the script name ends in '.cgi' it will run as a CGI script and
ignore the options below. You should also add '-T' to the #! line.
* If the script name ends in '.pl' it will run as below:
$PROGRAM [OPTIONS] -a|{ulnpx} [-s #] [-c #]
OPTIONS
-u = Use UPPER case letters
-l = Use lower case letters
-n = Use numbers
-p = Use some punctuation
-P = Use all punctuation
-a = Same as -ulnp (default)
-A = Same as -ulnP
-x = Use only mixed case Hex characters ( 0..9, a..z, A..Z)
-s {size} = Size of the password (default = 20)
-c {count} = Count of how many passwords to generate (default = 10)
-h = This usage
-v = Be verbose
-V = Show version, copyright and license information
Examples, if no options are given runs as:
$PROGRAM -a -c 10 -s 20
DESCRIPTION ($VERSION)
Create reasonably random passwords and/or unpronounceable alien names for
Science Fiction stories.
If -a is used, it takes precedence over ANY other character set
option and silently overrides it.
BUGS
It would be nice if -T was always there so you don't have to remember
to add it, but I can't get that to work in CLI mode on Windows--
I get a 'Too late for "-T" option' error. :-(
AUTHOR / BUG REPORTS
JP Vossen (jp {at} jpsdomain {dot} org)
http://www.jpsdomain.org/
COPYRIGHT & LICENSE
$COPYRIGHT
$LICENSE
SEE ALSO
http://sourceforge.net/projects/passwordsafe/
EoN
print STDERR ("$USAGE"); # Print the usage
exit $exit_code; # exit with the specified error code
} # end of usage
# Declare everything to keep -w and use strict happy
our ($opt_u, $opt_l, $opt_n, $opt_p, $opt_P, $opt_a, $opt_A, $opt_x,
$opt_s, $opt_c, $opt_h, $opt_v, $opt_V);
our (@newpass, $HTMLBodyOptions);
use strict;
# For CGI use, set simple page formatting
my $HTML_bgcolor = '#BABDD3';
my $HTML_background = 'http://www.jpsdomain.org/images/blutxtr1.jpg';
##########################################################################
# Main
# Possible character sets
our %char_set = ('upper' => join("",'A' .. 'Z'),
'lower' => join("",'a' .. 'z'),
'numbers' => join("",'0' .. '9'),
'somepunct' => '%!@$^&*~#+=',
'allpunct' => '%!@$^&*~#+=()_`-[]{}\\|;":\',./?',
'hex' => join("", 0 .. 9, 'a' .. 'f', 'A' .. 'F')
);
# Selection of sets to actually use (options)
our %use = ('upper' => 0,
'lower' => 0,
'numbers' => 0,
'somepunct' => 0,
'allpunct' => 0,
'hex' => 0
);
# Default specifications for length and count
our %specs = ( 'size' => 20,
'count' => 10
);
if ($0 =~ m/\.cgi$/i) { # If my name ends in .cgi
RunAsCGI(); # Run as a CGI program
} elsif ($0 =~ m/\.pl$/i) {
RunAsScript(); # Run as a command line interface (CLI) script
} elsif ($^O eq "MSWin32") { # Oh no
die ("Script does not end in '.cgi' or '.pl' so I don't know what to do!\n");
}
# End of main
##########################################################################
# Subroutines
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Emit version and other information
# Called like: Version ({exit code})
sub Version {
my $exit_code = $_[0] || 1; # Default of 1 if exit code not specified
print ("$PROGRAM version $VERSION\n\t$COPYRIGHT\n\t$LICENSE\n");
exit $exit_code; # exit with the specified error code
} # end of sub Version
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Run as a command line interface (CLI) script
# Called like: RunAsScript()
sub RunAsScript {
my $newpass;
use Getopt::Std;
getopts('ulnpPaAxs:c:vV');
Usage(0) if $opt_h;
Version(0) if $opt_V;
# Set the options, first the detfault of -a if nothing was specified
if (not ($opt_a or $opt_A or $opt_x or $opt_u or $opt_l or $opt_n or $opt_p or $opt_P)) {
foreach (keys %use) { $use{$_} = 1; }
$use{"allpunct"} = 0;
$use{"hex"} = 0;
} elsif ($opt_a) { # -u, -l, -n, -p
foreach (keys %use) { $use{$_} = 1; }
$use{"allpunct"} = 0;
$use{"hex"} = 0;
} elsif ($opt_A) { # -u, -l, -n, -P
foreach (keys %use) { $use{$_} = 1; }
$use{"somepunct"} = 0;
$use{"hex"} = 0;
} elsif ($opt_x) { # -x
$use{"hex"} = 1;
} else { # ala cart
$use{"upper"} = 1 if $opt_u;
$use{"lower"} = 1 if $opt_l;
$use{"numbers"} = 1 if $opt_n;
$use{"somepunct"} = 1 if $opt_p;
$use{"allpunct"} = 1 if $opt_P;
} # end of option set
# Set the specs
$specs{"size"} = $opt_s if $opt_s;
$specs{"count"} = $opt_c if $opt_c;
&BuildArray; # Actually do the work
if ($opt_v) { # If we're being verbose
print STDERR ("$PROGRAM version $VERSION\n\t$COPYRIGHT\n\t$LICENSE\n\n");
foreach $newpass (@newpass) {
print ("Random password: $newpass\n");
}
} else {
foreach $newpass (@newpass) {
print ("$newpass\n");
}
} # end of if
if ($opt_v) { print STDERR ("\n\a$PROGRAM finished in ",time()-$^T," seconds.\n"); }
return 1;
} # end of sub RunAsScript
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Run as a CGI program
# Called like: RunAsCGI()
sub RunAsCGI {
my ($set, $newpass, @char_set_out, @char_set_in, @checked);
use CGI qw(:standard escapeHTML);
use CGI::Pretty qw(:standard); # Emit slighly less hidiously formatted HTML
# use CGI::Carp qw(fatalsToBrowser); # Sometimes helpful in debugging
# Load possible char sets into an array to use with CGI's checkbox_group
# Note the names of the options are the same as the values.
foreach $set (sort values %char_set) { push (@char_set_out, $set) };
# Create the default checked list
push (@checked, $char_set{"upper"});
push (@checked, $char_set{"lower"});
push (@checked, $char_set{"numbers"});
push (@checked, $char_set{"somepunct"});
# Generate an HTML form to get options
print header(), # Send the HTML header
start_html(-title=>"$PROGRAM $VERSION",
-bgcolor=>"$HTML_bgcolor", -background=>"$HTML_background");
# CGI_Diagnostics(); # Display table of server and client debug diags
print h1("Generate random passwords..."); # Header
# General version and copyright info
print ("$PROGRAM (source code)
");
print ("$VERSION
$COPYRIGHT");
print hr; # Horizontal rule
print start_form, # Start a form
("How many passwords (1-255)? ", textfield("HOW_MANY",$specs{"count"},4,3),"
"),
("How long should each password be (1-255)? ", textfield("SIZE",$specs{"size"},4,3),"
"), ("Hint: use the hex option with a length of 10 to generate random 40-bit WEP keys, or a length of 26 for 128-bit WEP keys.
"),
("Which character sets should be used (note that angle brackets are omitted due to issues with the resulting HTML code)?
"),
# This group of checkboxes gets both its label AND value from the @char_set array built above
("
"),checkbox_group("CHARSET",\@char_set_out,\@checked,'true'),(""), p(""), submit ("Generate Random Password(s)"); # Button to gen passwords print hr; # Horizontal rule # Overwrite the defaults with the form values, IF ANY $specs{"size"} = param("SIZE") if param("SIZE"); $specs{"count"} = param("HOW_MANY") if param("HOW_MANY"); @char_set_in = param("CHARSET") || @checked; foreach $set (@char_set_in) { # Flip all the flags # Since we're using checkboxes, you can and will have more than one choice!!! $use{"upper"} = 1 if ($set eq $char_set{"upper"}); $use{"lower"} = 1 if ($set eq $char_set{"lower"}); $use{"numbers"} = 1 if ($set eq $char_set{"numbers"}); $use{"somepunct"} = 1 if ($set eq $char_set{"somepunct"}); $use{"allpunct"} = 1 if ($set eq $char_set{"allpunct"}); } # end of foreach charset &BuildArray; # Actually do the work print h2 qq($specs{"count"} random password(s) of length $specs{"size"}:\n); # Header foreach $newpass (@newpass) { print ("${newpass}