add support for checking pbkdf2 passwords

This commit is contained in:
Nicola Murino
2019-08-17 15:20:49 +02:00
parent 9d342cb125
commit 133f2e8601
4 changed files with 70 additions and 6 deletions

View File

@@ -4,13 +4,21 @@
package dataprovider
import (
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"crypto/subtle"
"encoding/base64"
"errors"
"fmt"
"hash"
"path/filepath"
"strconv"
"strings"
"github.com/alexedwards/argon2id"
"golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/ssh"
"github.com/drakkan/sftpgo/logger"
@@ -30,6 +38,9 @@ const (
logSender = "dataProvider"
argonPwdPrefix = "$argon2id$"
bcryptPwdPrefix = "$2a$"
pbkdf2SHA1Prefix = "$pbkdf2-sha1$"
pbkdf2SHA256Prefix = "$pbkdf2-sha256$"
pbkdf2SHA512Prefix = "$pbkdf2-sha512$"
manageUsersDisabledError = "please set manage_users to 1 in sftpgo.conf to enable this method"
trackQuotaDisabledError = "please enable track_quota in sftpgo.conf to use this method"
)
@@ -42,6 +53,8 @@ var (
sqlPlaceholders []string
validPerms = []string{PermAny, PermListItems, PermDownload, PermUpload, PermDelete, PermRename,
PermCreateDirs, PermCreateSymlinks}
hashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix, pbkdf2SHA1Prefix, pbkdf2SHA256Prefix, pbkdf2SHA512Prefix}
pbkdfPwdPrefixes = []string{pbkdf2SHA1Prefix, pbkdf2SHA256Prefix, pbkdf2SHA512Prefix}
)
// Config provider configuration
@@ -237,8 +250,7 @@ func validateUser(user *User) error {
return &ValidationError{err: fmt.Sprintf("Invalid permission: %v", p)}
}
}
if len(user.Password) > 0 && !strings.HasPrefix(user.Password, argonPwdPrefix) &&
!strings.HasPrefix(user.Password, bcryptPwdPrefix) {
if len(user.Password) > 0 && !utils.IsStringPrefixInSlice(user.Password, hashPwdPrefixes) {
pwd, err := argon2id.CreateHash(user.Password, argon2id.DefaultParams)
if err != nil {
return err
@@ -272,6 +284,12 @@ func checkUserAndPass(user User, password string) (User, error) {
return user, err
}
match = true
} else if utils.IsStringPrefixInSlice(user.Password, pbkdfPwdPrefixes) {
match, err = comparePbkdf2PasswordAndHash(password, user.Password)
if err != nil {
logger.Warn(logSender, "error comparing password with pbkdf2 sha256 hash: %v", err)
return user, err
}
}
if !match {
err = errors.New("Invalid credentials")
@@ -296,6 +314,37 @@ func checkUserAndPubKey(user User, pubKey string) (User, error) {
return user, errors.New("Invalid credentials")
}
func comparePbkdf2PasswordAndHash(password, hashedPassword string) (bool, error) {
vals := strings.Split(hashedPassword, "$")
if len(vals) != 5 {
return false, fmt.Errorf("pbkdf2: hash is not in the correct format")
}
var hashFunc func() hash.Hash
var hashSize int
if strings.HasPrefix(hashedPassword, pbkdf2SHA256Prefix) {
hashSize = sha256.Size
hashFunc = sha256.New
} else if strings.HasPrefix(hashedPassword, pbkdf2SHA512Prefix) {
hashSize = sha512.Size
hashFunc = sha512.New
} else if strings.HasPrefix(hashedPassword, pbkdf2SHA1Prefix) {
hashSize = sha1.Size
hashFunc = sha1.New
} else {
return false, fmt.Errorf("pbkdf2: invalid or unsupported hash format %v", vals[1])
}
iterations, err := strconv.Atoi(vals[2])
if err != nil {
return false, err
}
salt := vals[3]
expected := vals[4]
df := pbkdf2.Key([]byte(password), []byte(salt), iterations, hashSize, hashFunc)
buf := make([]byte, base64.StdEncoding.EncodedLen(len(df)))
base64.StdEncoding.Encode(buf, df)
return subtle.ConstantTimeCompare(buf, []byte(expected)) == 1, nil
}
func getSSLMode() string {
if config.Driver == PGSSQLDataProviderName {
if config.SSLMode == 0 {