|
1 | 1 | # frozen_string_literal: true |
| 2 | +require 'openssl' |
| 3 | +require 'base64' |
2 | 4 |
|
3 | 5 | # @summary This function returns the postgresql password hash from the clear text username / password |
4 | 6 | Puppet::Functions.create_function(:'postgresql::postgresql_password') do |
|
8 | 10 | # The clear text `password` |
9 | 11 | # @param sensitive |
10 | 12 | # If the Postgresql-Passwordhash should be of Datatype Sensitive[String] |
| 13 | + # @param hash |
| 14 | + # Set type for password hash |
| 15 | + # @param salt |
| 16 | + # Use a specific salt value for scram-sha-256, default is username |
11 | 17 | # |
12 | 18 | # @return |
13 | 19 | # The postgresql password hash from the clear text username / password. |
14 | 20 | dispatch :default_impl do |
15 | 21 | required_param 'Variant[String[1], Integer]', :username |
16 | 22 | required_param 'Variant[String[1], Sensitive[String[1]], Integer]', :password |
17 | 23 | optional_param 'Boolean', :sensitive |
| 24 | + optional_param "Optional[Enum['md5', 'scram-sha-256']]", :hash |
| 25 | + optional_param 'Optional[Variant[String[1], Integer]]', :salt |
18 | 26 | return_type 'Variant[String, Sensitive[String]]' |
19 | 27 | end |
20 | 28 |
|
21 | | - def default_impl(username, password, sensitive = false) |
| 29 | + def default_impl(username, password, sensitive = false, hash = 'md5', salt = nil) |
22 | 30 | password = password.unwrap if password.respond_to?(:unwrap) |
23 | | - result_string = 'md5' + Digest::MD5.hexdigest(password.to_s + username.to_s) |
| 31 | + pass = if hash == 'md5' |
| 32 | + 'md5' + Digest::MD5.hexdigest(password.to_s + username.to_s) |
| 33 | + else |
| 34 | + pg_sha256(password, (salt || username)) |
| 35 | + end |
24 | 36 | if sensitive |
25 | | - Puppet::Pops::Types::PSensitiveType::Sensitive.new(result_string) |
| 37 | + Puppet::Pops::Types::PSensitiveType::Sensitive.new(pass) |
26 | 38 | else |
27 | | - result_string |
| 39 | + pass |
28 | 40 | end |
29 | 41 | end |
| 42 | + |
| 43 | + def pg_sha256(password, salt) |
| 44 | + digest = digest_key(password, salt) |
| 45 | + 'SCRAM-SHA-256$%s:%s$%s:%s' % [ |
| 46 | + '4096', |
| 47 | + Base64.strict_encode64(salt), |
| 48 | + Base64.strict_encode64(client_key(digest)), |
| 49 | + Base64.strict_encode64(server_key(digest)) |
| 50 | + ] |
| 51 | + end |
| 52 | + |
| 53 | + def digest_key(password, salt) |
| 54 | + OpenSSL::KDF.pbkdf2_hmac( |
| 55 | + password, |
| 56 | + salt: salt, |
| 57 | + iterations: 4096, |
| 58 | + length: 32, |
| 59 | + hash: OpenSSL::Digest::SHA256.new |
| 60 | + ) |
| 61 | + end |
| 62 | + |
| 63 | + def client_key(digest_key) |
| 64 | + hmac = OpenSSL::HMAC.new(digest_key, OpenSSL::Digest::SHA256.new) |
| 65 | + hmac << 'Client Key' |
| 66 | + hmac.digest |
| 67 | + OpenSSL::Digest.new('SHA256').digest hmac.digest |
| 68 | + end |
| 69 | + |
| 70 | + def server_key(digest_key) |
| 71 | + hmac = OpenSSL::HMAC.new(digest_key, OpenSSL::Digest::SHA256.new) |
| 72 | + hmac << 'Server Key' |
| 73 | + hmac.digest |
| 74 | + end |
30 | 75 | end |
0 commit comments