#!/usr/bin/env perl # mkpass --- prompt for cleartext password and echo crypted result # Copyright (C) 1993, 1994, 1996, 1997, 2005 Noah S. Friedman # Author: Noah Friedman # Created: 1993-09-26 # $Id: mkpass,v 1.7 2006/01/13 22:19:06 friedman Exp $ # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, you can either send email to this # program's maintainer or write to: The Free Software Foundation, # Inc.; 51 Franklin Street, Fifth Floor; Boston, MA 02110-1301, USA. # Commentary: # Code: use strict; use Getopt::Long; use POSIX; $^W = 1; # enable warnings my $progname; my $salt; my $method; my $interactivep; my %crypt_fn = ( des => \&crypt_des, md5 => \&crypt_md5 ); sub usage { print "Usage: $progname {options} {cleartext} Options are: -D, --debug Enable debugging. -h, --help You're looking at it. -i, --interactive Prompt for input and confirmation even if standard input is not a terminal. -s, --salt SALT Use SALT to encode cleartext. Default is to generate a random sequence. SALT should be a 2-letter sequence for DES style encryption, or an 8-letter sequence optionally prefixed by \`\$1\$' for MD5 style. -m, --method [md5|des] --md5 Choose an encryption algorithm. --des Default depends on supplied SALT, or \`des' if no salt is supplied. If cleartext is not specified on the command line, it will be prompted for interactively. Specifying the cleartext on the command line is less secure since someone may be able to see it with the \`ps' command. You can also feed the cleartext password as input using a pipe.\n"; exit (1); } sub parse_options { ($progname = $0) =~ s|.*/||; $interactivep = -t STDIN; Getopt::Long::config ('bundling', 'autoabbrev'); GetOptions ("s|salt=s", \$salt, "m|method=s", \$method, "md5", sub { $method = 'md5' }, "des", sub { $method = 'des' }, "i|interactive", \$interactivep, "h|help", \&usage); $method = (defined $salt ? (length ($salt) < 3 ? 'des' : 'md5') : 'des') unless defined $method; unless (exists $crypt_fn{$method}) { print STDERR "$progname: $method: Unknown encryption method.\n"; exit (1); } } sub input_noecho { my ($prompt) = @_; my $tty; my $c_lflag; my %trap_sigs = ( HUP => 1, INT => 2, QUIT => 3, TERM => 15); my %sig_orig; # If stdin is a tty, disable echo while reading password. if (-t STDIN) { $tty = POSIX::Termios->new; my $fd = fileno (STDIN); $tty->getattr ($fd); $c_lflag = $tty->getlflag; # Set up handlers to restore tty on typical signals my $restore = sub { $tty->setlflag ($c_lflag); $tty->setattr (fileno (STDIN)); my $signum = $trap_sigs{$_[0]}; print STDERR "\nExiting on signal $signum (SIG$_[0])\n"; # 7th bit set indicates lower 6 bits represent a # signal number (0x80 == 2**7) exit (0x80 | $signum); }; map { $sig_orig{$_} = $SIG{$_} || 'DEFAULT'; $SIG{$_} = $restore } keys %trap_sigs; $tty->setlflag ($c_lflag & ~&POSIX::ECHO); $tty->setattr ($fd); } # Temporarily disable buffering on stderr, which is where prompt is printed. my $fh_orig = select (STDERR); my $stderr_bufp = $|; $| = 1; $prompt = "Password:" unless defined $prompt; print $prompt; my $input = ; chomp $input if defined $input; $| = $stderr_bufp; select ($fh_orig); # Restore echo afterward, if it was originally on; # and restore signal handlers print STDERR "\n" if $interactivep; if ($tty) { $tty->setlflag ($c_lflag); $tty->setattr (fileno (STDIN)); map { $SIG{$_} = $sig_orig{$_} } keys %trap_sigs; } return $input; } sub gensalt { my $saltlen = shift; my $sc = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; my $sclen = length ($sc) + 1; join ("", map { substr ($sc, int (rand () * 100000) % $sclen, 1) } (0 .. $saltlen)); } sub crypt_des { my ($pass, $salt) = @_; $salt = gensalt (2) unless defined $salt; $salt =~ s/^\$1\$//; # strip md5 tag if using des return crypt ($pass, $salt); } sub crypt_md5 { my ($pass, $salt) = @_; if (! defined $salt) { $salt = '$1$' . gensalt (8); } elsif ($salt !~ /^\$1\$/) { $salt = '$1$' . $salt; } my $result = crypt ($pass, $salt); if (length $result == 13 && substr ($result, 0, 3) ne '$1$') { print STDERR "$progname: warning: system does not seem to support", " MD5 hashes; using DES instead.\n"; return crypt_des (@_); } return $result; } sub main { parse_options; my $pass; if (defined $ARGV[0]) { $pass = shift @ARGV; } elsif ($interactivep) { while (1) { $pass = input_noecho ("Password:"); my $pass2 = input_noecho ("Confirm password:"); exit (1) unless defined $pass2; last if $pass eq $pass2; print STDERR "Input mismatch; please try again.\n\n"; } } else { $pass = ; exit (1) unless defined $pass; chomp $pass; } my $crypt = $crypt_fn{$method}; print &$crypt ($pass, $salt), "\n"; } main (@ARGV); # mkpass ends here