Procházet zdrojové kódy

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>
Nicola Murino před 2 roky
rodič
revize
74e5999c63

+ 3 - 1
docs/full-configuration.md

@@ -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.

+ 27 - 7
internal/dataprovider/dataprovider.go

@@ -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,
-		sha256cryptPwdPrefix, sha512cryptPwdPrefix, yescryptPwdPrefix}
+		pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix, md5cryptPwdPrefix, md5cryptApr1PwdPrefix, md5DigestPwdPrefix,
+		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) {
-		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 {
 	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)

+ 2 - 1
internal/sftpd/sftpd_test.go

@@ -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