fix: SFTP auth bypass with no pub key in LDAP (#20986)

If a user attempts to authenticate with a key but does not have an
sshpubkey attribute in LDAP, the server allows the connection, which 
means the server trusted the key without reason. This is now fixed, 
and a test has been added for validation.
This commit is contained in:
Aditya Manthramurthy
2025-02-27 10:43:32 -08:00
committed by GitHub
parent 6cd8a372cb
commit 4c71f1b4ec
2 changed files with 85 additions and 25 deletions

View File

@@ -62,7 +62,7 @@ var (
// if the sftp parameter --trusted-user-ca-key is set, then
// the final form of the key file will be set as this variable.
var caPublicKey ssh.PublicKey
var globalSFTPTrustedCAPubkey ssh.PublicKey
// https://cs.opensource.google/go/x/crypto/+/refs/tags/v0.22.0:ssh/common.go;l=46
// preferredKexAlgos specifies the default preference for key-exchange
@@ -161,8 +161,8 @@ internalAuth:
return nil, errNoSuchUser
}
if caPublicKey != nil && pass == nil {
err := validateKey(c, key)
if globalSFTPTrustedCAPubkey != nil && pass == nil {
err := validateClientKeyIsTrusted(c, key)
if err != nil {
return nil, errAuthentication
}
@@ -256,9 +256,18 @@ func processLDAPAuthentication(key ssh.PublicKey, pass []byte, user string) (per
return nil, errAuthentication
}
}
// Save each attribute to claims.
claims[ldapAttribPrefix+attribKey] = attribValue[0]
}
if key != nil {
// If a key was provided, we expect the user to have an sshPublicKey
// attribute.
if _, ok := claims[ldapAttribPrefix+"sshPublicKey"]; !ok {
return nil, errAuthentication
}
}
expiryDur, err := globalIAMSys.LDAPConfig.GetExpiryDuration("")
if err != nil {
return nil, err
@@ -310,8 +319,8 @@ func processLDAPAuthentication(key ssh.PublicKey, pass []byte, user string) (per
}, nil
}
func validateKey(c ssh.ConnMetadata, clientKey ssh.PublicKey) (err error) {
if caPublicKey == nil {
func validateClientKeyIsTrusted(c ssh.ConnMetadata, clientKey ssh.PublicKey) (err error) {
if globalSFTPTrustedCAPubkey == nil {
return errors.New("public key authority validation requested but no ca public key specified.")
}
@@ -331,7 +340,7 @@ func validateKey(c ssh.ConnMetadata, clientKey ssh.PublicKey) (err error) {
// and that certificate type is correct.
checker := ssh.CertChecker{}
checker.IsUserAuthority = func(k ssh.PublicKey) bool {
return subtle.ConstantTimeCompare(caPublicKey.Marshal(), k.Marshal()) == 1
return subtle.ConstantTimeCompare(globalSFTPTrustedCAPubkey.Marshal(), k.Marshal()) == 1
}
_, err = checker.Authenticate(c, clientKey)
@@ -428,7 +437,7 @@ func startSFTPServer(args []string) {
allowMACs = filterAlgos(arg, strings.Split(tokens[1], ","), supportedMACs)
case "trusted-user-ca-key":
userCaKeyFile = tokens[1]
case "password-auth":
case "disable-password-auth":
disablePassAuth, _ = strconv.ParseBool(tokens[1])
}
}
@@ -457,7 +466,7 @@ func startSFTPServer(args []string) {
logger.Fatal(fmt.Errorf("invalid arguments passed, trusted user certificate authority public key file is not accessible: %v", err), "unable to start SFTP server")
}
caPublicKey, _, _, _, err = ssh.ParseAuthorizedKey(keyBytes)
globalSFTPTrustedCAPubkey, _, _, _, err = ssh.ParseAuthorizedKey(keyBytes)
if err != nil {
logger.Fatal(fmt.Errorf("invalid arguments passed, trusted user certificate authority public key file is not parseable: %v", err), "unable to start SFTP server")
}