12
12
// See the License for the specific language governing permissions and
13
13
// limitations under the License.
14
14
15
+ //go:build linux && cgo
15
16
// +build linux,cgo
16
17
17
18
package handlers
@@ -28,6 +29,7 @@ import (
28
29
"github.com/kinvolk/seccompagent/pkg/nsenter"
29
30
"github.com/kinvolk/seccompagent/pkg/readarg"
30
31
"github.com/kinvolk/seccompagent/pkg/registry"
32
+ "github.com/kinvolk/seccompagent/pkg/userns"
31
33
)
32
34
33
35
var _ = nsenter .RegisterModule ("mount" , runMountInNamespaces )
@@ -37,6 +39,8 @@ type mountModuleParams struct {
37
39
Source string `json:"source,omitempty"`
38
40
Dest string `json:"dest,omitempty"`
39
41
Filesystem string `json:"filesystem,omitempty"`
42
+ Flags int64 `json:"flags,omitempty"`
43
+ Options string `json:"options,omitempty"`
40
44
}
41
45
42
46
func runMountInNamespaces (param []byte ) string {
@@ -46,14 +50,14 @@ func runMountInNamespaces(param []byte) string {
46
50
return fmt .Sprintf ("%d" , int (unix .ENOSYS ))
47
51
}
48
52
49
- err = unix .Mount (params .Source , params .Dest , params .Filesystem , 0 , "" )
53
+ err = unix .Mount (params .Source , params .Dest , params .Filesystem , 0 , params . Options )
50
54
if err != nil {
51
55
return fmt .Sprintf ("%d" , int (err .(unix.Errno )))
52
56
}
53
57
return "0"
54
58
}
55
59
56
- func Mount (allowedFilesystems map [string ]struct {}) registry.HandlerFunc {
60
+ func Mount (allowedFilesystems map [string ]struct {}, requireUserNamespaceAdmin bool ) registry.HandlerFunc {
57
61
return func (fd libseccomp.ScmpFd , req * libseccomp.ScmpNotifReq ) (result registry.HandlerResult ) {
58
62
memFile , err := readarg .OpenMem (req .Pid )
59
63
if err != nil {
@@ -96,12 +100,17 @@ func Mount(allowedFilesystems map[string]struct{}) registry.HandlerFunc {
96
100
return registry .HandlerResultErrno (unix .EFAULT )
97
101
}
98
102
103
+ // We don't handle flags, we may want to consider allowing a few.
104
+ // This is here so the debug logging makes it possible to see flags used.
105
+ flags := int64 (req .Data .Args [3 ])
106
+
99
107
log .WithFields (log.Fields {
100
108
"fd" : fd ,
101
109
"pid" : req .Pid ,
102
110
"source" : source ,
103
111
"dest" : dest ,
104
112
"filesystem" : filesystem ,
113
+ "flags" : flags ,
105
114
}).Debug ("Mount" )
106
115
107
116
if _ , ok := allowedFilesystems [filesystem ]; ! ok {
@@ -110,11 +119,70 @@ func Mount(allowedFilesystems map[string]struct{}) registry.HandlerFunc {
110
119
return registry .HandlerResultContinue ()
111
120
}
112
121
122
+ var options string
123
+ if req .Data .Args [4 ] != 0 /* NULL */ && filesystem != "sysfs" {
124
+ // Get options, we assume because this is specified in
125
+ // allowedFilesystems that the data argument to mount(2)
126
+ // is a string so this is safe now. We ignore options for sysfs, as it
127
+ // doesn't define options.
128
+ options , err = readarg .ReadString (memFile , int64 (req .Data .Args [4 ]))
129
+ if err != nil {
130
+ log .WithFields (log.Fields {
131
+ "fd" : fd ,
132
+ "pid" : req .Pid ,
133
+ "arg" : 4 ,
134
+ "err" : err ,
135
+ }).Error ("Cannot read argument" )
136
+ return registry .HandlerResultErrno (unix .EFAULT )
137
+ }
138
+
139
+ // Log this at trace level only as it could have user credentials.
140
+ log .WithFields (log.Fields {
141
+ "fd" : fd ,
142
+ "pid" : req .Pid ,
143
+ "source" : source ,
144
+ "dest" : dest ,
145
+ "filesystem" : filesystem ,
146
+ "flags" : flags ,
147
+ "options" : options ,
148
+ }).Trace ("Handle mount" )
149
+ }
150
+
151
+ if requireUserNamespaceAdmin {
152
+ ok , err := userns .IsPIDAdminCapable (req .Pid )
153
+ if err != nil {
154
+ log .WithFields (log.Fields {
155
+ "fd" : fd ,
156
+ "pid" : req .Pid ,
157
+ "err" : err ,
158
+ }).Error ("Cannot check user namespace capabilities" )
159
+ return registry .HandlerResultErrno (unix .EFAULT )
160
+ }
161
+ if ! ok {
162
+ log .WithFields (log.Fields {
163
+ "fd" : fd ,
164
+ "pid" : req .Pid ,
165
+ }).Info ("Mount attempted without CAP_SYS_ADMIN" )
166
+ return registry .HandlerResultErrno (unix .EPERM )
167
+ }
168
+
169
+ // Ensure the notification is still valid after checking user namespace capabilities.
170
+ if err := libseccomp .NotifIDValid (fd , req .ID ); err != nil {
171
+ log .WithFields (log.Fields {
172
+ "fd" : fd ,
173
+ "req" : req ,
174
+ "err" : err ,
175
+ }).Debug ("Notification no longer valid" )
176
+ return registry .HandlerResultIntr ()
177
+ }
178
+ }
179
+
113
180
params := mountModuleParams {
114
181
Module : "mount" ,
115
182
Source : source ,
116
183
Dest : dest ,
117
184
Filesystem : filesystem ,
185
+ Options : options ,
118
186
}
119
187
120
188
mntns , err := nsenter .OpenNamespace (req .Pid , "mnt" )
0 commit comments