mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add a function to decode file capabilities extended attributes.
The attributes are usually retrieved via API like Fgetxattr, Getxattr in the form of a slice of bytes. PiperOrigin-RevId: 605466585
This commit is contained in:
@@ -227,6 +227,31 @@ const (
|
||||
HighestCapabilityVersion = LINUX_CAPABILITY_VERSION_3
|
||||
)
|
||||
|
||||
// Constants that are used by file capability extended attributes, defined
|
||||
// in Linux's include/uapi/linux/capability.h.
|
||||
const (
|
||||
// VFS_CAP_REVISION_1 was the original file capability implementation,
|
||||
// which supported 32-bit masks for file capabilities.
|
||||
VFS_CAP_REVISION_1 = 0x01000000
|
||||
// VFS_CAP_REVISION_2 allows for file capability masks that are 64
|
||||
// bits in size, and was necessary as the number of supported
|
||||
// capabilities grew beyond 32.
|
||||
VFS_CAP_REVISION_2 = 0x02000000
|
||||
// VFS_CAP_REVISION_3 are provided to support namespaced file capabilities.
|
||||
// As with version 2 file capabilities, version 3 capability
|
||||
// masks are 64 bits in size. But in addition, the root user
|
||||
// ID of namespace is encoded in the security.capability
|
||||
// extended attribute.
|
||||
VFS_CAP_REVISION_3 = 0x03000000
|
||||
VFS_CAP_REVISION_MASK = 0xFF000000
|
||||
// The encoded VFS_CAP_REVISION_1 data's number of bytes.
|
||||
XATTR_CAPS_SZ_1 = 12
|
||||
// The encoded VFS_CAP_REVISION_2 data's number of bytes.
|
||||
XATTR_CAPS_SZ_2 = 20
|
||||
// The encoded VFS_CAP_REVISION_3 data's number of bytes.
|
||||
XATTR_CAPS_SZ_3 = 24
|
||||
)
|
||||
|
||||
// CapUserHeader is equivalent to Linux's cap_user_header_t.
|
||||
//
|
||||
// +marshal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//pkg/sync/locking:locking.bzl", "declare_mutex", "declare_rwmutex")
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
load("//tools:defs.bzl", "go_library", "go_test")
|
||||
load("//tools/go_generics:defs.bzl", "go_template_instance")
|
||||
|
||||
package(
|
||||
@@ -102,3 +102,10 @@ go_library(
|
||||
"//pkg/sync/locking",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "auth_test",
|
||||
srcs = ["capability_set_test.go"],
|
||||
library = ":auth",
|
||||
deps = ["//pkg/abi/linux"],
|
||||
)
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/bits"
|
||||
)
|
||||
@@ -23,6 +26,15 @@ import (
|
||||
// value of CapabilitySet is a set containing no capabilities.
|
||||
type CapabilitySet uint64
|
||||
|
||||
// VfsCapData is equivalent to Linux's cpu_vfs_cap_data, defined
|
||||
// in Linux's include/linux/capability.h.
|
||||
type VfsCapData struct {
|
||||
MagicEtc uint32
|
||||
RootID uint32
|
||||
Permitted CapabilitySet
|
||||
Inheritable CapabilitySet
|
||||
}
|
||||
|
||||
// AllCapabilities is a CapabilitySet containing all valid capabilities.
|
||||
var AllCapabilities = CapabilitySetOf(linux.CAP_LAST_CAP+1) - 1
|
||||
|
||||
@@ -41,6 +53,38 @@ func CapabilitySetOfMany(cps []linux.Capability) CapabilitySet {
|
||||
return CapabilitySet(cs)
|
||||
}
|
||||
|
||||
// VfsCapDataOf returns a VfsCapData containing the file capabilities for the given slice of bytes.
|
||||
// For each field of the cap data, which are in the structure of either vfs_cap_data or vfs_ns_cap_data,
|
||||
// the bytes are ordered in little endian.
|
||||
func VfsCapDataOf(data []byte) (VfsCapData, error) {
|
||||
var capData VfsCapData
|
||||
size := len(data)
|
||||
if size < linux.XATTR_CAPS_SZ_1 {
|
||||
return capData, fmt.Errorf("the size of security.capability is too small, actual size: %v", size)
|
||||
}
|
||||
capData.MagicEtc = binary.LittleEndian.Uint32(data[:4])
|
||||
capData.Permitted = CapabilitySet(binary.LittleEndian.Uint32(data[4:8]))
|
||||
capData.Inheritable = CapabilitySet(binary.LittleEndian.Uint32(data[8:12]))
|
||||
// The version of the file capabilities takes first 4 bytes of the given
|
||||
// slice.
|
||||
version := capData.MagicEtc & linux.VFS_CAP_REVISION_MASK
|
||||
switch {
|
||||
case version == linux.VFS_CAP_REVISION_3 && size == linux.XATTR_CAPS_SZ_3:
|
||||
// Like version 2 file capabilities, version 3 capability
|
||||
// masks are 64 bits in size. In addition, version 3 has
|
||||
// the root user ID of namespace, which is encoded in the
|
||||
// security.capability extended attribute.
|
||||
capData.RootID = binary.LittleEndian.Uint32(data[20:24])
|
||||
fallthrough
|
||||
case version == linux.VFS_CAP_REVISION_2 && size == linux.XATTR_CAPS_SZ_2:
|
||||
capData.Permitted += CapabilitySet(binary.LittleEndian.Uint32(data[12:16])) << 32
|
||||
capData.Inheritable += CapabilitySet(binary.LittleEndian.Uint32(data[16:20])) << 32
|
||||
default:
|
||||
return VfsCapData{}, fmt.Errorf("VFS_CAP_REVISION_%v with cap data size %v is not supported", version, size)
|
||||
}
|
||||
return capData, nil
|
||||
}
|
||||
|
||||
// TaskCapabilities represents all the capability sets for a task. Each of these
|
||||
// sets is explained in greater detail in capabilities(7).
|
||||
type TaskCapabilities struct {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2024 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
)
|
||||
|
||||
func TestVfsCapData(t *testing.T) {
|
||||
for _, tst := range []struct {
|
||||
name string
|
||||
data []byte
|
||||
capData VfsCapData
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "VfsCapRevision1",
|
||||
data: []byte{0, 0, 0, 1, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
capData: VfsCapData{},
|
||||
wantErr: fmt.Errorf("VFS_CAP_REVISION_%v with cap data size %v is not supported", 0x1000000, 20),
|
||||
},
|
||||
{
|
||||
name: "VfsCapRevision2",
|
||||
data: []byte{1, 0, 0, 2, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0},
|
||||
capData: VfsCapData{
|
||||
MagicEtc: 0x2000001,
|
||||
Permitted: CapabilitySetOf(linux.CAP_NET_RAW),
|
||||
Inheritable: CapabilitySetOf(linux.CAP_SYSLOG),
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "VfsCapRevision3",
|
||||
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},
|
||||
capData: VfsCapData{
|
||||
MagicEtc: 0x3000000,
|
||||
RootID: 1,
|
||||
Permitted: CapabilitySetOf(linux.CAP_SYSLOG),
|
||||
Inheritable: CapabilitySetOf(linux.CAP_NET_ADMIN),
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "VfsCapRevisionNotSupported",
|
||||
data: []byte{0, 0, 0, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0},
|
||||
capData: VfsCapData{},
|
||||
wantErr: fmt.Errorf("VFS_CAP_REVISION_%v with cap data size %v is not supported", 0xf000000, 20),
|
||||
},
|
||||
{
|
||||
name: "VfsInvalidInput",
|
||||
data: []byte{0, 0, 0, 0},
|
||||
capData: VfsCapData{},
|
||||
wantErr: fmt.Errorf("the size of security.capability is too small, actual size: %v", 4),
|
||||
},
|
||||
} {
|
||||
t.Run(tst.name, func(t *testing.T) {
|
||||
capData, err := VfsCapDataOf(tst.data)
|
||||
if err == nil {
|
||||
if tst.wantErr != nil {
|
||||
t.Errorf("VfsCapDataOf(%v) returned unexpected error %v", tst.data, tst.wantErr)
|
||||
}
|
||||
if tst.capData != capData {
|
||||
t.Errorf("VfsCapDataOf(%v) = %v, want %v", tst.data, capData, tst.capData)
|
||||
}
|
||||
} else if tst.wantErr == nil || tst.wantErr.Error() != err.Error() {
|
||||
t.Errorf("VfsCapDataOf(%v) returned error %v, wantErr: %v", tst.data, err, tst.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user