Quick *nix shadow passwords with Ruby

by Kenneth Kalmer on May 1, 2009

Just thought I’d share this one to boost the available online information. Using String#crypt and man crypt you’ll come up with something similar to the gist below (extract from a project I’m busy working on).

module Linux
  class User
    class << self
      # Generate an MD5 salt string
      def salt
        seeds = ('a'..'z').to_a
        seeds.concat( ('A'..'Z').to_a )
        seeds.concat( (0..9).to_a )
        seeds.concat ['/', '.']
        seeds.compact!
 
        salt_string = '$1$'
        8.times { salt_string << seeds[ rand(seeds.size) ].to_s }
 
        salt_string
      end
 
      # Crypt a password suitable for use in shadow files
      def crypt( string )
        string.crypt( self.salt )
      end
    end
  end
end

And the spec

require File.dirname(__FILE__) + '/../spec_helper'
 
describe Linux::User do
  describe "generating shadow passwords" do
    it "should generate a salt for crypt" do
      salt = Linux::User.salt
      salt.length.should be(11)
      salt.should match(/^\$1\$[a-zA-Z0-9\.\/]{8}$/)
    end
 
    it "should generate a shadow password" do
      pass = Linux::User.crypt( 'secret' )
      pass.should match(/^\$1\$[a-zA-Z0-9\.\/]{8}\$[a-zA-Z0-9\.\/]{22}$/)
      pass.length.should be(34)
    end
  end
end

HTH

3 comments

from the doc:
“Applies a one-way cryptographic hash to str by invoking the standard library function crypt. The argument is the salt string, which should be two characters long, each character drawn from [a-zA-Z0-9./].”

so only the first 2 chars will be used

>> “secret”.crypt “ab”
=> “abNANd1rDfiNc”
>> “secret”.crypt “abcd”
=> “abNANd1rDfiNc”
>> “secret”.crypt “abcdefgh”
=> “abNANd1rDfiNc”

by Brian Demant on May 1, 2009 at 3:32 pm. #

@Brian yes, and no. Ruby uses the underlying linux crypt function (see man crypt). By default it uses a DES encryption scheme which does exactly what you said, but with glibc2 on the host things change. From the man page: If salt is a character string starting with the characters “$id$” followed by a string terminated by “$”: $id$salt$encrypted then instead of using the DES machine, id identifies the encryption method used and this then determines how the rest of the password string is interpreted.

=> “abNANd1rDfiNc”
irb(main):002:0> “secret”.crypt(“abasasa”)
=> “abNANd1rDfiNc”
irb(main):003:0> “secret”.crypt(“$1$abasasa”)
=> “$1$abasasa$2RZY2vd6E2ZEPSDa0eLec0″
irb(main):004:0> “secret”.crypt(“$1$abasa”)
=> “$1$abasa$ikoKICgwOFdcWgmDl9Asy1″

You can clearly see how the behavior of the crypt method changes in the latter calls. The Ruby rdoc’s are confusing in this regard.

by Kenneth Kalmer on May 1, 2009 at 3:40 pm. #

Thanks for this and the comment explaining the way crypt works with different formats of salts. That’s trick!

Here’s my version: http://d.strelau.net/post/220354423/shadow-passwords-in-ruby

by Dean Strelau on October 23, 2009 at 12:49 am. #

Leave your comment

Required.

Required. Not published.

If you have one.