diff --git a/Readme.md b/Readme.md index 764a7df..57600cf 100644 --- a/Readme.md +++ b/Readme.md @@ -64,6 +64,13 @@ address: "127.0.0.1" # the bind address port: "8000" # the listening port dir: "/home/webdav" # the provided base dir prefix: "/webdav" # the url-prefix of the original url +deny: # deny your OS to create garbage + create: + file: # deny creation of specified files + - .DS_Store + - ._* # globbing supported, https://pkg.go.dev/path/filepath#Match + directory: # deny creation of specified directories + - .Trashes users: user: # with password 'foo' and jailed access to '/home/webdav/user' password: "$2a$10$yITzSSNJZAdDZs8iVBQzkuZCzZ49PyjTiPIrmBUKUpB0pwX7eySvW" diff --git a/app/config.go b/app/config.go index 69313ae..bbf407a 100644 --- a/app/config.go +++ b/app/config.go @@ -3,11 +3,13 @@ package app import ( "errors" "fmt" + "os" + "path/filepath" + "strings" + "github.com/fsnotify/fsnotify" log "github.com/sirupsen/logrus" "github.com/spf13/viper" - "os" - "path/filepath" ) // Config represents the configuration of the server application. @@ -16,6 +18,7 @@ type Config struct { Port string Prefix string Dir string + Deny Deny TLS *TLS Log Logging Realm string @@ -23,6 +26,15 @@ type Config struct { Cors Cors } +type Deny struct { + Create Create +} + +type Create struct { + File []string + Directory []string +} + // Logging allows definition for logging each CRUD method. type Logging struct { Error bool @@ -98,6 +110,8 @@ func setDefaults() { viper.SetDefault("Port", "8000") viper.SetDefault("Prefix", "") viper.SetDefault("Dir", "/tmp") + viper.SetDefault("Deny.Create.File", nil) + viper.SetDefault("Deny.Create.Directory", nil) viper.SetDefault("Users", nil) viper.SetDefault("TLS", nil) viper.SetDefault("Realm", "dave") @@ -179,6 +193,14 @@ func updateConfig(cfg *Config, updatedCfg *Config) { cfg.Log.Delete = updatedCfg.Log.Delete log.WithField("enabled", cfg.Log.Delete).Info("Set logging for delete operations") } + if !stringSlicesEqual(cfg.Deny.Create.File, updatedCfg.Deny.Create.File) { + cfg.Deny.Create.File = updatedCfg.Deny.Create.File + log.WithField("updated", strings.Join(cfg.Deny.Create.File, "; ")).Info("Updated denied file create entries") + } + if !stringSlicesEqual(cfg.Deny.Create.Directory, updatedCfg.Deny.Create.Directory) { + cfg.Deny.Create.Directory = updatedCfg.Deny.Create.Directory + log.WithField("updated", strings.Join(cfg.Deny.Create.Directory, "; ")).Info("Updated denied directory create entries") + } } func (cfg *Config) ensureUserDirs() { @@ -201,3 +223,15 @@ func (cfg *Config) ensureUserDirs() { } } } + +func stringSlicesEqual(f, j []string) bool { + if len(f) != len(j) { + return false + } + for i, v := range f { + if v != j[i] { + return false + } + } + return true +} diff --git a/app/fs.go b/app/fs.go index de39e67..e91802a 100644 --- a/app/fs.go +++ b/app/fs.go @@ -2,12 +2,15 @@ package app import ( "context" - log "github.com/sirupsen/logrus" - "golang.org/x/net/webdav" + "errors" + "fmt" "os" "path" "path/filepath" "strings" + + log "github.com/sirupsen/logrus" + "golang.org/x/net/webdav" ) // This file is an extension of golang.org/x/net/webdav/file.go. @@ -58,6 +61,17 @@ func (d Dir) Mkdir(ctx context.Context, name string, perm os.FileMode) error { if name = d.resolve(ctx, name); name == "" { return os.ErrNotExist } + + for _, v := range d.Config.Deny.Create.Directory { + matched, err := filepath.Match(v, filepath.Base(name)) + if err != nil { + return err + } + if matched { + return errors.New(fmt.Sprintf("mkdir %s, action denied", name)) + } + } + err := os.Mkdir(name, perm) if err != nil { return err @@ -78,6 +92,21 @@ func (d Dir) OpenFile(ctx context.Context, name string, flag int, perm os.FileMo if name = d.resolve(ctx, name); name == "" { return nil, os.ErrNotExist } + if len(d.Config.Deny.Create.File) > 0 { + // os.O_RDONLY: 0, os.O_RDWR: 2, os.O_CREATE: 512, O_TRUNC: 1024 + if flag == os.O_RDWR|os.O_CREATE|os.O_TRUNC || flag == os.O_RDWR|os.O_CREATE || flag == os.O_CREATE|os.O_TRUNC || flag == os.O_CREATE { + for _, v := range d.Config.Deny.Create.File { + matched, err := filepath.Match(v, filepath.Base(name)) + if err != nil { + return nil, err + } + if matched { + return nil, errors.New(fmt.Sprintf("create %s, action denied", name)) + } + } + } + } + f, err := os.OpenFile(name, flag, perm) if err != nil { return nil, err