summaryrefslogtreecommitdiff
path: root/internal/user
diff options
context:
space:
mode:
authorPaul Bütow <pbuetow@mimecast.com>2020-01-20 18:41:05 +0000
committerPaul Bütow <pbuetow@mimecast.com>2020-01-21 14:35:23 +0000
commitc128865c4c7411c29a59fca9a3a2f95537686d7b (patch)
tree193bccc70d942c8b70cc93fae2670263701e43aa /internal/user
parent3755a9911ecb05886577095f2b8cc8b9e4066a3a (diff)
Move commands to cmd/ and move internal dependencies to internal/
Diffstat (limited to 'internal/user')
-rw-r--r--internal/user/name.go24
-rw-r--r--internal/user/server/user.go131
2 files changed, 155 insertions, 0 deletions
diff --git a/internal/user/name.go b/internal/user/name.go
new file mode 100644
index 0000000..5171ec7
--- /dev/null
+++ b/internal/user/name.go
@@ -0,0 +1,24 @@
+package user
+
+import (
+ "os/user"
+ )
+
+
+func Name() string {
+ user, err := user.Current()
+ if err != nil {
+ panic(err)
+ }
+
+ if user.Uid == "0" {
+ panic("Not allowed to run as UID 0")
+ }
+
+ if user.Gid == "0" {
+ panic("Not allowed to run as GID 0")
+ }
+
+ return user.Username
+}
+
diff --git a/internal/user/server/user.go b/internal/user/server/user.go
new file mode 100644
index 0000000..fad38d8
--- /dev/null
+++ b/internal/user/server/user.go
@@ -0,0 +1,131 @@
+package server
+
+import (
+ "github.com/mimecast/dtail/internal/config"
+ "github.com/mimecast/dtail/internal/fs/permissions"
+ "github.com/mimecast/dtail/internal/logger"
+ "fmt"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+)
+
+const maxLinkDepth int = 100
+
+// User represents an end-user which connected to the server via the DTail client.
+type User struct {
+ // The user name.
+ Name string
+ // The remote address connected from.
+ remoteAddress string
+ // The permissions the user has.
+ permissions []string
+}
+
+// New returns a new user.
+func New(name, remoteAddress string) *User {
+ return &User{
+ Name: name,
+ remoteAddress: remoteAddress,
+ }
+}
+
+// String representation of the user.
+func (u *User) String() string {
+ return fmt.Sprintf("%s@%s", u.Name, u.remoteAddress)
+}
+
+// HasFilePermission is used to determine whether user is alowed to read a file.
+func (u *User) HasFilePermission(filePath string) (hasPermission bool) {
+ cleanPath, err := filepath.EvalSymlinks(filePath)
+ if err != nil {
+ logger.Error(u, filePath, "Unable to evaluate symlinks", err)
+ hasPermission = false
+ return
+ }
+
+ cleanPath, err = filepath.Abs(cleanPath)
+ if err != nil {
+ logger.Error(u, cleanPath, "Unable to make file path absolute", err)
+ hasPermission = false
+ return
+ }
+
+ if cleanPath != filePath {
+ logger.Info(u, filePath, cleanPath, "Calculated new clean path from original file path (possibly symlink)")
+ }
+
+ hasPermission, err = u.hasFilePermission(cleanPath)
+ if err != nil {
+ logger.Warn(u, cleanPath, err)
+ }
+
+ return
+}
+
+func (u *User) hasFilePermission(cleanPath string) (bool, error) {
+ // First check file system Linux/UNIX permission.
+ if _, err := permissions.ToRead(u.Name, cleanPath); err != nil {
+ return false, fmt.Errorf("User without OS file system permissions to read file: '%v'", err)
+ }
+ logger.Info(u, cleanPath, "User has OS file system permissions to read file")
+
+ // If file system permission is given, also check permissions
+ // as configured in DTail config file.
+ if len(u.permissions) == 0 {
+ p, err := config.ServerUserPermissions(u.Name)
+ if err != nil {
+ return false, err
+ }
+ u.permissions = p
+ }
+
+ var hasPermission bool
+ var err error
+
+ if hasPermission, err = u.iteratePaths(cleanPath); err != nil {
+ return false, err
+ }
+
+ // Only allow to follow regular files or symlinks.
+ info, err := os.Lstat(cleanPath)
+ if err != nil {
+ return false, fmt.Errorf("Unable to determine file type: '%v'", err)
+ }
+
+ if !info.Mode().IsRegular() {
+ return false, fmt.Errorf("Can only open regular files or follow symlinks")
+ }
+
+ return hasPermission, nil
+}
+
+func (u *User) iteratePaths(cleanPath string) (bool, error) {
+ for _, permission := range u.permissions {
+ var regexStr string
+ var negate bool
+
+ if strings.HasPrefix(permission, "!") {
+ regexStr = permission[1:]
+ negate = true
+ }
+ regexStr = permission
+ negate = false
+
+ re, err := regexp.Compile(regexStr)
+ if err != nil {
+ return false, fmt.Errorf("Permission test failed, can't compile regex '%s': '%v'", regexStr, err)
+ }
+
+ if negate && re.MatchString(cleanPath) {
+ return false, fmt.Errorf("Permission test failed, matching negative pattern '%s'", permission)
+ }
+
+ if !negate && re.MatchString(cleanPath) {
+ logger.Info(u, cleanPath, "Permission test passed partially, matching positive pattern", permission)
+ }
+ }
+
+ return true, nil
+}