-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscript.go
116 lines (89 loc) · 3.09 KB
/
script.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package suexec
import (
"fmt"
"os"
"strings"
"syscall"
)
type Script struct {
path, cwd string
path_info, cwd_info os.FileInfo
}
func NewScript(path string, cwd string) (*Script, error) {
cwd_info, err := os.Lstat(cwd)
if err != nil || !cwd_info.IsDir() {
return nil, fmt.Errorf("cannot stat directory: (%s)\n", path)
}
if !cwd_info.IsDir() {
return nil, fmt.Errorf("cannot stat program: (%s) %s\n", path)
}
path_info, err := os.Lstat(path)
if err != nil {
return nil, fmt.Errorf("cannot stat program: (%s) %s\n", path)
}
if path_info.Mode()&os.ModeSymlink != 0 {
return nil, fmt.Errorf("cannot stat program: (%s) %s\n", path)
}
return &Script{path: path, cwd: cwd, path_info: path_info, cwd_info: cwd_info}, nil
}
func (self *Script) VerifyToSuexec(uid int, gid int) *SuexecError {
if !self.HasSecurePath() {
return NewSuexecError(104, "invalid command (%s)\n", self.path)
}
if self.IsDirWritableByOthers() {
return NewSuexecError(116, "directory is writable by others: (%s)\n", self.cwd)
}
if self.IsWritableByOthers() {
return NewSuexecError(118, "file is writable by others: (%s/%s)\n", self.cwd, self.path)
}
if self.IsSetuid() || self.IsSetgid() {
return NewSuexecError(119, "file is either setuid or setgid: (%s/%s)\n", self.path, self.cwd)
}
if !self.IsExecutable() {
return NewSuexecError(121, "file has no execute permission: (%s/%s)\n", self.cwd, self.path)
}
if !self.IfOwnerMatch(uid, gid) {
return NewSuexecError(121, "target uid/gid (%d/%d) mismatch with directory (%d/%d) or program (%d/%d)\n",
uid, gid,
self.path_info.Sys().(*syscall.Stat_t).Uid,
self.path_info.Sys().(*syscall.Stat_t).Gid,
self.cwd_info.Sys().(*syscall.Stat_t).Uid,
self.cwd_info.Sys().(*syscall.Stat_t).Gid)
}
return nil
}
func (self *Script) HasSecurePath() bool {
return !self.hasAbsolutePath() && !self.hasRelativePath()
}
func (self *Script) hasAbsolutePath() bool {
return strings.HasPrefix(self.path, "/")
}
func (self *Script) hasRelativePath() bool {
return strings.HasPrefix(self.path, "../") || strings.Index(self.path, "/../") > 0
}
func (self *Script) IsSetuid() bool {
return self.path_info.Mode()&os.ModeSetuid != 0
}
func (self *Script) IsSetgid() bool {
return self.path_info.Mode()&os.ModeSetgid != 0
}
func (self *Script) IsExecutable() bool {
return self.path_info.Sys().(*syscall.Stat_t).Mode&syscall.S_IXUSR != 0
}
func (self *Script) IsWritableByOthers() bool {
return self.path_info.Sys().(*syscall.Stat_t).Mode&syscall.S_IWOTH != 0 ||
self.path_info.Sys().(*syscall.Stat_t).Mode&syscall.S_IWGRP != 0
}
func (self *Script) IsDirWritableByOthers() bool {
return self.cwd_info.Sys().(*syscall.Stat_t).Mode&syscall.S_IWOTH != 0 ||
self.cwd_info.Sys().(*syscall.Stat_t).Mode&syscall.S_IWGRP != 0
}
func (self *Script) IfOwnerMatch(uid int, gid int) bool {
if uint32(uid) != self.cwd_info.Sys().(*syscall.Stat_t).Uid ||
uint32(gid) != self.cwd_info.Sys().(*syscall.Stat_t).Gid ||
uint32(uid) != self.path_info.Sys().(*syscall.Stat_t).Uid ||
uint32(gid) != self.path_info.Sys().(*syscall.Stat_t).Gid {
return false
}
return true
}