Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,

I am trying to add a user using 'useradd $username -p $password'. At first it didn't work, because i was unaware that -p needed an encrypted password. So i went off and found out how to encrypt a password for the command, and came back with this:
my @chars = ('a'..'z', 'A'..'Z', 0..9); my $password = do {{ local $_ = join "" => map {$chars [rand @chars]} 1..8; redo unless /[a-z]/ && /[A-Z]/ && /\d/; $_; }}; my $salt = join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]; my $encrypted_pass = crypt($password, $salt);
This seemed to work ok, but i was alerted by a freind that this was not a very securly encrypted password. He told me to look at /etc/shadow and see the difference between an account created via the system, and one using my script.
system:$1$52060970$luRfo2g2xith2rfv8fWC31:12176:0:99999:7::: myscript:$1$52075700$:12176::::::
How can i make my passwords like the ones created by the system?

Replies are listed 'Best First'.
Re: Shadow Passwords
by Util (Priest) on May 03, 2003 at 17:16 UTC

    I would use Expect.pm to communicate with your system's passwd program, which always knows which kind of hash to use, and which may be PAM-enabled to update the password in more places that just /etc/shadow.

    Working example code, loosely tested (needs more error checking).

    #!/usr/bin/perl -w use strict; use Expect; my @user_pass_pairs = ( [ qw( Tarzan Ungowa ) ], [ qw( Jane GoneNative) ], ); # Time out if we don't get responses within 5 seconds. my $timeout = 5; foreach (@user_pass_pairs) { my ($user, $pass) = @$_; system('useradd', $username) == 0 or die; my $xp = Expect->spawn("passwd $user") or die; $xp->expect($timeout,"New UNIX password: ") or die; print $xp "$pass\r"; $xp->expect($timeout,"Retype new UNIX password: ") or die; print $xp "$pass\r"; $xp->expect( $timeout,"passwd: all authentication tokens updated successfully." ) or die; }

    In previous questions, some notable other suggestions were:

    Those previous questions were:

Re: Shadow Passwords
by dug (Chaplain) on May 03, 2003 at 16:00 UTC
    Howdy AM,
      crypt() in Perl encrypts a string exactly like crypt(3) in your C library. Consulting your crypt(3) manpage will probably show you that if crypt() is provided a salt that begins with the three characters '$1$' followed by at most 8 characters, it will use an MD5-based algorigth.

    Changing your code to:
    my @chars = ('a'..'z', 'A'..'Z', 0..9); my $password = do {{ local $_ = join "" => map {$chars [rand @chars]} 1..8; redo unless /[a-z]/ && /[A-Z]/ && /\d/; $_; }}; my $salt = '$1$' . join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[ map { int rand 64 } 0 .. 7 ]; my $encrypted_pass = crypt($password, $salt);
    should result in an MD5 passwd (depending on your systems crypt(3), of course).

    Hope that helps,

    -- dug
Re: Shadow Passwords
by halley (Prior) on May 03, 2003 at 15:32 UTC

    If you just want to add one user at a time as root, just separate the steps:

    # useradd joe # passwd joe New Password: *****

    But you're asking about the way to automate it.

    The /etc/shadow file takes multiple hashing formats for passwords. (Encryption is a misnomer; encryption can be reversed but hashing cannot.) There's crypt() as you've tried, and there's MD5 and a few other hashing functions. The system knows the difference based on the first couple characters. (You didn't cut and paste your shadow example literally, since your crypt() function would not have begun with a '$1'.)

    Although you can get such hashes from the command line:

    # openssl passwd -salt '$1' 'mypassword' $1XahR1gy5QBc # openssl passwd -apr1 -salt '$1' 'mypasswd' $apr1$$1$WU93LWav20QAwX/j3i0CW/

    I hope other monks can discuss what's necessary for a script to use a module (maybe Digest::MD5) to properly hash and encode the type and hash for /etc/shadow.

    --
    [ e d @ h a l l e y . c c ]

      The Linux passwd supports the --stdin option to read the password from stdin. This should be easy to automate with Perl. Using passwd means that PAM handles changing the password and it knows the current configuration of shadow, MD5, etc.
      system("useradd $user"); open PASSWD, "| passwd --stdin $user"; print PASSWD $password; close PASSWD;
Re: Shadow Passwords
by mr_mischief (Monsignor) on May 03, 2003 at 16:16 UTC
    This has nothing to do with whether your passwords are shadowed or not. It also has little to do with automation vs. doing it by hand.

    The traditional crypt() function is 3DES. The current de facto standard is MD5. Some crypt functions do one, and some do the other.

    GNU extension
    On Linux in particular, and perhaps elsewhere too, the system crypt() call uses 3DES if the salt does not begin with "$1$" and MD5 if it does.

    3DES takes a two-character salt. MD5 typically takes up to eight after the "$1$". Try using more salt characters if your salt is not long enough, and check to see if you must use "$1$" on your system as a prefix to the actual salt characters.

    Christopher E. Stith
    use coffee;
Re: Shadow Passwords
by fokat (Deacon) on May 03, 2003 at 16:36 UTC

    Try using Crypt::Passwd or Crypt::PasswdMD5 to generate the hashed password. The salt bytes are *very* important, as these protect your passwords from pre-computed hash lists. Use random bytes for these (Math::TrulyRandom comes to mind).

    Best regards

    -lem, but some call me fokat

      is it possible to do this then:
      my $epassword = `openssl passwd -apr1 -salt '$1' '$password'`; !system ('useradd', '-g', 'users', '-d', "/home/$username", '-p', $epassword, '-s', '/bin/false', '-c', $fullname, $username) or die "Creation of User Failed";

        I would seriously consider Util's (++) suggestion about using Expect. His point about PAM and hashing algorythm is worthy of examination, IMHO. This also makes the solution more portable, as generally passwd has a very similar behavior among systems. This is not true for useradd or adduser.

        Best regards

        -lem, but some call me fokat

Re: Shadow Passwords
by halley (Prior) on May 03, 2003 at 15:40 UTC

    As far as security goes, the type of hash is not as important as selecting good passwords. Fully random noise is not as good as you might think.

    Your script generates random passwords, and that's useful for the initial user password. However, they should be required to change it to something of their choice immediately for two reasons: (1) they won't remember something random you gave them without writing it down, and (2) you absolve some theoretical liability for breakins after they change it from your password to theirs.

    --
    [ e d @ h a l l e y . c c ]

Re: Shadow Passwords
by richardX (Pilgrim) on May 04, 2003 at 04:37 UTC
    I found this code at htpasswd.pl that describes a way of encrypting the salt.
    # Random seed from p.223 of _Programming Perl_ (O'Reilly & Assoc) srand(time() ^ ($$ + ($$ << 15)) ); @saltchars=(a..z,A..Z,0..9,'.','/'); # valid salt chars $salt=$saltchars[int(rand($#saltchars+1))]; # first random salt cha +r $salt.=$saltchars[int(rand($#saltchars+1))]; # second random salt c +har $cpw = crypt($pass,$salt);

    If you want to create more than two characters for the salt, then repeat the random salt char line

    Richard

    There are three types of people in this world, those that can count and those that cannot. Anon