#!/usr/bin/perl # # crypt8 # # AUTHOR: # Dan Harkless # # COPYRIGHT: # This file is Copyright (C) 2008 by Dan Harkless, and is released under the # GNU General Public License . # # USAGE: # % crypt8 [-s ] # does not accept string to crypt on command line # # DESCRIPTION: # Prompts (on /dev/tty) for a string to run through crypt(). Prompts again # for verification. If the two entries match, the crypt()ed result is printed # to stdout. # # Note this is the algorithm used to encrypt passwords, not the easily # breakable algorithm used by the crypt(1) command. Also note that only the # first 8 characters in the string are significant (thus the name crypt8). # # This script can come in handy if you ever need to change a password, but you # can't (or don't want to) use the passwd(1) command. Reasons this might be # the case include: # # 1. passwd program has accidentally been deleted. # 2. passwd program has maliciously been deleted. # 3. Hackers have broken on to the system and you fear they may have # installed a booby-trapped passwd program. # 4. Hackers have broken on to the system and you fear they may be # monitoring your keystrokes. # 5. A libc problem is causing passwd to core-dump (this happened on our # Solaris 2.5 systems, prompting me to write this script). # # If one of these scenarios (or any other one) occurs, no sweat. Just enter # the new password into this script and cut & paste the encrypted result into # the /etc/passwd file (or shadow file, if you have one). You can also get # more fancy and use I/O redirection, as the encrypted string is the only # thing this script prints on stdout. Note that if you are trying to change a # password on a system that's been compromised, you should run this script on # a system believed to be secure, just in case keystroke monitoring _is_ going # on. # # You may provide a two-character salt string with the -s parameter (be sure # you only use characters [./0-9A-Za-z]). If you don't provide one, a random # one will be chosen, using the time you wait between prompts as a (somewhat # half-assed) entropy source. # # DATE MODIFICATION # ========== ================================================================== # 2008-09-02 "use English qw(-no_match_vars)": avoid regex performance penalty. # 2000-04-21 Allow specification of a specific salt with the new -s option. # 1999-05-28 Original. ## Modules used ################################################################ use English qw(-no_match_vars); # allow use of names like @ARG rather than @_ use File::Basename; # for basename() use POSIX; # for POSIX::times() ## Global constants ############################################################ $salt_chars="./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; ## Subroutines ################################################################# sub crypt8_die(@) { system "stty echo"; # restore terminal echoing die "$progname: ", @ARG, ". Aborting.\n"; } sub illegal_salt($) { if (substr(@ARG[0], 0, 1) =~ m<[./0-9A-Za-z]> and substr(@ARG[0], 1, 1) =~ m<[./0-9A-Za-z]>) { return 0; } else { return 1; } } sub signal_handler($) { crypt8_die("\nCaught SIG", shift); } ## Main ######################################################################## $timestamp1 = (POSIX::times)[0]; $progname = basename($PROGRAM_NAME); if (@ARGV != 0 and @ARGV != 2) { print STDERR <] You will be prompted for the string to be encrypted. If the string you gave on the commandline to get this message was a planned password, you should think of a new one, since someone may have seen it with the 'ps' command. HERE_DOCUMENT_EOF exit 1; } else { # We're about to turn off terminal echoing, so set it up so that we'll turn # it back on if the user CTRL-Cs. This isn't necessary for smart shells # like tcsh, but it is for dumb shells like sh. Technically we should catch # other signals besides SIGINT, but the list of fatal signals is different # for each UNIX, and SIGINT is the only one we're likely to receive. $SIG{INT} = \&signal_handler; # Turn off terminal echoing. Unfortunately, this appears to fail if you've # redirected stdin, not that you'd have any reason to do that. The CPAN # Curses and ReadKey modules provide more robust methods of turning off # echoing, but as I said, they're CPAN modules -- they're not distributed # with perl. system "stty -echo"; # Open the terminal. Need to have two separate handles to preserve -echo. open(TTY_IN, ">/dev/tty") or crypt8_die "Can't open /dev/tty for writing"; if (@ARGV[0] ne "-s") { print TTY_OUT "Please wait a bit before typing at each prompt below to", " improve salt randomness.\n\n"; } elsif (length @ARGV[1] != 2 or illegal_salt(@ARGV[1])) { crypt8_die "Salt must be two characters from the range", " [./0-9A-Za-z]."; } print TTY_OUT "String to crypt() (only first 8 chars are significant): "; chomp($input1 = ); if (@ARGV[0] ne "-s") { $timestamp2 = (POSIX::times)[0]; $salt_char_1_index = ($timestamp2 - $timestamp1) % 64; # 64 salt chars. } print TTY_OUT "\nPlease enter the string one more time for verification: "; chomp($input2 = ); print TTY_OUT "\n\n"; if ($input1 ne $input2) { crypt8_die "Second entry did not match the first"; } else { if (@ARGV[0] eq "-s") { $salt = @ARGV[1]; } else { $timestamp3 = (POSIX::times)[0]; $salt_char_2_index = ($timestamp3-$timestamp2) % 64; # 64 salt chars $salt = substr($salt_chars, $salt_char_1_index, 1) . substr($salt_chars, $salt_char_2_index, 1); } print crypt($input2, $salt), "\n"; } system "stty echo"; # restore terminal echoing }