// Copyright 2018 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 linux

import (
	"strings"
	"structs"
)

// A Capability represents the ability to perform a privileged operation.
type Capability int

// Capabilities defined by Linux. Taken from the kernel's
// include/uapi/linux/capability.h. See capabilities(7) or that file for more
// detailed capability descriptions.
const (
	CAP_CHOWN              = Capability(0)
	CAP_DAC_OVERRIDE       = Capability(1)
	CAP_DAC_READ_SEARCH    = Capability(2)
	CAP_FOWNER             = Capability(3)
	CAP_FSETID             = Capability(4)
	CAP_KILL               = Capability(5)
	CAP_SETGID             = Capability(6)
	CAP_SETUID             = Capability(7)
	CAP_SETPCAP            = Capability(8)
	CAP_LINUX_IMMUTABLE    = Capability(9)
	CAP_NET_BIND_SERVICE   = Capability(10)
	CAP_NET_BROADCAST      = Capability(11)
	CAP_NET_ADMIN          = Capability(12)
	CAP_NET_RAW            = Capability(13)
	CAP_IPC_LOCK           = Capability(14)
	CAP_IPC_OWNER          = Capability(15)
	CAP_SYS_MODULE         = Capability(16)
	CAP_SYS_RAWIO          = Capability(17)
	CAP_SYS_CHROOT         = Capability(18)
	CAP_SYS_PTRACE         = Capability(19)
	CAP_SYS_PACCT          = Capability(20)
	CAP_SYS_ADMIN          = Capability(21)
	CAP_SYS_BOOT           = Capability(22)
	CAP_SYS_NICE           = Capability(23)
	CAP_SYS_RESOURCE       = Capability(24)
	CAP_SYS_TIME           = Capability(25)
	CAP_SYS_TTY_CONFIG     = Capability(26)
	CAP_MKNOD              = Capability(27)
	CAP_LEASE              = Capability(28)
	CAP_AUDIT_WRITE        = Capability(29)
	CAP_AUDIT_CONTROL      = Capability(30)
	CAP_SETFCAP            = Capability(31)
	CAP_MAC_OVERRIDE       = Capability(32)
	CAP_MAC_ADMIN          = Capability(33)
	CAP_SYSLOG             = Capability(34)
	CAP_WAKE_ALARM         = Capability(35)
	CAP_BLOCK_SUSPEND      = Capability(36)
	CAP_AUDIT_READ         = Capability(37)
	CAP_PERFMON            = Capability(38)
	CAP_BPF                = Capability(39)
	CAP_CHECKPOINT_RESTORE = Capability(40)

	// CAP_LAST_CAP is the highest-numbered capability.
	// Search for "CAP_LAST_CAP" to find other places that need to change.
	CAP_LAST_CAP = CAP_CHECKPOINT_RESTORE
)

// Ok returns true if cp is a supported capability.
func (cp Capability) Ok() bool {
	return cp >= 0 && cp <= CAP_LAST_CAP
}

