Skip to content

Commit 0c66b7e

Browse files
ayushr2gvisor-bot
authored andcommitted
Use vfs_cap_data and vfs_ns_cap_data structs defined in linux header.
This allows us to use the generated `Marshal/Unmarshal` functions and get rid of the manual binary encoding/decoding. Also fixed up vfs.VfsCapDataOf() to emulate Linux behavior more accurately. It now returns linuxerr errors instead of fmt.Errorf()s, because in future changes this code will be used on syscall paths. PiperOrigin-RevId: 754101576
1 parent 996d8c7 commit 0c66b7e

File tree

4 files changed

+124
-108
lines changed

4 files changed

+124
-108
lines changed

pkg/abi/linux/capability.go

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,9 @@ const (
230230
// Constants that are used by file capability extended attributes, defined
231231
// in Linux's include/uapi/linux/capability.h.
232232
const (
233-
// The flag decides the value of effective file capabilit
233+
// VFS_CAP_FLAGS_EFFECTIVE allows the effective capability set to be
234+
// initialized with the permitted file capabilities.
234235
VFS_CAP_FLAGS_EFFECTIVE = 0x000001
235-
// VFS_CAP_REVISION_1 was the original file capability implementation,
236-
// which supported 32-bit masks for file capabilities.
237-
VFS_CAP_REVISION_1 = 0x01000000
238236
// VFS_CAP_REVISION_2 allows for file capability masks that are 64
239237
// bits in size, and was necessary as the number of supported
240238
// capabilities grew beyond 32.
@@ -246,14 +244,41 @@ const (
246244
// extended attribute.
247245
VFS_CAP_REVISION_3 = 0x03000000
248246
VFS_CAP_REVISION_MASK = 0xFF000000
249-
// The encoded VFS_CAP_REVISION_1 data's number of bytes.
250-
XATTR_CAPS_SZ_1 = 12
251-
// The encoded VFS_CAP_REVISION_2 data's number of bytes.
247+
// XATTR_CAPS_SZ_2 is sizeof(struct vfs_cap_data).
252248
XATTR_CAPS_SZ_2 = 20
253-
// The encoded VFS_CAP_REVISION_3 data's number of bytes.
249+
// XATTR_CAPS_SZ_3 is sizeof(struct vfs_ns_cap_data).
254250
XATTR_CAPS_SZ_3 = 24
255251
)
256252

253+
// VfsCapData is equivalent to Linux's struct vfs_cap_data.
254+
//
255+
// +marshal
256+
type VfsCapData struct {
257+
MagicEtc uint32
258+
PermittedLo uint32
259+
InheritableLo uint32
260+
PermittedHi uint32
261+
InheritableHi uint32
262+
}
263+
264+
// Permitted returns the permitted capability set.
265+
func (c *VfsCapData) Permitted() uint64 {
266+
return uint64(c.PermittedHi)<<32 | uint64(c.PermittedLo)
267+
}
268+
269+
// Inheritable returns the inheritable capability set.
270+
func (c *VfsCapData) Inheritable() uint64 {
271+
return uint64(c.InheritableHi)<<32 | uint64(c.InheritableLo)
272+
}
273+
274+
// VfsNsCapData is equivalent to Linux's struct vfs_ns_cap_data.
275+
//
276+
// +marshal
277+
type VfsNsCapData struct {
278+
VfsCapData
279+
RootID uint32
280+
}
281+
257282
// CapUserHeader is equivalent to Linux's cap_user_header_t.
258283
//
259284
// +marshal

pkg/sentry/kernel/auth/capability_set.go

Lines changed: 21 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,16 @@
1515
package auth
1616

1717
import (
18-
"encoding/binary"
19-
"fmt"
20-
2118
"gvisor.dev/gvisor/pkg/abi/linux"
2219
"gvisor.dev/gvisor/pkg/bits"
2320
"gvisor.dev/gvisor/pkg/errors/linuxerr"
21+
"gvisor.dev/gvisor/pkg/log"
2422
)
2523

2624
// A CapabilitySet is a set of capabilities implemented as a bitset. The zero
2725
// value of CapabilitySet is a set containing no capabilities.
2826
type CapabilitySet uint64
2927

30-
// VfsCapData is equivalent to Linux's cpu_vfs_cap_data, defined
31-
// in Linux's include/linux/capability.h.
32-
type VfsCapData struct {
33-
MagicEtc uint32
34-
RootID uint32
35-
Permitted CapabilitySet
36-
Inheritable CapabilitySet
37-
}
38-
3928
// AllCapabilities is a CapabilitySet containing all valid capabilities.
4029
var AllCapabilities = CapabilitySetOf(linux.CAP_LAST_CAP+1) - 1
4130

@@ -57,47 +46,40 @@ func CapabilitySetOfMany(cps []linux.Capability) CapabilitySet {
5746
// VfsCapDataOf returns a VfsCapData containing the file capabilities for the given slice of bytes.
5847
// For each field of the cap data, which are in the structure of either vfs_cap_data or vfs_ns_cap_data,
5948
// the bytes are ordered in little endian.
60-
func VfsCapDataOf(data []byte) (VfsCapData, error) {
61-
var capData VfsCapData
49+
func VfsCapDataOf(data []byte) (linux.VfsNsCapData, error) {
6250
size := len(data)
63-
if size < linux.XATTR_CAPS_SZ_1 {
64-
return capData, fmt.Errorf("the size of security.capability is too small, actual size: %v", size)
51+
if size != linux.XATTR_CAPS_SZ_2 && size != linux.XATTR_CAPS_SZ_3 {
52+
log.Warningf("the size of security.capability is invalid: size=%d", size)
53+
return linux.VfsNsCapData{}, linuxerr.EINVAL
54+
}
55+
var capData linux.VfsNsCapData
56+
if size == linux.XATTR_CAPS_SZ_3 {
57+
capData.UnmarshalUnsafe(data)
58+
} else {
59+
capData.VfsCapData.UnmarshalUnsafe(data)
60+
// rootid = 0 is correct for version 2 file capabilities.
6561
}
66-
capData.MagicEtc = binary.LittleEndian.Uint32(data[:4])
67-
capData.Permitted = CapabilitySet(binary.LittleEndian.Uint32(data[4:8]))
68-
capData.Inheritable = CapabilitySet(binary.LittleEndian.Uint32(data[8:12]))
69-
// The version of the file capabilities takes first 4 bytes of the given
70-
// slice.
71-
version := capData.MagicEtc & linux.VFS_CAP_REVISION_MASK
72-
switch {
73-
case version == linux.VFS_CAP_REVISION_3 && size >= linux.XATTR_CAPS_SZ_3:
74-
// Like version 2 file capabilities, version 3 capability
75-
// masks are 64 bits in size. In addition, version 3 has
76-
// the root user ID of namespace, which is encoded in the
77-
// security.capability extended attribute.
78-
capData.RootID = binary.LittleEndian.Uint32(data[20:24])
79-
fallthrough
80-
case version == linux.VFS_CAP_REVISION_2 && size >= linux.XATTR_CAPS_SZ_2:
81-
capData.Permitted += CapabilitySet(binary.LittleEndian.Uint32(data[12:16])) << 32
82-
capData.Inheritable += CapabilitySet(binary.LittleEndian.Uint32(data[16:20])) << 32
83-
default:
84-
return VfsCapData{}, fmt.Errorf("VFS_CAP_REVISION_%v with cap data size %v is not supported", version, size)
62+
// See security/commoncap.c:validheader().
63+
if sansflags := capData.MagicEtc & ^uint32(linux.VFS_CAP_FLAGS_EFFECTIVE); (size == linux.XATTR_CAPS_SZ_2 && sansflags != linux.VFS_CAP_REVISION_2) ||
64+
(size == linux.XATTR_CAPS_SZ_3 && sansflags != linux.VFS_CAP_REVISION_3) {
65+
log.Warningf("the magic header of security.capability is invalid: magic=%#x, size=%d", capData.MagicEtc, size)
66+
return linux.VfsNsCapData{}, linuxerr.EINVAL
8567
}
8668
return capData, nil
8769
}
8870

8971
// CapsFromVfsCaps returns a copy of the given creds with new capability sets
9072
// by applying the file capability that is specified by capData.
91-
func CapsFromVfsCaps(capData VfsCapData, creds *Credentials) (*Credentials, error) {
73+
func CapsFromVfsCaps(capData linux.VfsNsCapData, creds *Credentials) (*Credentials, error) {
9274
// If the real or effective user ID of the process is root,
9375
// the file inheritable and permitted sets are ignored from
9476
// `Capabilities and execution of programs by root` at capabilities(7).
9577
if root := creds.UserNamespace.MapToKUID(RootUID); creds.EffectiveKUID == root || creds.RealKUID == root {
9678
return creds, nil
9779
}
9880
effective := (capData.MagicEtc & linux.VFS_CAP_FLAGS_EFFECTIVE) > 0
99-
permittedCaps := (capData.Permitted & creds.BoundingCaps) |
100-
(capData.Inheritable & creds.InheritableCaps)
81+
permittedCaps := (CapabilitySet(capData.Permitted()) & creds.BoundingCaps) |
82+
(CapabilitySet(capData.Inheritable()) & creds.InheritableCaps)
10183
// P'(effective) = effective ? P'(permitted) : P'(ambient).
10284
// The ambient capabilities has not supported yet in gVisor,
10385
// set effective capabilities to 0 when effective bit is false.
@@ -106,7 +88,7 @@ func CapsFromVfsCaps(capData VfsCapData, creds *Credentials) (*Credentials, erro
10688
effectiveCaps = permittedCaps
10789
}
10890
// Insufficient to execute correctly.
109-
if (capData.Permitted & ^permittedCaps) != 0 {
91+
if (CapabilitySet(capData.Permitted()) & ^permittedCaps) != 0 {
11092
return nil, linuxerr.EPERM
11193
}
11294
// If the capabilities don't change, it will return the creds'

pkg/sentry/kernel/auth/capability_set_test.go

Lines changed: 67 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
package auth
1616

1717
import (
18-
"fmt"
1918
"testing"
2019

2120
"gvisor.dev/gvisor/pkg/abi/linux"
@@ -40,37 +39,62 @@ func credentialsWithCaps(creds *Credentials, permittedCaps, inheritableCaps, eff
4039
return newCreds
4140
}
4241

42+
func vfsNsCapDataFrom(effective bool, rootid uint32, permitted, inheritable CapabilitySet) linux.VfsNsCapData {
43+
capData := vfsCapDataFrom(effective, permitted, inheritable)
44+
capData.MagicEtc = linux.VFS_CAP_REVISION_3
45+
if effective {
46+
capData.MagicEtc |= linux.VFS_CAP_FLAGS_EFFECTIVE
47+
}
48+
capData.RootID = rootid
49+
return capData
50+
}
51+
52+
func vfsCapDataFrom(effective bool, permitted, inheritable CapabilitySet) linux.VfsNsCapData {
53+
var capData linux.VfsNsCapData
54+
capData.MagicEtc = linux.VFS_CAP_REVISION_2
55+
if effective {
56+
capData.MagicEtc |= linux.VFS_CAP_FLAGS_EFFECTIVE
57+
}
58+
capData.PermittedLo = uint32(permitted & 0xffffffff)
59+
capData.PermittedHi = uint32(permitted >> 32)
60+
capData.InheritableLo = uint32(inheritable & 0xffffffff)
61+
capData.InheritableHi = uint32(inheritable >> 32)
62+
return capData
63+
}
64+
4365
func TestCapsFromVfsCaps(t *testing.T) {
4466
for _, tst := range []struct {
4567
name string
46-
capData VfsCapData
68+
capData linux.VfsNsCapData
4769
creds *Credentials
4870
wantCaps TaskCapabilities
4971
wantErr error
5072
}{
5173
{
5274
name: "TestRootCredential",
53-
capData: VfsCapData{
54-
MagicEtc: 0x2000001,
55-
Permitted: CapabilitySetOf(linux.CAP_NET_ADMIN),
56-
Inheritable: CapabilitySetOf(linux.CAP_NET_ADMIN),
57-
},
58-
creds: credentialsWithCaps(NewRootCredentials(NewRootUserNamespace()), AllCapabilities, CapabilitySetOf(linux.CAP_NET_RAW), AllCapabilities, CapabilitySetOf(linux.CAP_SYSLOG)),
75+
capData: vfsCapDataFrom(
76+
true, // effective
77+
CapabilitySetOf(linux.CAP_NET_ADMIN), // permitted
78+
CapabilitySetOf(linux.CAP_NET_ADMIN)), // inheritable
79+
creds: credentialsWithCaps(
80+
NewRootCredentials(NewRootUserNamespace()),
81+
AllCapabilities,
82+
CapabilitySetOf(linux.CAP_NET_RAW),
83+
AllCapabilities,
84+
CapabilitySetOf(linux.CAP_SYSLOG)),
5985
wantCaps: TaskCapabilities{
6086
PermittedCaps: AllCapabilities,
6187
InheritableCaps: CapabilitySetOf(linux.CAP_NET_RAW),
6288
EffectiveCaps: AllCapabilities,
6389
BoundingCaps: CapabilitySetOf(linux.CAP_SYSLOG),
6490
},
65-
wantErr: nil,
6691
},
6792
{
6893
name: "TestPermittedAndInheritableCaps",
69-
capData: VfsCapData{
70-
MagicEtc: 0x2000001,
71-
Permitted: CapabilitySetOfMany([]linux.Capability{linux.CAP_CHOWN, linux.CAP_SETUID}),
72-
Inheritable: CapabilitySetOfMany([]linux.Capability{linux.CAP_CHOWN, linux.CAP_SETGID}),
73-
},
94+
capData: vfsCapDataFrom(
95+
true, // effective
96+
CapabilitySetOfMany([]linux.Capability{linux.CAP_CHOWN, linux.CAP_SETUID}), // permitted
97+
CapabilitySetOfMany([]linux.Capability{linux.CAP_CHOWN, linux.CAP_SETGID})), // inheritable
7498
creds: credentialsWithCaps(
7599
NewUserCredentials(123, 321, nil, nil, NewRootUserNamespace()),
76100
AllCapabilities,
@@ -83,15 +107,13 @@ func TestCapsFromVfsCaps(t *testing.T) {
83107
EffectiveCaps: CapabilitySetOfMany([]linux.Capability{linux.CAP_CHOWN, linux.CAP_SETUID, linux.CAP_SETGID}),
84108
BoundingCaps: AllCapabilities,
85109
},
86-
wantErr: nil,
87110
},
88111
{
89112
name: "TestEffectiveBitOff",
90-
capData: VfsCapData{
91-
MagicEtc: 0x2000000,
92-
Permitted: CapabilitySetOfMany([]linux.Capability{linux.CAP_CHOWN, linux.CAP_SETUID}),
93-
Inheritable: CapabilitySetOfMany([]linux.Capability{linux.CAP_CHOWN, linux.CAP_SETGID}),
94-
},
113+
capData: vfsCapDataFrom(
114+
false, // effective
115+
CapabilitySetOfMany([]linux.Capability{linux.CAP_CHOWN, linux.CAP_SETUID}), // permitted
116+
CapabilitySetOfMany([]linux.Capability{linux.CAP_CHOWN, linux.CAP_SETGID})), // inheritable
95117
creds: credentialsWithCaps(
96118
NewUserCredentials(123, 321, nil, nil, NewRootUserNamespace()),
97119
AllCapabilities,
@@ -104,23 +126,20 @@ func TestCapsFromVfsCaps(t *testing.T) {
104126
EffectiveCaps: 0,
105127
BoundingCaps: AllCapabilities,
106128
},
107-
wantErr: nil,
108129
},
109130
{
110131
name: "TestInsufficientCaps",
111-
capData: VfsCapData{
112-
MagicEtc: 0x2000001,
113-
Permitted: CapabilitySetOfMany([]linux.Capability{linux.CAP_CHOWN, linux.CAP_SETUID}),
114-
Inheritable: CapabilitySetOfMany([]linux.Capability{linux.CAP_CHOWN}),
115-
},
132+
capData: vfsCapDataFrom(
133+
true, // effective
134+
CapabilitySetOfMany([]linux.Capability{linux.CAP_CHOWN, linux.CAP_SETUID}), // permitted
135+
CapabilitySetOf(linux.CAP_CHOWN)), // inheritable
116136
creds: credentialsWithCaps(
117137
NewUserCredentials(123, 321, nil, nil, NewRootUserNamespace()),
118138
AllCapabilities,
119139
AllCapabilities,
120140
AllCapabilities,
121141
CapabilitySetOf(linux.CAP_CHOWN)),
122-
wantCaps: TaskCapabilities{},
123-
wantErr: linuxerr.EPERM,
142+
wantErr: linuxerr.EPERM,
124143
},
125144
} {
126145
t.Run(tst.name, func(t *testing.T) {
@@ -150,47 +169,28 @@ func TestVfsCapData(t *testing.T) {
150169
for _, tst := range []struct {
151170
name string
152171
data []byte
153-
capData VfsCapData
172+
capData linux.VfsNsCapData
154173
wantErr error
155174
}{
156175
{
157176
name: "VfsCapRevision1",
158-
data: []byte{0, 0, 0, 1, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
159-
capData: VfsCapData{},
160-
wantErr: fmt.Errorf("VFS_CAP_REVISION_%v with cap data size %v is not supported", 0x1000000, 20),
177+
data: []byte{0, 0, 0, 1, 0, 16, 0, 0, 0, 0, 0, 0},
178+
wantErr: linuxerr.EINVAL,
161179
},
162180
{
163-
name: "VfsCapRevision2",
164-
data: []byte{1, 0, 0, 2, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0},
165-
capData: VfsCapData{
166-
MagicEtc: 0x2000001,
167-
Permitted: CapabilitySetOf(linux.CAP_NET_RAW),
168-
Inheritable: CapabilitySetOf(linux.CAP_SYSLOG),
169-
},
170-
wantErr: nil,
181+
name: "VfsCapRevision2WithEffective",
182+
data: []byte{1, 0, 0, 2, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0},
183+
capData: vfsCapDataFrom(true, CapabilitySetOf(linux.CAP_NET_RAW), CapabilitySetOf(linux.CAP_SYSLOG)),
171184
},
172185
{
173-
name: "VfsCapRevision3",
174-
data: []byte{0, 0, 0, 3, 0, 0, 0, 0, 0, 16, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
175-
capData: VfsCapData{
176-
MagicEtc: 0x3000000,
177-
RootID: 1,
178-
Permitted: CapabilitySetOf(linux.CAP_SYSLOG),
179-
Inheritable: CapabilitySetOf(linux.CAP_NET_ADMIN),
180-
},
181-
wantErr: nil,
186+
name: "VfsCapRevision3",
187+
data: []byte{0, 0, 0, 3, 0, 0, 0, 0, 0, 16, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
188+
capData: vfsNsCapDataFrom(false, 1, CapabilitySetOf(linux.CAP_SYSLOG), CapabilitySetOf(linux.CAP_NET_ADMIN)),
182189
},
183190
{
184191
name: "VfsCapRevisionNotSupported",
185192
data: []byte{0, 0, 0, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0},
186-
capData: VfsCapData{},
187-
wantErr: fmt.Errorf("VFS_CAP_REVISION_%v with cap data size %v is not supported", 0xf000000, 20),
188-
},
189-
{
190-
name: "VfsInvalidInput",
191-
data: []byte{0, 0, 0, 0},
192-
capData: VfsCapData{},
193-
wantErr: fmt.Errorf("the size of security.capability is too small, actual size: %v", 4),
193+
wantErr: linuxerr.EINVAL,
194194
},
195195
} {
196196
t.Run(tst.name, func(t *testing.T) {
@@ -200,11 +200,20 @@ func TestVfsCapData(t *testing.T) {
200200
t.Errorf("VfsCapDataOf(%v) returned unexpected error %v", tst.data, tst.wantErr)
201201
}
202202
if tst.capData != capData {
203-
t.Errorf("VfsCapDataOf(%v) = %v, want %v", tst.data, capData, tst.capData)
203+
t.Errorf("VfsCapDataOf(%v) = %+v, want %+v", tst.data, capData, tst.capData)
204204
}
205205
} else if tst.wantErr == nil || tst.wantErr.Error() != err.Error() {
206206
t.Errorf("VfsCapDataOf(%v) returned error %v, wantErr: %v", tst.data, err, tst.wantErr)
207207
}
208208
})
209209
}
210210
}
211+
212+
func TestXattrCapsSizeBytes(t *testing.T) {
213+
if got := (*linux.VfsCapData)(nil).SizeBytes(); got != linux.XATTR_CAPS_SZ_2 {
214+
t.Errorf("XATTR_CAPS_SZ_2 = %v, got %v", linux.XATTR_CAPS_SZ_2, got)
215+
}
216+
if got := (*linux.VfsNsCapData)(nil).SizeBytes(); got != linux.XATTR_CAPS_SZ_3 {
217+
t.Errorf("XATTR_CAPS_SZ_3 = %v, got %v", linux.XATTR_CAPS_SZ_3, got)
218+
}
219+
}

pkg/sentry/kernel/kernel.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,15 +1088,15 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID,
10881088
if se != nil {
10891089
return nil, 0, errors.New(se.String())
10901090
}
1091-
var capData auth.VfsCapData
1091+
var vfsCaps linux.VfsNsCapData
10921092
if len(image.FileCaps()) != 0 {
10931093
var err error
1094-
capData, err = auth.VfsCapDataOf([]byte(image.FileCaps()))
1094+
vfsCaps, err = auth.VfsCapDataOf([]byte(image.FileCaps()))
10951095
if err != nil {
10961096
return nil, 0, err
10971097
}
10981098
}
1099-
creds, err := auth.CapsFromVfsCaps(capData, args.Credentials)
1099+
creds, err := auth.CapsFromVfsCaps(vfsCaps, args.Credentials)
11001100
if err != nil {
11011101
return nil, 0, err
11021102
}

0 commit comments

Comments
 (0)