-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
440 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package osutil | ||
|
||
import ( | ||
"fmt" | ||
"io/fs" | ||
"os" | ||
) | ||
|
||
func IsWriteGroup(mode os.FileMode) bool { | ||
return mode&0o20 != 0 | ||
} | ||
|
||
func IsWriteOther(mode os.FileMode) bool { | ||
return mode&0o02 != 0 | ||
} | ||
|
||
func checkPathInfo(info fs.FileInfo, path string, uid int, permissions int) error { | ||
err := FileUidMatch(info, path, uid) | ||
if err != nil { | ||
return err | ||
} | ||
err = FilePermissionsMatch(info, path, permissions) | ||
if err != nil { | ||
return err | ||
} | ||
return err | ||
} | ||
|
||
func FilePermissionsMatch(info fs.FileInfo, path string, permissions int) (err error) { | ||
if permissions != 0 && int(info.Mode().Perm()) != permissions { | ||
return fmt.Errorf("path %q does not have permissions %o", path, permissions) | ||
} | ||
if permissions == 0 && (IsWriteOther(info.Mode()) || IsWriteGroup(info.Mode())) { | ||
return fmt.Errorf("path %q has insecure permissions %o. Vault expects no write permissions for group or others", path, info.Mode().Perm()) | ||
} | ||
|
||
return err | ||
} | ||
|
||
// OwnerPermissionsMatch checks if vault user is the owner and permissions are secure for input path | ||
func OwnerPermissionsMatch(path string, uid int, permissions int) (err error) { | ||
if path == "" { | ||
return fmt.Errorf("could not verify permissions for path. No path provided ") | ||
} | ||
|
||
info, err := os.Stat(path) | ||
if err != nil { | ||
return fmt.Errorf("error stating %q: %w", path, err) | ||
} | ||
if info.Mode()&os.ModeSymlink != 0 { | ||
symLinkInfo, err := os.Lstat(path) | ||
if err != nil { | ||
return fmt.Errorf("error stating %q: %w", path, err) | ||
} | ||
err = checkPathInfo(symLinkInfo, path, uid, permissions) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
err = checkPathInfo(info, path, uid, permissions) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package osutil | ||
|
||
import ( | ||
"io/fs" | ||
"os" | ||
"os/user" | ||
"runtime" | ||
"strconv" | ||
"testing" | ||
) | ||
|
||
func TestCheckPathInfo(t *testing.T) { | ||
currentUser, err := user.Current() | ||
if err != nil { | ||
t.Errorf("failed to get details of current process owner. The error is: %v", err) | ||
} | ||
uid, err := strconv.ParseInt(currentUser.Uid, 0, 64) | ||
if err != nil { | ||
t.Errorf("failed to convert uid to int64. The error is: %v", err) | ||
} | ||
uid2, err := strconv.ParseInt(currentUser.Uid+"1", 0, 64) | ||
if err != nil { | ||
t.Errorf("failed to convert uid to int64. The error is: %v", err) | ||
} | ||
|
||
testCases := []struct { | ||
uid int | ||
filepermissions fs.FileMode | ||
permissions int | ||
expectError bool | ||
}{ | ||
{ | ||
uid: 0, | ||
filepermissions: 0o700, | ||
permissions: 0, | ||
expectError: false, | ||
}, | ||
{ | ||
uid: int(uid2), | ||
filepermissions: 0o700, | ||
permissions: 0, | ||
expectError: true, | ||
}, | ||
{ | ||
uid: int(uid), | ||
filepermissions: 0o700, | ||
permissions: 0, | ||
expectError: false, | ||
}, | ||
{ | ||
uid: 0, | ||
filepermissions: 0o777, | ||
permissions: 744, | ||
expectError: true, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
err := os.Mkdir("testFile", tc.filepermissions) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
info, err := os.Stat("testFile") | ||
if err != nil { | ||
t.Errorf("error stating %q: %v", "testFile", err) | ||
} | ||
if tc.uid != 0 && runtime.GOOS == "windows" && tc.expectError == true { | ||
t.Skip("Skipping test in windows environment as no error will be returned in this case") | ||
} | ||
|
||
err = checkPathInfo(info, "testFile", tc.uid, int(tc.permissions)) | ||
if tc.expectError && err == nil { | ||
t.Errorf("invalid result. expected error") | ||
} | ||
if !tc.expectError && err != nil { | ||
t.Errorf(err.Error()) | ||
} | ||
|
||
err = os.RemoveAll("testFile") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
//go:build !windows | ||
|
||
package osutil | ||
|
||
import ( | ||
"fmt" | ||
"io/fs" | ||
"os/user" | ||
"strconv" | ||
"syscall" | ||
) | ||
|
||
func FileUIDEqual(info fs.FileInfo, uid int) bool { | ||
if stat, ok := info.Sys().(*syscall.Stat_t); ok { | ||
path_uid := int(stat.Uid) | ||
if path_uid == uid { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func FileGIDEqual(info fs.FileInfo, gid int) bool { | ||
if stat, ok := info.Sys().(*syscall.Stat_t); ok { | ||
path_gid := int(stat.Gid) | ||
if path_gid == gid { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func FileUidMatch(info fs.FileInfo, path string, uid int) (err error) { | ||
currentUser, err := user.Current() | ||
if err != nil { | ||
return fmt.Errorf("failed to get details of current process owner. The error is: %w", err) | ||
} | ||
switch uid { | ||
case 0: | ||
currentUserUid, err := strconv.Atoi(currentUser.Uid) | ||
if err != nil { | ||
return fmt.Errorf("failed to convert uid %q to int. The error is: %w", currentUser.Uid, err) | ||
} | ||
if !FileUIDEqual(info, currentUserUid) { | ||
return fmt.Errorf("path %q is not owned by my uid %s", path, currentUser.Uid) | ||
} | ||
default: | ||
if !FileUIDEqual(info, uid) { | ||
return fmt.Errorf("path %q is not owned by uid %d", path, uid) | ||
} | ||
} | ||
return err | ||
} |
Oops, something went wrong.