// String returns the capability name.
func (cp Capability) String() string {
	switch cp {
	case CAP_CHOWN:
		return "CAP_CHOWN"
	case CAP_DAC_OVERRIDE:
		return "CAP_DAC_OVERRIDE"
	case CAP_DAC_READ_SEARCH:
		return "CAP_DAC_READ_SEARCH"
	case CAP_FOWNER:
		return "CAP_FOWNER"
	case CAP_FSETID:
		return "CAP_FSETID"
	case CAP_KILL:
		return "CAP_KILL"
	case CAP_SETGID:
		return "CAP_SETGID"
	case CAP_SETUID:
		return "CAP_SETUID"
	case CAP_SETPCAP:
		return "CAP_SETPCAP"
	case CAP_LINUX_IMMUTABLE:
		return "CAP_LINUX_IMMUTABLE"
	case CAP_NET_BIND_SERVICE:
		return "CAP_NET_BIND_SERVICE"
	case CAP_NET_BROADCAST:
		return "CAP_NET_BROADCAST"
	case CAP_NET_ADMIN:
		return "CAP_NET_ADMIN"
	case CAP_NET_RAW:
		return "CAP_NET_RAW"
	case CAP_IPC_LOCK:
		return "CAP_IPC_LOCK"
	case CAP_IPC_OWNER:
		return "CAP_IPC_OWNER"
	case CAP_SYS_MODULE:
		return "CAP_SYS_MODULE"
	case CAP_SYS_RAWIO:
		return "CAP_SYS_RAWIO"
	case CAP_SYS_CHROOT:
		return "CAP_SYS_CHROOT"
	case CAP_SYS_PTRACE:
		return "CAP_SYS_PTRACE"
	case CAP_SYS_PACCT:
		return "CAP_SYS_PACCT"
	case CAP_SYS_ADMIN:
		return "CAP_SYS_ADMIN"
	case CAP_SYS_BOOT:
		return "CAP_SYS_BOOT"
	case CAP_SYS_NICE:
		return "CAP_SYS_NICE"
	case CAP_SYS_RESOURCE:
		return "CAP_SYS_RESOURCE"
	case CAP_SYS_TIME:
		return "CAP_SYS_TIME"
	case CAP_SYS_TTY_CONFIG:
		return "CAP_SYS_TTY_CONFIG"
	case CAP_MKNOD:
		return "CAP_MKNOD"
	case CAP_LEASE:
		return "CAP_LEASE"
	case CAP_AUDIT_WRITE:
		return "CAP_AUDIT_WRITE"
	case CAP_AUDIT_CONTROL:
		return "CAP_AUDIT_CONTROL"
	case CAP_SETFCAP:
		return "CAP_SETFCAP"
	case CAP_MAC_OVERRIDE:
		return "CAP_MAC_OVERRIDE"
	case CAP_MAC_ADMIN:
		return "CAP_MAC_ADMIN"
	case CAP_SYSLOG:
		return "CAP_SYSLOG"
	case CAP_WAKE_ALARM:
		return "CAP_WAKE_ALARM"
	case CAP_BLOCK_SUSPEND:
		return "CAP_BLOCK_SUSPEND"
	case CAP_AUDIT_READ:
		return "CAP_AUDIT_READ"
	default:
		return "UNKNOWN"
	}
}

// TrimmedString returns the capability name without the "CAP_" prefix.
func (cp Capability) TrimmedString() string {
	const capPrefix = "CAP_"
	s := cp.String()
	if !strings.HasPrefix(s, capPrefix) {
		return s
	}
	// This could use strings.TrimPrefix, but that function doesn't guarantee
	// that it won't allocate a new string, whereas string slicing does.
	// In the case of this function, since Capability.String returns a constant
	// string, the underlying set of bytes backing that string will never be
	// garbage-collected. Therefore, we always want to use a string slice that
	// points to this same constant set of bytes, rather than risking
	// allocating a new string.
	return s[len(capPrefix):]
}

// CapabilityFromString converts a string to a capability.
// If the capability doesn't exist, its second return value is `false`.
// The capability name is expected to include the "CAP_" prefix.
func CapabilityFromString(capability string) (Capability, bool) {
	for cp := Capability(0); cp <= CAP_LAST_CAP; cp++ {
		if !cp.Ok() {
			continue
		}
		if cp.String() == capability {
			return cp, true
		}
	}
	return -1, false
}

// AllCapabilities returns a list of all defined capabilities.
func AllCapabilities() []Capability {
	allCapapabilities := make([]Capability, 0, CAP_LAST_CAP+1)
	for cp := Capability(0); cp <= CAP_LAST_CAP; cp++ {
		if !cp.Ok() {
			continue
		}
		allCapapabilities = append(allCapapabilities, cp)
	}
	return allCapapabilities
}

