diff --git a/docs/full-configuration.md b/docs/full-configuration.md index 4d6ba751..4a624c1d 100644 --- a/docs/full-configuration.md +++ b/docs/full-configuration.md @@ -585,7 +585,9 @@ Supported hash algorithms: - MD5 crypt APR1, prefix `$apr1$` - SHA256 crypt, prefix `$5$` - 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. 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. diff --git a/internal/dataprovider/dataprovider.go b/internal/dataprovider/dataprovider.go index cef97c18..3bdddff3 100644 --- a/internal/dataprovider/dataprovider.go +++ b/internal/dataprovider/dataprovider.go @@ -100,7 +100,9 @@ const ( sha256cryptPwdPrefix = "$5$" sha512cryptPwdPrefix = "$6$" yescryptPwdPrefix = "$y$" - md5LDAPPwdPrefix = "{MD5}" + md5DigestPwdPrefix = "{MD5}" + sha256DigestPwdPrefix = "{SHA256}" + sha512DigestPwdPrefix = "{SHA512}" trackQuotaDisabledError = "please enable track_quota in your configuration to use this method" operationAdd = "add" operationUpdate = "update" @@ -180,12 +182,13 @@ var ( sqlPlaceholders []string internalHashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix} hashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix, pbkdf2SHA1Prefix, pbkdf2SHA256Prefix, - pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix, md5cryptPwdPrefix, md5cryptApr1PwdPrefix, md5LDAPPwdPrefix, - sha256cryptPwdPrefix, sha512cryptPwdPrefix, yescryptPwdPrefix} + pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix, md5cryptPwdPrefix, md5cryptApr1PwdPrefix, md5DigestPwdPrefix, + sha256DigestPwdPrefix, sha512DigestPwdPrefix, sha256cryptPwdPrefix, sha512cryptPwdPrefix, yescryptPwdPrefix} pbkdfPwdPrefixes = []string{pbkdf2SHA1Prefix, pbkdf2SHA256Prefix, pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix} pbkdfPwdB64SaltPrefixes = []string{pbkdf2SHA256B64SaltPrefix} unixPwdPrefixes = []string{md5cryptPwdPrefix, md5cryptApr1PwdPrefix, sha256cryptPwdPrefix, sha512cryptPwdPrefix, yescryptPwdPrefix} + digestPwdPrefixes = []string{md5DigestPwdPrefix, sha256DigestPwdPrefix, sha512DigestPwdPrefix} sharedProviders = []string{PGSQLDataProviderName, MySQLDataProviderName, CockroachDataProviderName} logSender = "dataprovider" sqlTableUsers string @@ -3211,10 +3214,8 @@ func isPasswordOK(user *User, password string) (bool, error) { if err != nil { return match, err } - } else if strings.HasPrefix(user.Password, md5LDAPPwdPrefix) { - h := md5.New() - h.Write([]byte(password)) - match = fmt.Sprintf("%s%x", md5LDAPPwdPrefix, h.Sum(nil)) == user.Password + } else if util.IsStringPrefixInSlice(user.Password, digestPwdPrefixes) { + match = compareDigestPasswordAndHash(user, password) } if err == nil && match { cachedUserPasswords.Add(user.Username, password, user.Password) @@ -3377,6 +3378,25 @@ func checkUserAndPubKey(user *User, pubKey []byte, isSSHCert bool) (User, string 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) { if strings.HasPrefix(user.Password, yescryptPwdPrefix) { return compareYescryptPassword(user.Password, password) diff --git a/internal/sftpd/sftpd_test.go b/internal/sftpd/sftpd_test.go index 65180f01..936454d1 100644 --- a/internal/sftpd/sftpd_test.go +++ b/internal/sftpd/sftpd_test.go @@ -7416,7 +7416,8 @@ func TestHashedPasswords(t *testing.T) { pwdMapping["$5$h4Aalt0fJdGX8sgv$Rd2ew0fvgzUN.DzAVlKa9QL4q/DZWo4SsKpB9.3AyZ/"] = plainPwd pwdMapping["$apr1$OBWLeSme$WoJbB736e7kKxMBIAqilb1"] = plainPwd pwdMapping["{MD5}5f4dcc3b5aa765d61d8327deb882cf99"] = plainPwd - + pwdMapping["{SHA256}5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"] = plainPwd + pwdMapping["{SHA512}b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86"] = plainPwd for pwd, clearPwd := range pwdMapping { u := getTestUser(usePubKey) u.Password = pwd