loginsrv

Unnamed repository; edit this file 'description' to name the repository.
git clone git@jamesshield.xyz:repos/loginsrv.git
Log | Files | Refs | README | LICENSE

commit d234785855cebab7a81b76fba046ac5c473b1291
parent 7b94209dd48880be6f9bfc07466f00ecd384c40a
Author: Sebastian Mancke <s.mancke@tarent.de>
Date:   Tue, 22 Nov 2016 12:22:32 +0100

added support for sha1 and md5 in htpasswd

Diffstat:
Mhtpasswd/auth.go | 33+++++++++++++++++++++++++++++++--
Mhtpasswd/auth_test.go | 15++++++++++++---
2 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/htpasswd/auth.go b/htpasswd/auth.go @@ -1,8 +1,13 @@ package htpasswd import ( + "bytes" + "crypto/sha1" + "crypto/subtle" + "encoding/base64" "encoding/csv" "fmt" + "github.com/abbot/go-http-auth" "golang.org/x/crypto/bcrypt" "io" "os" @@ -50,11 +55,35 @@ func (a *Auth) parse(filename string) error { func (a *Auth) Authenticate(username, password string) (bool, error) { if hash, exist := a.userHash[username]; exist { - if strings.HasPrefix(hash, "$2y$") { - matchErr := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + h := []byte(hash) + p := []byte(password) + if strings.HasPrefix(hash, "$2y$") || strings.HasPrefix(hash, "$2b$") { + matchErr := bcrypt.CompareHashAndPassword(h, p) return (matchErr == nil), nil } + if strings.HasPrefix(hash, "{SHA}") { + return compareSha(h, p), nil + } + if strings.HasPrefix(hash, "$apr1$") { + return compareMD5(h, p), nil + } return false, fmt.Errorf("unknown algorythm for user %q", username) } return false, nil } + +func compareSha(hashedPassword, password []byte) bool { + d := sha1.New() + d.Write(password) + return 1 == subtle.ConstantTimeCompare(hashedPassword[5:], []byte(base64.StdEncoding.EncodeToString(d.Sum(nil)))) +} + +func compareMD5(hashedPassword, password []byte) bool { + parts := bytes.SplitN(hashedPassword, []byte("$"), 4) + if len(parts) != 4 { + return false + } + magic := []byte("$" + string(parts[1]) + "$") + salt := parts[2] + return 1 == subtle.ConstantTimeCompare(hashedPassword, auth.MD5Crypt(password, salt, magic)) +} diff --git a/htpasswd/auth_test.go b/htpasswd/auth_test.go @@ -10,7 +10,7 @@ import ( const testfile = `bob-md5:$apr1$IDZSCL/o$N68zaFDDRivjour94OVeB. bob-bcrypt:$2y$05$Hw6y1sFwh6CdwiPOKFMYj..xVSQWI3wzyQvt5th392ig8RLmeLU.6 -bob-sha:{SHA}5en6G6MezRroT3XKqkdPOmY/BfQ= # a comment +bob-sha:{SHA}5en6G6MezRroT3XKqkdPOmY/BfQ= # a comment bob-foo:{fooo}sdcsdcsdc/BfQ= @@ -22,8 +22,7 @@ func TestClient_Hashes(t *testing.T) { auth, err := NewAuth(writeTmpfile(testfile)) assert.NoError(t, err) - //testUsers := []string{"bob-md5", "bob-bcrypt", "bob-sha"} - testUsers := []string{"bob-bcrypt"} + testUsers := []string{"bob-md5", "bob-bcrypt", "bob-sha"} for _, name := range testUsers { t.Run(name, func(t *testing.T) { authenticated, err := auth.Authenticate(name, "secret") @@ -59,6 +58,16 @@ func TestClient_ErrorOnInvalidFileContents(t *testing.T) { assert.Error(t, err) } +func TestClient_BadMD5Format(t *testing.T) { + // missing $ separator in md5 hash + a, err := NewAuth(writeTmpfile("foo:$apr1$IDZSCL/oN68zaFDDRivjour94OVeB.")) + assert.NoError(t, err) + + authenticated, err := a.Authenticate("foo", "secret") + assert.NoError(t, err) + assert.False(t, authenticated) +} + func TestClient_Hashes_UnknownAlgoError(t *testing.T) { auth, err := NewAuth(writeTmpfile(testfile)) assert.NoError(t, err)