Vault-12308: Change password policy testing to be deterministic (#20625)
* change testing password policy to be deterministic * fix panic * test password against rules * improve error message * make test password gen more random * fix check on test password length
This commit is contained in:
parent
722c578ff4
commit
11f9603b37
|
@ -70,7 +70,7 @@ func sortCharset(chars string) string {
|
||||||
return string(r)
|
return string(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringGenerator generats random strings from the provided charset & adhering to a set of rules. The set of rules
|
// StringGenerator generates random strings from the provided charset & adhering to a set of rules. The set of rules
|
||||||
// are things like CharsetRule which requires a certain number of characters from a sub-charset.
|
// are things like CharsetRule which requires a certain number of characters from a sub-charset.
|
||||||
type StringGenerator struct {
|
type StringGenerator struct {
|
||||||
// Length of the string to generate.
|
// Length of the string to generate.
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -3019,28 +3020,44 @@ func (*SystemBackend) handlePoliciesPasswordSet(ctx context.Context, req *logica
|
||||||
fmt.Sprintf("passwords must be between %d and %d characters", minPasswordLength, maxPasswordLength))
|
fmt.Sprintf("passwords must be between %d and %d characters", minPasswordLength, maxPasswordLength))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate some passwords to ensure that we're confident that the policy isn't impossible
|
// Attempt to construct a test password from the rules to ensure that the policy isn't impossible
|
||||||
timeout := 1 * time.Second
|
var testPassword []rune
|
||||||
genCtx, cancel := context.WithTimeout(ctx, timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
attempts := 10
|
for _, rule := range policy.Rules {
|
||||||
failed := 0
|
charsetRule, ok := rule.(random.CharsetRule)
|
||||||
for i := 0; i < attempts; i++ {
|
if !ok {
|
||||||
_, err = policy.Generate(genCtx, nil)
|
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("unexpected rule type %T", charsetRule))
|
||||||
if err != nil {
|
}
|
||||||
failed++
|
|
||||||
|
for j := 0; j < charsetRule.MinLength(); j++ {
|
||||||
|
charIndex := rand.Intn(len(charsetRule.Chars()))
|
||||||
|
testPassword = append(testPassword, charsetRule.Chars()[charIndex])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if failed == attempts {
|
for i := len(testPassword); i < policy.Length; i++ {
|
||||||
return nil, logical.CodedError(http.StatusBadRequest,
|
for _, rule := range policy.Rules {
|
||||||
fmt.Sprintf("unable to generate password from provided policy in %s: are the rules impossible?", timeout))
|
if len(testPassword) >= policy.Length {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
charsetRule, ok := rule.(random.CharsetRule)
|
||||||
|
if !ok {
|
||||||
|
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("unexpected rule type %T", charsetRule))
|
||||||
|
}
|
||||||
|
|
||||||
|
charIndex := rand.Intn(len(charsetRule.Chars()))
|
||||||
|
testPassword = append(testPassword, charsetRule.Chars()[charIndex])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if failed > 0 {
|
rand.Shuffle(policy.Length, func(i, j int) {
|
||||||
return nil, logical.CodedError(http.StatusBadRequest,
|
testPassword[i], testPassword[j] = testPassword[j], testPassword[i]
|
||||||
fmt.Sprintf("failed to generate passwords %d times out of %d attempts in %s - is the policy too restrictive?", failed, attempts, timeout))
|
})
|
||||||
|
|
||||||
|
for _, rule := range policy.Rules {
|
||||||
|
if !rule.Pass(testPassword) {
|
||||||
|
return nil, logical.CodedError(http.StatusBadRequest, "unable to construct test password from provided policy: are the rules impossible?")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := passwordPolicyConfig{
|
cfg := passwordPolicyConfig{
|
||||||
|
|
Loading…
Reference in New Issue