11package traversal
22
33import (
4+ "fmt"
45 "os"
6+ "os/exec"
57 "path/filepath"
8+ "strings"
9+
10+ "github.com/PythonHacker24/linux-acl-management-backend/config"
11+ "go.uber.org/zap"
612)
713
14+ /* list files in a given directory with some basic information */
815func ListFiles (path string , userID string ) ([]FileEntry , error ) {
916 var entries []FileEntry
1017
11- /* list all the files in the given directory */
12- files , err := os .ReadDir (path )
18+ /* combine basePath with the requested path */
19+ fullPath := filepath .Join (config .BackendConfig .AppInfo .BasePath , path )
20+
21+ /* clean the path to prevent directory traversal */
22+ fullPath = filepath .Clean (fullPath )
23+
24+ /* ensure the resulting path is still within the basePath (prevent directory traversal) */
25+ if ! strings .HasPrefix (fullPath , filepath .Clean (config .BackendConfig .AppInfo .BasePath )) {
26+ return nil , fmt .Errorf ("Path traversal attempt detected: %s" , path )
27+ }
28+
29+ /* list all the files in the given directory */
30+ files , err := os .ReadDir (fullPath )
1331 if err != nil {
1432 return nil , err
1533 }
@@ -19,8 +37,12 @@ func ListFiles(path string, userID string) ([]FileEntry, error) {
1937 fullPath := filepath .Join (path , f .Name ())
2038
2139 /* check ACL access first */
22- hasAccess := checkACLAccess (fullPath , userID )
23- if ! hasAccess {
40+ isOwner , err := isOwner (fullPath , userID )
41+ if err != nil {
42+ return nil , fmt .Errorf ("error during listing files: %w" , err )
43+ }
44+
45+ if ! isOwner {
2446 /* if the user doesn't have right ACL permissions for the file, skip it */
2547 continue
2648 }
@@ -43,3 +65,61 @@ func ListFiles(path string, userID string) ([]FileEntry, error) {
4365
4466 return entries , nil
4567}
68+
69+ /*
70+ checks if the user is the owner of the file
71+ username is the LDAP CN for the user
72+ uses getfacl to fetch the permissions (usually from filesystems mounted from remote servers)
73+ */
74+ func isOwner (filePath string , userCN string ) (bool , error ) {
75+
76+ cleanPath := filepath .Clean (filePath )
77+
78+ /* additional validation to ensure that the path doesn't contain dangerous characters */
79+ if strings .Contains (cleanPath , ";" ) || strings .Contains (cleanPath , "|" ) ||
80+ strings .Contains (cleanPath , "&" ) || strings .Contains (cleanPath , "`" ) ||
81+ strings .Contains (cleanPath , "$" ) || strings .Contains (cleanPath , "(" ) ||
82+ strings .Contains (cleanPath , ")" ) {
83+ zap .L ().Warn ("Illegal method attempted while getting file path by injecting dangerous character in the file path!" )
84+ return false , fmt .Errorf ("invalid characters in file path: %s" , cleanPath )
85+ }
86+
87+ /* get the file's ACL using getfacl with properly escaped arguments */
88+ cmd := exec .Command ("getfacl" , "--" , cleanPath )
89+ output , err := cmd .Output ()
90+ if err != nil {
91+ return false , fmt .Errorf ("failed to execute getfacl on %s: %v" , cleanPath , err )
92+ }
93+
94+ /* parse the getfacl output to check ownership */
95+ lines := strings .Split (string (output ), "\n " )
96+ for _ , line := range lines {
97+ line = strings .TrimSpace (line )
98+
99+ /* check for owner line (format: "# owner: username") */
100+ if strings .HasPrefix (line , "# owner:" ) {
101+ owner := strings .TrimSpace (strings .TrimPrefix (line , "# owner:" ))
102+
103+ /* compare with the provided CN (case-insensitive) */
104+ if strings .EqualFold (owner , userCN ) {
105+ return true , nil
106+ }
107+ }
108+
109+ /* also check user ACL entries (format: "user:username:permissions") */
110+ if strings .HasPrefix (line , "user:" ) && ! strings .HasPrefix (line , "user::" ) {
111+ parts := strings .Split (line , ":" )
112+ if len (parts ) >= 3 {
113+ aclUser := parts [1 ]
114+ permissions := parts [2 ]
115+
116+ /* check if this user has write permissions (indicating ownership-like access) */
117+ if strings .EqualFold (aclUser , userCN ) && strings .Contains (permissions , "w" ) {
118+ return true , nil
119+ }
120+ }
121+ }
122+ }
123+
124+ return false , nil
125+ }
0 commit comments