// Version numbers used by the capget/capset syscalls, defined in Linux's
// include/uapi/linux/capability.h.
const (
	// LINUX_CAPABILITY_VERSION_1 causes the data pointer to be
	// interpreted as a pointer to a single cap_user_data_t. Since capability
	// sets are 64 bits and the "capability sets" in cap_user_data_t are 32
	// bits only, this causes the upper 32 bits to be implicitly 0.
	LINUX_CAPABILITY_VERSION_1 = 0x19980330

	// LINUX_CAPABILITY_VERSION_2 and LINUX_CAPABILITY_VERSION_3 cause the
	// data pointer to be interpreted as a pointer to an array of 2
	// cap_user_data_t, using the second to store the 32 MSB of each capability
	// set. Versions 2 and 3 are identical, but Linux printk's a warning on use
	// of version 2 due to a userspace API defect.
	LINUX_CAPABILITY_VERSION_2 = 0x20071026
	LINUX_CAPABILITY_VERSION_3 = 0x20080522

	// HighestCapabilityVersion is the highest supported
	// LINUX_CAPABILITY_VERSION_* version.
	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_FLAGS_EFFECTIVE allows the effective capability set to be
	// initialized with the permitted file capabilities.
	VFS_CAP_FLAGS_EFFECTIVE = 0x000001
	// 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
	// XATTR_CAPS_SZ_2 is sizeof(struct vfs_cap_data).
	XATTR_CAPS_SZ_2 = 20
	// XATTR_CAPS_SZ_3 is sizeof(struct vfs_ns_cap_data).
	XATTR_CAPS_SZ_3 = 24
)

// VfsCapData is equivalent to Linux's struct vfs_cap_data.
//
// +marshal
type VfsCapData struct {
	_             structs.HostLayout
	MagicEtc      uint32
	PermittedLo   uint32
	InheritableLo uint32
	PermittedHi   uint32
	InheritableHi uint32
}

// Permitted returns the permitted capability set.
func (c *VfsCapData) Permitted() uint64 {
	return uint64(c.PermittedHi)<<32 | uint64(c.PermittedLo)
}

// Inheritable returns the inheritable capability set.
func (c *VfsCapData) Inheritable() uint64 {
	return uint64(c.InheritableHi)<<32 | uint64(c.InheritableLo)
}

// IsRevision2 returns true if c is v2.
func (c *VfsCapData) IsRevision2() bool {
	return (c.MagicEtc & VFS_CAP_REVISION_MASK) == VFS_CAP_REVISION_2
}

// ToString marshals c into bytes and returns it as a string.
func (c *VfsCapData) ToString() string {
	buf := make([]byte, c.SizeBytes())
	c.MarshalUnsafe(buf)
	return string(buf)
}

// VfsNsCapData is equivalent to Linux's struct vfs_ns_cap_data.
//
// +marshal
type VfsNsCapData struct {
	_ structs.HostLayout
	VfsCapData
	RootID uint32
}

// ConvertToV3 converts c to v3 file capabilities.
func (c *VfsNsCapData) ConvertToV3(rootid uint32) {
	c.RootID = rootid
	if c.IsRevision2() {
		// Change to v3 while retaining the effective bit.
		c.MagicEtc = VFS_CAP_REVISION_3 | c.MagicEtc&VFS_CAP_FLAGS_EFFECTIVE
	}
}

// ConvertToV2 converts c to v2 file capabilities.
func (c *VfsNsCapData) ConvertToV2() {
	c.RootID = 0
	if !c.IsRevision2() {
		// Change to v2 while retaining the effective bit.
		c.MagicEtc = VFS_CAP_REVISION_2 | c.MagicEtc&VFS_CAP_FLAGS_EFFECTIVE
	}
}

// ToString marshals c into bytes and returns it as a string.
func (c *VfsNsCapData) ToString() string {
	if c.IsRevision2() {
		return c.VfsCapData.ToString()
	}
	buf := make([]byte, c.SizeBytes())
	c.MarshalUnsafe(buf)
	return string(buf)
}

// CapUserHeader is equivalent to Linux's cap_user_header_t.
//
// +marshal
type CapUserHeader struct {
	_       structs.HostLayout
	Version uint32
	Pid     int32
}

// CapUserData is equivalent to Linux's cap_user_data_t.
//
// +marshal slice:CapUserDataSlice
type CapUserData struct {
	_           structs.HostLayout
	Effective   uint32
	Permitted   uint32
	Inheritable uint32
}
