Puppet Function: pw_hash

Defined in:
vendor_modules/stdlib/lib/puppet/parser/functions/pw_hash.rb
Function type:
Ruby 3.x API

Summary

Hashes a password using the crypt function. Provides a hash usable on most POSIX systems.

Overview

pw_hash()String

The first argument to this function is the password to hash. If it is undef or an empty string, this function returns undef.

The second argument to this function is which hash algorithm to use. It will be converted into the appropriate crypt(3) hash specifier. Valid hash types are:

|Hash type|Prefix|Note | |———|——|———————| |MD5 |1 | | |SHA-256 |5 | | |SHA-512 |6 |Recommended | |bcrypt |2b | | |bcrypt-a |2a |bug compatible | |bcrypt-x |2x |bug compatible | |bcrypt-y |2y |historic alias for 2b|

The third argument to this function is the salt to use. For bcrypt-type hashes, the first two characters of the salt represent a strength parameter, with a value between 4 and 31 inclusive.

> Note:: this uses the Puppet Server's implementation of crypt(3). If your

environment contains several different operating systems, ensure that they
are compatible before using this function.

Returns:

  • (String)

    Provides a crypt hash usable on most POSIX systems.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'vendor_modules/stdlib/lib/puppet/parser/functions/pw_hash.rb', line 6

Puppet::Parser::Functions.newfunction(:pw_hash, type: :rvalue, arity: 3, doc: <<-DOC
  @summary
    Hashes a password using the crypt function. Provides a hash usable
    on most POSIX systems.

  The first argument to this function is the password to hash. If it is
  undef or an empty string, this function returns undef.

  The second argument to this function is which hash algorithm to use. It
  will be converted into the appropriate crypt(3) hash specifier. Valid
  hash types are:

  |Hash type|Prefix|Note                 |
  |---------|------|---------------------|
  |MD5      |1     |                     |
  |SHA-256  |5     |                     |
  |SHA-512  |6     |Recommended          |
  |bcrypt   |2b    |                     |
  |bcrypt-a |2a    |bug compatible       |
  |bcrypt-x |2x    |bug compatible       |
  |bcrypt-y |2y    |historic alias for 2b|

  The third argument to this function is the salt to use. For bcrypt-type hashes,
  the first two characters of the salt represent a strength parameter, with a value
  between 4 and 31 inclusive.

  @return [String]
    Provides a crypt hash usable on most POSIX systems.

  > *Note:*: this uses the Puppet Server's implementation of crypt(3). If your
    environment contains several different operating systems, ensure that they
    are compatible before using this function.
  DOC
) do |args|
  raise ArgumentError, "pw_hash(): wrong number of arguments (#{args.size} for 3)" if args.size != 3
  args.map! do |arg|
    if (defined? Puppet::Pops::Types::PSensitiveType::Sensitive) && (arg.is_a? Puppet::Pops::Types::PSensitiveType::Sensitive)
      arg.unwrap
    else
      arg
    end
  end

  hashes = {
    'md5'       => { prefix: '1' },
    'sha-256'   => { prefix: '5' },
    'sha-512'   => { prefix: '6' },
    'bcrypt'    => { prefix: '2b', salt: %r{^(0[4-9]|[12][0-9]|3[01])\$[./A-Za-z0-9]{22}} },
    'bcrypt-a'  => { prefix: '2a', salt: %r{^(0[4-9]|[12][0-9]|3[01])\$[./A-Za-z0-9]{22}} },
    'bcrypt-x'  => { prefix: '2x', salt: %r{^(0[4-9]|[12][0-9]|3[01])\$[./A-Za-z0-9]{22}} },
    'bcrypt-y'  => { prefix: '2y', salt: %r{^(0[4-9]|[12][0-9]|3[01])\$[./A-Za-z0-9]{22}} },
  }

  raise ArgumentError, 'pw_hash(): first argument must be a string' unless args[0].is_a?(String) || args[0].nil?
  raise ArgumentError, 'pw_hash(): second argument must be a string' unless args[1].is_a? String
  hash_type = hashes[args[1].downcase]
  raise ArgumentError, "pw_hash(): #{args[1]} is not a valid hash type" if hash_type.nil?
  raise ArgumentError, 'pw_hash(): third argument must be a string' unless args[2].is_a? String
  raise ArgumentError, 'pw_hash(): third argument must not be empty' if args[2].empty?
  salt_doc = hash_type.include?(:salt) ? "match #{hash_type[:salt]}" : 'be in the set [a-zA-Z0-9./]'
  salt_regex = hash_type.fetch(:salt, %r{\A[a-zA-Z0-9./]+\z})
  raise ArgumentError, "pw_hash(): characters in salt must #{salt_doc}" unless salt_regex.match?(args[2])

  password = args[0]
  return nil if password.nil? || password.empty?

  salt = "$#{hash_type[:prefix]}$#{args[2]}"

  # handle weak implementations of String#crypt
  # dup the string to get rid of frozen status for testing
  if ('test'.dup).crypt('$1$1') != '$1$1$Bp8CU9Oujr9SSEw53WV6G.'
    # JRuby < 1.7.17
    # MS Windows and other systems that don't support enhanced salts
    raise Puppet::ParseError, 'system does not support enhanced salts' unless RUBY_PLATFORM == 'java'
    # puppetserver bundles Apache Commons Codec
    org.apache.commons.codec.digest.Crypt.crypt(password.to_java_bytes, salt)
  else
    password.crypt(salt)
  end
end