2019-12-07 23:53:07 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2020-11-08 21:52:20 +00:00
|
|
|
"emperror.dev/errors"
|
2020-09-01 03:14:04 +00:00
|
|
|
"github.com/apex/log"
|
|
|
|
"regexp"
|
2019-12-07 23:53:07 +00:00
|
|
|
)
|
|
|
|
|
2020-09-01 03:14:04 +00:00
|
|
|
type SftpAuthRequest struct {
|
|
|
|
User string `json:"username"`
|
|
|
|
Pass string `json:"password"`
|
|
|
|
IP string `json:"ip"`
|
|
|
|
SessionID []byte `json:"session_id"`
|
|
|
|
ClientVersion []byte `json:"client_version"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type SftpAuthResponse struct {
|
|
|
|
Server string `json:"server"`
|
|
|
|
Token string `json:"token"`
|
|
|
|
Permissions []string `json:"permissions"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type sftpInvalidCredentialsError struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ice sftpInvalidCredentialsError) Error() string {
|
|
|
|
return "the credentials provided were invalid"
|
|
|
|
}
|
|
|
|
|
|
|
|
func IsInvalidCredentialsError(err error) bool {
|
|
|
|
_, ok := err.(*sftpInvalidCredentialsError)
|
|
|
|
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// Usernames all follow the same format, so don't even bother hitting the API if the username is not
|
|
|
|
// at least in the expected format. This is very basic protection against random bots finding the SFTP
|
|
|
|
// server and sending a flood of usernames.
|
|
|
|
var validUsernameRegexp = regexp.MustCompile(`^(?i)(.+)\.([a-z0-9]{8})$`)
|
|
|
|
|
2020-10-31 17:04:20 +00:00
|
|
|
func (r *Request) ValidateSftpCredentials(request SftpAuthRequest) (*SftpAuthResponse, error) {
|
2020-09-01 03:14:04 +00:00
|
|
|
// If the username doesn't meet the expected format that the Panel would even recognize just go ahead
|
2020-09-05 19:08:40 +00:00
|
|
|
// and bail out of the process here to avoid accidentally brute forcing the panel if a bot decides
|
2020-09-01 03:14:04 +00:00
|
|
|
// to connect to spam username attempts.
|
|
|
|
if !validUsernameRegexp.MatchString(request.User) {
|
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"subsystem": "sftp",
|
2020-09-05 19:08:40 +00:00
|
|
|
"username": request.User,
|
|
|
|
"ip": request.IP,
|
2020-09-01 03:14:04 +00:00
|
|
|
}).Warn("failed to validate user credentials (invalid format)")
|
|
|
|
|
|
|
|
return nil, new(sftpInvalidCredentialsError)
|
|
|
|
}
|
|
|
|
|
2020-10-31 17:04:20 +00:00
|
|
|
resp, err := r.Post("/sftp/auth", request)
|
2019-12-07 23:53:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
2020-10-31 17:04:20 +00:00
|
|
|
e := resp.Error()
|
|
|
|
if e != nil {
|
|
|
|
if resp.StatusCode >= 400 && resp.StatusCode < 500 {
|
2020-09-13 20:55:40 +00:00
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"subsystem": "sftp",
|
2020-09-20 18:51:12 +00:00
|
|
|
"username": request.User,
|
|
|
|
"ip": request.IP,
|
2020-10-31 17:04:20 +00:00
|
|
|
}).Warn(e.Error())
|
2020-09-13 20:55:40 +00:00
|
|
|
|
2020-10-31 17:04:20 +00:00
|
|
|
return nil, &sftpInvalidCredentialsError{}
|
2019-12-07 23:53:07 +00:00
|
|
|
}
|
|
|
|
|
2020-10-31 17:04:20 +00:00
|
|
|
rerr := errors.New(e.Error())
|
2020-04-25 18:49:21 +00:00
|
|
|
|
|
|
|
return nil, rerr
|
2019-12-07 23:53:07 +00:00
|
|
|
}
|
|
|
|
|
2020-10-31 17:04:20 +00:00
|
|
|
var response SftpAuthResponse
|
|
|
|
if err := resp.Bind(&response); err != nil {
|
2019-12-07 23:53:07 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-10-31 17:04:20 +00:00
|
|
|
return &response, nil
|
2020-09-05 19:08:40 +00:00
|
|
|
}
|