added support for verifying sha256/sha512 passwords hash
this simplifies the migration of users from some proprietary products Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
48939b2b4f
commit
74e5999c63
3 changed files with 32 additions and 9 deletions
|
@ -585,7 +585,9 @@ Supported hash algorithms:
|
||||||
- MD5 crypt APR1, prefix `$apr1$`
|
- MD5 crypt APR1, prefix `$apr1$`
|
||||||
- SHA256 crypt, prefix `$5$`
|
- SHA256 crypt, prefix `$5$`
|
||||||
- SHA512 crypt, prefix `$6$`
|
- SHA512 crypt, prefix `$6$`
|
||||||
- LDAP MD5, prefix `{MD5}`
|
- MD5 digest, prefix `{MD5}`
|
||||||
|
- SHA256 digest, prefix `{SHA256}`
|
||||||
|
- SHA512 digest, prefix `{SHA512}`
|
||||||
|
|
||||||
If you set a password with one of these prefixes it will not be hashed.
|
If you set a password with one of these prefixes it will not be hashed.
|
||||||
When users log in, if their passwords are stored with anything other than the preferred algorithm, SFTPGo will automatically upgrade the algorithm to the preferred one.
|
When users log in, if their passwords are stored with anything other than the preferred algorithm, SFTPGo will automatically upgrade the algorithm to the preferred one.
|
||||||
|
|
|
@ -100,7 +100,9 @@ const (
|
||||||
sha256cryptPwdPrefix = "$5$"
|
sha256cryptPwdPrefix = "$5$"
|
||||||
sha512cryptPwdPrefix = "$6$"
|
sha512cryptPwdPrefix = "$6$"
|
||||||
yescryptPwdPrefix = "$y$"
|
yescryptPwdPrefix = "$y$"
|
||||||
md5LDAPPwdPrefix = "{MD5}"
|
md5DigestPwdPrefix = "{MD5}"
|
||||||
|
sha256DigestPwdPrefix = "{SHA256}"
|
||||||
|
sha512DigestPwdPrefix = "{SHA512}"
|
||||||
trackQuotaDisabledError = "please enable track_quota in your configuration to use this method"
|
trackQuotaDisabledError = "please enable track_quota in your configuration to use this method"
|
||||||
operationAdd = "add"
|
operationAdd = "add"
|
||||||
operationUpdate = "update"
|
operationUpdate = "update"
|
||||||
|
@ -180,12 +182,13 @@ var (
|
||||||
sqlPlaceholders []string
|
sqlPlaceholders []string
|
||||||
internalHashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix}
|
internalHashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix}
|
||||||
hashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix, pbkdf2SHA1Prefix, pbkdf2SHA256Prefix,
|
hashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix, pbkdf2SHA1Prefix, pbkdf2SHA256Prefix,
|
||||||
pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix, md5cryptPwdPrefix, md5cryptApr1PwdPrefix, md5LDAPPwdPrefix,
|
pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix, md5cryptPwdPrefix, md5cryptApr1PwdPrefix, md5DigestPwdPrefix,
|
||||||
sha256cryptPwdPrefix, sha512cryptPwdPrefix, yescryptPwdPrefix}
|
sha256DigestPwdPrefix, sha512DigestPwdPrefix, sha256cryptPwdPrefix, sha512cryptPwdPrefix, yescryptPwdPrefix}
|
||||||
pbkdfPwdPrefixes = []string{pbkdf2SHA1Prefix, pbkdf2SHA256Prefix, pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix}
|
pbkdfPwdPrefixes = []string{pbkdf2SHA1Prefix, pbkdf2SHA256Prefix, pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix}
|
||||||
pbkdfPwdB64SaltPrefixes = []string{pbkdf2SHA256B64SaltPrefix}
|
pbkdfPwdB64SaltPrefixes = []string{pbkdf2SHA256B64SaltPrefix}
|
||||||
unixPwdPrefixes = []string{md5cryptPwdPrefix, md5cryptApr1PwdPrefix, sha256cryptPwdPrefix, sha512cryptPwdPrefix,
|
unixPwdPrefixes = []string{md5cryptPwdPrefix, md5cryptApr1PwdPrefix, sha256cryptPwdPrefix, sha512cryptPwdPrefix,
|
||||||
yescryptPwdPrefix}
|
yescryptPwdPrefix}
|
||||||
|
digestPwdPrefixes = []string{md5DigestPwdPrefix, sha256DigestPwdPrefix, sha512DigestPwdPrefix}
|
||||||
sharedProviders = []string{PGSQLDataProviderName, MySQLDataProviderName, CockroachDataProviderName}
|
sharedProviders = []string{PGSQLDataProviderName, MySQLDataProviderName, CockroachDataProviderName}
|
||||||
logSender = "dataprovider"
|
logSender = "dataprovider"
|
||||||
sqlTableUsers string
|
sqlTableUsers string
|
||||||
|
@ -3211,10 +3214,8 @@ func isPasswordOK(user *User, password string) (bool, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return match, err
|
return match, err
|
||||||
}
|
}
|
||||||
} else if strings.HasPrefix(user.Password, md5LDAPPwdPrefix) {
|
} else if util.IsStringPrefixInSlice(user.Password, digestPwdPrefixes) {
|
||||||
h := md5.New()
|
match = compareDigestPasswordAndHash(user, password)
|
||||||
h.Write([]byte(password))
|
|
||||||
match = fmt.Sprintf("%s%x", md5LDAPPwdPrefix, h.Sum(nil)) == user.Password
|
|
||||||
}
|
}
|
||||||
if err == nil && match {
|
if err == nil && match {
|
||||||
cachedUserPasswords.Add(user.Username, password, user.Password)
|
cachedUserPasswords.Add(user.Username, password, user.Password)
|
||||||
|
@ -3377,6 +3378,25 @@ func checkUserAndPubKey(user *User, pubKey []byte, isSSHCert bool) (User, string
|
||||||
return *user, "", ErrInvalidCredentials
|
return *user, "", ErrInvalidCredentials
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func compareDigestPasswordAndHash(user *User, password string) bool {
|
||||||
|
if strings.HasPrefix(user.Password, md5DigestPwdPrefix) {
|
||||||
|
h := md5.New()
|
||||||
|
h.Write([]byte(password))
|
||||||
|
return fmt.Sprintf("%s%x", md5DigestPwdPrefix, h.Sum(nil)) == user.Password
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(user.Password, sha256DigestPwdPrefix) {
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write([]byte(password))
|
||||||
|
return fmt.Sprintf("%s%x", sha256DigestPwdPrefix, h.Sum(nil)) == user.Password
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(user.Password, sha512DigestPwdPrefix) {
|
||||||
|
h := sha512.New()
|
||||||
|
h.Write([]byte(password))
|
||||||
|
return fmt.Sprintf("%s%x", sha512DigestPwdPrefix, h.Sum(nil)) == user.Password
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func compareUnixPasswordAndHash(user *User, password string) (bool, error) {
|
func compareUnixPasswordAndHash(user *User, password string) (bool, error) {
|
||||||
if strings.HasPrefix(user.Password, yescryptPwdPrefix) {
|
if strings.HasPrefix(user.Password, yescryptPwdPrefix) {
|
||||||
return compareYescryptPassword(user.Password, password)
|
return compareYescryptPassword(user.Password, password)
|
||||||
|
|
|
@ -7416,7 +7416,8 @@ func TestHashedPasswords(t *testing.T) {
|
||||||
pwdMapping["$5$h4Aalt0fJdGX8sgv$Rd2ew0fvgzUN.DzAVlKa9QL4q/DZWo4SsKpB9.3AyZ/"] = plainPwd
|
pwdMapping["$5$h4Aalt0fJdGX8sgv$Rd2ew0fvgzUN.DzAVlKa9QL4q/DZWo4SsKpB9.3AyZ/"] = plainPwd
|
||||||
pwdMapping["$apr1$OBWLeSme$WoJbB736e7kKxMBIAqilb1"] = plainPwd
|
pwdMapping["$apr1$OBWLeSme$WoJbB736e7kKxMBIAqilb1"] = plainPwd
|
||||||
pwdMapping["{MD5}5f4dcc3b5aa765d61d8327deb882cf99"] = plainPwd
|
pwdMapping["{MD5}5f4dcc3b5aa765d61d8327deb882cf99"] = plainPwd
|
||||||
|
pwdMapping["{SHA256}5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"] = plainPwd
|
||||||
|
pwdMapping["{SHA512}b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86"] = plainPwd
|
||||||
for pwd, clearPwd := range pwdMapping {
|
for pwd, clearPwd := range pwdMapping {
|
||||||
u := getTestUser(usePubKey)
|
u := getTestUser(usePubKey)
|
||||||
u.Password = pwd
|
u.Password = pwd
|
||||||
|
|
Loading…
Reference in a new issue