Quick *nix shadow passwords with Ruby

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

flattr this!

  • http://www.briandemant.dk Brian Demant

    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”

  • Kenneth Kalmer

    @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.

  • http://d.strelau.net/ Dean Strelau

    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