Merge pull request #3201 from influxdb/jw-salted-hash

Use salted hashes for in memory password cache
pull/3203/head
Jason Wilder 2015-06-30 17:10:32 -06:00
commit 656fc0d60b
1 changed files with 46 additions and 5 deletions

View File

@ -1,6 +1,9 @@
package meta
import (
"bytes"
crand "crypto/rand"
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
@ -28,6 +31,9 @@ import (
const (
MuxRaftHeader = 0
MuxExecHeader = 1
// SaltBytes is the number of bytes used for salts
SaltBytes = 32
)
// ExecMagic is the first 4 bytes sent to a remote exec connection to verify
@ -97,7 +103,7 @@ type Store struct {
CommitTimeout time.Duration
// Authentication cache.
authCache map[string]string
authCache map[string]authUser
// hashPassword generates a cryptographically secure hash for password.
// Returns an error if the password is invalid or a hash cannot be generated.
@ -106,6 +112,11 @@ type Store struct {
Logger *log.Logger
}
type authUser struct {
salt []byte
hash []byte
}
// NewStore returns a new instance of Store.
func NewStore(c Config) *Store {
return &Store{
@ -123,7 +134,7 @@ func NewStore(c Config) *Store {
ElectionTimeout: time.Duration(c.ElectionTimeout),
LeaderLeaseTimeout: time.Duration(c.LeaderLeaseTimeout),
CommitTimeout: time.Duration(c.CommitTimeout),
authCache: make(map[string]string, 0),
authCache: make(map[string]authUser, 0),
hashPassword: func(password string) ([]byte, error) {
return bcrypt.GenerateFromPassword([]byte(password), BcryptCost)
},
@ -1012,8 +1023,14 @@ func (s *Store) Authenticate(username, password string) (ui *UserInfo, err error
}
// Check the local auth cache first.
if p, ok := s.authCache[username]; ok {
if p == password {
if au, ok := s.authCache[username]; ok {
// verify the password using the cached salt and hash
hashed, err := s.hashWithSalt(au.salt, password)
if err != nil {
return err
}
if bytes.Equal(hashed, au.hash) {
ui = u
return nil
} else {
@ -1026,7 +1043,12 @@ func (s *Store) Authenticate(username, password string) (ui *UserInfo, err error
return ErrAuthenticate
}
s.authCache[username] = password
// generate a salt and hash of the password for the cache
salt, hashed, err := s.saltedHash(password)
if err != nil {
return err
}
s.authCache[username] = authUser{salt: salt, hash: hashed}
ui = u
return nil
@ -1034,6 +1056,25 @@ func (s *Store) Authenticate(username, password string) (ui *UserInfo, err error
return
}
// hashWithSalt returns a salted hash of password using salt
func (s *Store) hashWithSalt(salt []byte, password string) ([]byte, error) {
hasher := sha256.New()
hasher.Write(append(salt, []byte(password)...))
return hasher.Sum(nil), nil
}
// saltedHash returns a salt and salted hash of password
func (s *Store) saltedHash(password string) (salt, hash []byte, err error) {
salt = make([]byte, SaltBytes)
_, err = io.ReadFull(crand.Reader, salt)
if err != nil {
return
}
hash, err = s.hashWithSalt(salt, password)
return
}
// CreateUser creates a new user in the store.
func (s *Store) CreateUser(name, password string, admin bool) (*UserInfo, error) {
// Hash the password before serializing it.