improve validations for user provided file and directory paths

This commit is contained in:
Nicola Murino
2020-03-03 09:09:58 +01:00
parent d0a81cabab
commit 7f1946de34
13 changed files with 72 additions and 10 deletions

View File

@@ -149,7 +149,7 @@ func getCustomServeFlags() []string {
result = append(result, "--"+configFileFlag) result = append(result, "--"+configFileFlag)
result = append(result, configFile) result = append(result, configFile)
} }
if logFilePath != defaultLogFile && len(logFilePath) > 0 && logFilePath != "." { if logFilePath != defaultLogFile && utils.IsFileInputValid(logFilePath) {
if !filepath.IsAbs(logFilePath) { if !filepath.IsAbs(logFilePath) {
logFilePath = filepath.Join(configDir, logFilePath) logFilePath = filepath.Join(configDir, logFilePath)
} }

View File

@@ -5,6 +5,7 @@ import (
"path/filepath" "path/filepath"
"github.com/drakkan/sftpgo/service" "github.com/drakkan/sftpgo/service"
"github.com/drakkan/sftpgo/utils"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -14,7 +15,7 @@ var (
Short: "Start SFTPGo Windows Service", Short: "Start SFTPGo Windows Service",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
configDir = filepath.Clean(configDir) configDir = filepath.Clean(configDir)
if !filepath.IsAbs(logFilePath) && len(logFilePath) > 0 && logFilePath != "." { if !filepath.IsAbs(logFilePath) && utils.IsFileInputValid(logFilePath) {
logFilePath = filepath.Join(configDir, logFilePath) logFilePath = filepath.Join(configDir, logFilePath)
} }
s := service.Service{ s := service.Service{

View File

@@ -172,6 +172,12 @@ func LoadConfig(configDir, configName string) error {
if strings.TrimSpace(globalConf.SFTPD.Banner) == "" { if strings.TrimSpace(globalConf.SFTPD.Banner) == "" {
globalConf.SFTPD.Banner = defaultBanner globalConf.SFTPD.Banner = defaultBanner
} }
if len(globalConf.ProviderConf.UsersBaseDir) > 0 && !utils.IsFileInputValid(globalConf.ProviderConf.UsersBaseDir) {
err = fmt.Errorf("invalid users base dir %#v will be ignored", globalConf.ProviderConf.UsersBaseDir)
globalConf.ProviderConf.UsersBaseDir = ""
logger.Warn(logSender, "", "Configuration error: %v", err)
logger.WarnToConsole("Configuration error: %v", err)
}
if globalConf.SFTPD.UploadMode < 0 || globalConf.SFTPD.UploadMode > 2 { if globalConf.SFTPD.UploadMode < 0 || globalConf.SFTPD.UploadMode > 2 {
err = fmt.Errorf("invalid upload_mode 0, 1 and 2 are supported, configured: %v reset upload_mode to 0", err = fmt.Errorf("invalid upload_mode 0, 1 and 2 are supported, configured: %v reset upload_mode to 0",
globalConf.SFTPD.UploadMode) globalConf.SFTPD.UploadMode)
@@ -198,6 +204,6 @@ func LoadConfig(configDir, configName string) error {
logger.Warn(logSender, "", "Configuration error: %v", err) logger.Warn(logSender, "", "Configuration error: %v", err)
logger.WarnToConsole("Configuration error: %v", err) logger.WarnToConsole("Configuration error: %v", err)
} }
logger.Debug(logSender, "", "config file used: '%v', config loaded: %+v", viper.ConfigFileUsed(), getRedactedGlobalConf()) logger.Debug(logSender, "", "config file used: '%#v', config loaded: %+v", viper.ConfigFileUsed(), getRedactedGlobalConf())
return err return err
} }

View File

@@ -161,6 +161,27 @@ func TestInvalidProxyProtocol(t *testing.T) {
os.Remove(configFilePath) os.Remove(configFilePath)
} }
func TestInvalidUsersBaseDir(t *testing.T) {
configDir := ".."
confName := tempConfigName + ".json"
configFilePath := filepath.Join(configDir, confName)
config.LoadConfig(configDir, "")
providerConf := config.GetProviderConf()
providerConf.UsersBaseDir = "."
c := make(map[string]dataprovider.Config)
c["data_provider"] = providerConf
jsonConf, _ := json.Marshal(c)
err := ioutil.WriteFile(configFilePath, jsonConf, 0666)
if err != nil {
t.Errorf("error saving temporary configuration")
}
err = config.LoadConfig(configDir, tempConfigName)
if err == nil {
t.Errorf("Loading configuration with invalid users base dir must fail")
}
os.Remove(configFilePath)
}
func TestSetGetConfig(t *testing.T) { func TestSetGetConfig(t *testing.T) {
sftpdConf := config.GetSFTPDConfig() sftpdConf := config.GetSFTPDConfig()
sftpdConf.IdleTimeout = 3 sftpdConf.IdleTimeout = 3

View File

@@ -55,7 +55,7 @@ func initializeBoltProvider(basePath string) error {
var err error var err error
logSender = BoltDataProviderName logSender = BoltDataProviderName
dbPath := config.Name dbPath := config.Name
if dbPath == "." { if !utils.IsFileInputValid(dbPath) {
return fmt.Errorf("Invalid database path: %#v", dbPath) return fmt.Errorf("Invalid database path: %#v", dbPath)
} }
if !filepath.IsAbs(dbPath) { if !filepath.IsAbs(dbPath) {

View File

@@ -39,7 +39,7 @@ type MemoryProvider struct {
func initializeMemoryProvider(basePath string) error { func initializeMemoryProvider(basePath string) error {
configFile := "" configFile := ""
if len(config.Name) > 0 && config.Name != "." { if utils.IsFileInputValid(config.Name) {
configFile = config.Name configFile = config.Name
if !filepath.IsAbs(configFile) { if !filepath.IsAbs(configFile) {
configFile = filepath.Join(basePath, configFile) configFile = filepath.Join(basePath, configFile)

View File

@@ -7,6 +7,7 @@ import (
"strings" "strings"
"github.com/drakkan/sftpgo/logger" "github.com/drakkan/sftpgo/logger"
"github.com/drakkan/sftpgo/utils"
) )
const ( const (
@@ -32,7 +33,7 @@ func initializeSQLiteProvider(basePath string) error {
logSender = SQLiteDataProviderName logSender = SQLiteDataProviderName
if len(config.ConnectionString) == 0 { if len(config.ConnectionString) == 0 {
dbPath := config.Name dbPath := config.Name
if dbPath == "." { if !utils.IsFileInputValid(dbPath) {
return fmt.Errorf("Invalid database path: %#v", dbPath) return fmt.Errorf("Invalid database path: %#v", dbPath)
} }
if !filepath.IsAbs(dbPath) { if !filepath.IsAbs(dbPath) {

1
go.mod
View File

@@ -3,7 +3,6 @@ module github.com/drakkan/sftpgo
go 1.13 go 1.13
require ( require (
cloud.google.com/go v0.53.0 // indirect
cloud.google.com/go/storage v1.6.0 cloud.google.com/go/storage v1.6.0
github.com/alexedwards/argon2id v0.0.0-20190612080829-01a59b2b8802 github.com/alexedwards/argon2id v0.0.0-20190612080829-01a59b2b8802
github.com/aws/aws-sdk-go v1.29.14 github.com/aws/aws-sdk-go v1.29.14

View File

@@ -15,6 +15,7 @@ import (
"github.com/drakkan/sftpgo/dataprovider" "github.com/drakkan/sftpgo/dataprovider"
"github.com/drakkan/sftpgo/logger" "github.com/drakkan/sftpgo/logger"
"github.com/drakkan/sftpgo/utils"
"github.com/go-chi/chi" "github.com/go-chi/chi"
) )
@@ -90,6 +91,10 @@ func (c Conf) Initialize(configDir string) error {
backupsPath = getConfigPath(c.BackupsPath, configDir) backupsPath = getConfigPath(c.BackupsPath, configDir)
staticFilesPath := getConfigPath(c.StaticFilesPath, configDir) staticFilesPath := getConfigPath(c.StaticFilesPath, configDir)
templatesPath := getConfigPath(c.TemplatesPath, configDir) templatesPath := getConfigPath(c.TemplatesPath, configDir)
if len(backupsPath) == 0 || len(staticFilesPath) == 0 || len(templatesPath) == 0 {
return fmt.Errorf("Required directory is invalid, backup path %#v, static file path: %#v template path: %#v",
backupsPath, staticFilesPath, templatesPath)
}
authUserFile := getConfigPath(c.AuthUserFile, configDir) authUserFile := getConfigPath(c.AuthUserFile, configDir)
httpAuth, err = newBasicAuthProvider(authUserFile) httpAuth, err = newBasicAuthProvider(authUserFile)
if err != nil { if err != nil {
@@ -129,7 +134,10 @@ func ReloadTLSCertificate() {
} }
func getConfigPath(name, configDir string) string { func getConfigPath(name, configDir string) string {
if len(name) > 0 && !filepath.IsAbs(name) && name != "." { if !utils.IsFileInputValid(name) {
return ""
}
if len(name) > 0 && !filepath.IsAbs(name) {
return filepath.Join(configDir, name) return filepath.Join(configDir, name)
} }
return name return name

View File

@@ -169,6 +169,13 @@ func TestInitialization(t *testing.T) {
if err == nil { if err == nil {
t.Error("Inizialize must fail") t.Error("Inizialize must fail")
} }
httpdConf.CertificateFile = ""
httpdConf.CertificateKeyFile = ""
httpdConf.TemplatesPath = "."
err = httpdConf.Initialize(configDir)
if err == nil {
t.Error("Inizialize must fail")
}
} }
func TestBasicUserHandling(t *testing.T) { func TestBasicUserHandling(t *testing.T) {

View File

@@ -47,7 +47,7 @@ func GetLogger() *zerolog.Logger {
// InitLogger configures the logger using the given parameters // InitLogger configures the logger using the given parameters
func InitLogger(logFilePath string, logMaxSize int, logMaxBackups int, logMaxAge int, logCompress bool, level zerolog.Level) { func InitLogger(logFilePath string, logMaxSize int, logMaxBackups int, logMaxAge int, logCompress bool, level zerolog.Level) {
zerolog.TimeFieldFormat = dateFormat zerolog.TimeFieldFormat = dateFormat
if len(logFilePath) > 0 && filepath.Clean(logFilePath) != "." { if isLogFilePathValid(logFilePath) {
logger = zerolog.New(&lumberjack.Logger{ logger = zerolog.New(&lumberjack.Logger{
Filename: logFilePath, Filename: logFilePath,
MaxSize: logMaxSize, MaxSize: logMaxSize,
@@ -183,3 +183,11 @@ func ConnectionFailedLog(user, ip, loginType, errorString string) {
Str("error", errorString). Str("error", errorString).
Msg("") Msg("")
} }
func isLogFilePathValid(logFilePath string) bool {
cleanInput := filepath.Clean(logFilePath)
if cleanInput == "." || cleanInput == ".." {
return false
}
return true
}

View File

@@ -50,7 +50,7 @@ func (s *Service) Start() error {
if !s.LogVerbose { if !s.LogVerbose {
logLevel = zerolog.InfoLevel logLevel = zerolog.InfoLevel
} }
if !filepath.IsAbs(s.LogFilePath) && len(s.LogFilePath) > 0 && s.LogFilePath != "." { if !filepath.IsAbs(s.LogFilePath) && utils.IsFileInputValid(s.LogFilePath) {
s.LogFilePath = filepath.Join(s.ConfigDir, s.LogFilePath) s.LogFilePath = filepath.Join(s.ConfigDir, s.LogFilePath)
} }
logger.InitLogger(s.LogFilePath, s.LogMaxSize, s.LogMaxBackups, s.LogMaxAge, s.LogCompress, logLevel) logger.InitLogger(s.LogFilePath, s.LogMaxSize, s.LogMaxBackups, s.LogMaxAge, s.LogCompress, logLevel)

View File

@@ -288,3 +288,14 @@ func LoadTemplate(t *template.Template, err error) *template.Template {
} }
return t return t
} }
// IsFileInputValid returns true this is a valid file name.
// This method must be used before joining a file name, generally provided as
// user input, with a directory
func IsFileInputValid(fileInput string) bool {
cleanInput := filepath.Clean(fileInput)
if cleanInput == "." || cleanInput == ".." {
return false
}
return true
}