mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-08 07:10:56 +03:00
add support for checking pbkdf2 passwords
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user