671 lines
23 KiB
Swift
671 lines
23 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
// Copyright © 2025-2026 Apple Inc. and the Containerization project 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
|
|
//
|
|
// https://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.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import CShim
|
|
|
|
#if canImport(FoundationEssentials)
|
|
import FoundationEssentials
|
|
#else
|
|
import Foundation
|
|
#endif
|
|
|
|
// MARK: - Configuration Types
|
|
|
|
public enum CapabilityParsingError: Swift.Error, CustomStringConvertible {
|
|
case invalidCapabilitySet(String)
|
|
case invalidCapabilityName(String)
|
|
|
|
public var description: String {
|
|
switch self {
|
|
case .invalidCapabilitySet(let value):
|
|
return "invalid CapabilitySet value '\(value)'"
|
|
case .invalidCapabilityName(let value):
|
|
return "invalid CapabilityName '\(value)'"
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct CapabilitySet: Sendable, Hashable {
|
|
private enum Value: Hashable, Sendable, CaseIterable {
|
|
case bounding
|
|
case effective
|
|
case inheritable
|
|
case permitted
|
|
case ambient
|
|
}
|
|
|
|
private var value: Value
|
|
private init(_ value: Value) {
|
|
self.value = value
|
|
}
|
|
|
|
public init(rawValue: String) throws {
|
|
let values = Value.allCases.reduce(into: [String: Value]()) {
|
|
$0[String(describing: $1).lowercased()] = $1
|
|
}
|
|
|
|
guard let match = values[rawValue.lowercased()] else {
|
|
throw CapabilityParsingError.invalidCapabilitySet(rawValue)
|
|
}
|
|
self.value = match
|
|
}
|
|
|
|
public static var bounding: Self { Self(.bounding) }
|
|
public static var effective: Self { Self(.effective) }
|
|
public static var inheritable: Self { Self(.inheritable) }
|
|
public static var permitted: Self { Self(.permitted) }
|
|
public static var ambient: Self { Self(.ambient) }
|
|
}
|
|
|
|
extension CapabilitySet: CustomStringConvertible {
|
|
public var description: String {
|
|
String(describing: self.value)
|
|
}
|
|
}
|
|
|
|
public struct CapabilityName: Sendable, Hashable {
|
|
private enum Value: Hashable, Sendable, CaseIterable {
|
|
case chown
|
|
case dacOverride
|
|
case dacReadSearch
|
|
case fowner
|
|
case fsetid
|
|
case kill
|
|
case setgid
|
|
case setuid
|
|
case setpcap
|
|
case linuxImmutable
|
|
case netBindService
|
|
case netBroadcast
|
|
case netAdmin
|
|
case netRaw
|
|
case ipcLock
|
|
case ipcOwner
|
|
case sysModule
|
|
case sysRawio
|
|
case sysChroot
|
|
case sysPtrace
|
|
case sysPacct
|
|
case sysAdmin
|
|
case sysBoot
|
|
case sysNice
|
|
case sysResource
|
|
case sysTime
|
|
case sysTtyConfig
|
|
case mknod
|
|
case lease
|
|
case auditWrite
|
|
case auditControl
|
|
case setfcap
|
|
case macOverride
|
|
case macAdmin
|
|
case syslog
|
|
case wakeAlarm
|
|
case blockSuspend
|
|
case auditRead
|
|
case perfmon
|
|
case bpf
|
|
case checkpointRestore
|
|
}
|
|
|
|
private var value: Value
|
|
private init(_ value: Value) {
|
|
self.value = value
|
|
}
|
|
|
|
public init(rawValue: String) throws {
|
|
let uppercased = rawValue.uppercased()
|
|
let normalized = uppercased.hasPrefix("CAP_") ? uppercased : "CAP_\(uppercased)"
|
|
|
|
let capNameMap: [String: Value] = [
|
|
"CAP_CHOWN": .chown,
|
|
"CAP_DAC_OVERRIDE": .dacOverride,
|
|
"CAP_DAC_READ_SEARCH": .dacReadSearch,
|
|
"CAP_FOWNER": .fowner,
|
|
"CAP_FSETID": .fsetid,
|
|
"CAP_KILL": .kill,
|
|
"CAP_SETGID": .setgid,
|
|
"CAP_SETUID": .setuid,
|
|
"CAP_SETPCAP": .setpcap,
|
|
"CAP_LINUX_IMMUTABLE": .linuxImmutable,
|
|
"CAP_NET_BIND_SERVICE": .netBindService,
|
|
"CAP_NET_BROADCAST": .netBroadcast,
|
|
"CAP_NET_ADMIN": .netAdmin,
|
|
"CAP_NET_RAW": .netRaw,
|
|
"CAP_IPC_LOCK": .ipcLock,
|
|
"CAP_IPC_OWNER": .ipcOwner,
|
|
"CAP_SYS_MODULE": .sysModule,
|
|
"CAP_SYS_RAWIO": .sysRawio,
|
|
"CAP_SYS_CHROOT": .sysChroot,
|
|
"CAP_SYS_PTRACE": .sysPtrace,
|
|
"CAP_SYS_PACCT": .sysPacct,
|
|
"CAP_SYS_ADMIN": .sysAdmin,
|
|
"CAP_SYS_BOOT": .sysBoot,
|
|
"CAP_SYS_NICE": .sysNice,
|
|
"CAP_SYS_RESOURCE": .sysResource,
|
|
"CAP_SYS_TIME": .sysTime,
|
|
"CAP_SYS_TTY_CONFIG": .sysTtyConfig,
|
|
"CAP_MKNOD": .mknod,
|
|
"CAP_LEASE": .lease,
|
|
"CAP_AUDIT_WRITE": .auditWrite,
|
|
"CAP_AUDIT_CONTROL": .auditControl,
|
|
"CAP_SETFCAP": .setfcap,
|
|
"CAP_MAC_OVERRIDE": .macOverride,
|
|
"CAP_MAC_ADMIN": .macAdmin,
|
|
"CAP_SYSLOG": .syslog,
|
|
"CAP_WAKE_ALARM": .wakeAlarm,
|
|
"CAP_BLOCK_SUSPEND": .blockSuspend,
|
|
"CAP_AUDIT_READ": .auditRead,
|
|
"CAP_PERFMON": .perfmon,
|
|
"CAP_BPF": .bpf,
|
|
"CAP_CHECKPOINT_RESTORE": .checkpointRestore,
|
|
]
|
|
|
|
guard let match = capNameMap[normalized] else {
|
|
throw CapabilityParsingError.invalidCapabilityName(rawValue)
|
|
}
|
|
self.value = match
|
|
}
|
|
|
|
public var capValue: UInt32 {
|
|
switch self.value {
|
|
case .chown: return 0
|
|
case .dacOverride: return 1
|
|
case .dacReadSearch: return 2
|
|
case .fowner: return 3
|
|
case .fsetid: return 4
|
|
case .kill: return 5
|
|
case .setgid: return 6
|
|
case .setuid: return 7
|
|
case .setpcap: return 8
|
|
case .linuxImmutable: return 9
|
|
case .netBindService: return 10
|
|
case .netBroadcast: return 11
|
|
case .netAdmin: return 12
|
|
case .netRaw: return 13
|
|
case .ipcLock: return 14
|
|
case .ipcOwner: return 15
|
|
case .sysModule: return 16
|
|
case .sysRawio: return 17
|
|
case .sysChroot: return 18
|
|
case .sysPtrace: return 19
|
|
case .sysPacct: return 20
|
|
case .sysAdmin: return 21
|
|
case .sysBoot: return 22
|
|
case .sysNice: return 23
|
|
case .sysResource: return 24
|
|
case .sysTime: return 25
|
|
case .sysTtyConfig: return 26
|
|
case .mknod: return 27
|
|
case .lease: return 28
|
|
case .auditWrite: return 29
|
|
case .auditControl: return 30
|
|
case .setfcap: return 31
|
|
case .macOverride: return 32
|
|
case .macAdmin: return 33
|
|
case .syslog: return 34
|
|
case .wakeAlarm: return 35
|
|
case .blockSuspend: return 36
|
|
case .auditRead: return 37
|
|
case .perfmon: return 38
|
|
case .bpf: return 39
|
|
case .checkpointRestore: return 40
|
|
}
|
|
}
|
|
|
|
public static var chown: Self { Self(.chown) }
|
|
public static var dacOverride: Self { Self(.dacOverride) }
|
|
public static var dacReadSearch: Self { Self(.dacReadSearch) }
|
|
public static var fowner: Self { Self(.fowner) }
|
|
public static var fsetid: Self { Self(.fsetid) }
|
|
public static var kill: Self { Self(.kill) }
|
|
public static var setgid: Self { Self(.setgid) }
|
|
public static var setuid: Self { Self(.setuid) }
|
|
public static var setpcap: Self { Self(.setpcap) }
|
|
public static var linuxImmutable: Self { Self(.linuxImmutable) }
|
|
public static var netBindService: Self { Self(.netBindService) }
|
|
public static var netBroadcast: Self { Self(.netBroadcast) }
|
|
public static var netAdmin: Self { Self(.netAdmin) }
|
|
public static var netRaw: Self { Self(.netRaw) }
|
|
public static var ipcLock: Self { Self(.ipcLock) }
|
|
public static var ipcOwner: Self { Self(.ipcOwner) }
|
|
public static var sysModule: Self { Self(.sysModule) }
|
|
public static var sysRawio: Self { Self(.sysRawio) }
|
|
public static var sysChroot: Self { Self(.sysChroot) }
|
|
public static var sysPtrace: Self { Self(.sysPtrace) }
|
|
public static var sysPacct: Self { Self(.sysPacct) }
|
|
public static var sysAdmin: Self { Self(.sysAdmin) }
|
|
public static var sysBoot: Self { Self(.sysBoot) }
|
|
public static var sysNice: Self { Self(.sysNice) }
|
|
public static var sysResource: Self { Self(.sysResource) }
|
|
public static var sysTime: Self { Self(.sysTime) }
|
|
public static var sysTtyConfig: Self { Self(.sysTtyConfig) }
|
|
public static var mknod: Self { Self(.mknod) }
|
|
public static var lease: Self { Self(.lease) }
|
|
public static var auditWrite: Self { Self(.auditWrite) }
|
|
public static var auditControl: Self { Self(.auditControl) }
|
|
public static var setfcap: Self { Self(.setfcap) }
|
|
public static var macOverride: Self { Self(.macOverride) }
|
|
public static var macAdmin: Self { Self(.macAdmin) }
|
|
public static var syslog: Self { Self(.syslog) }
|
|
public static var wakeAlarm: Self { Self(.wakeAlarm) }
|
|
public static var blockSuspend: Self { Self(.blockSuspend) }
|
|
public static var auditRead: Self { Self(.auditRead) }
|
|
public static var perfmon: Self { Self(.perfmon) }
|
|
public static var bpf: Self { Self(.bpf) }
|
|
public static var checkpointRestore: Self { Self(.checkpointRestore) }
|
|
|
|
public static var allCases: [CapabilityName] {
|
|
Value.allCases.map { CapabilityName($0) }
|
|
}
|
|
}
|
|
|
|
extension CapabilityName: CustomStringConvertible {
|
|
public var description: String {
|
|
switch self.value {
|
|
case .chown: return "CAP_CHOWN"
|
|
case .dacOverride: return "CAP_DAC_OVERRIDE"
|
|
case .dacReadSearch: return "CAP_DAC_READ_SEARCH"
|
|
case .fowner: return "CAP_FOWNER"
|
|
case .fsetid: return "CAP_FSETID"
|
|
case .kill: return "CAP_KILL"
|
|
case .setgid: return "CAP_SETGID"
|
|
case .setuid: return "CAP_SETUID"
|
|
case .setpcap: return "CAP_SETPCAP"
|
|
case .linuxImmutable: return "CAP_LINUX_IMMUTABLE"
|
|
case .netBindService: return "CAP_NET_BIND_SERVICE"
|
|
case .netBroadcast: return "CAP_NET_BROADCAST"
|
|
case .netAdmin: return "CAP_NET_ADMIN"
|
|
case .netRaw: return "CAP_NET_RAW"
|
|
case .ipcLock: return "CAP_IPC_LOCK"
|
|
case .ipcOwner: return "CAP_IPC_OWNER"
|
|
case .sysModule: return "CAP_SYS_MODULE"
|
|
case .sysRawio: return "CAP_SYS_RAWIO"
|
|
case .sysChroot: return "CAP_SYS_CHROOT"
|
|
case .sysPtrace: return "CAP_SYS_PTRACE"
|
|
case .sysPacct: return "CAP_SYS_PACCT"
|
|
case .sysAdmin: return "CAP_SYS_ADMIN"
|
|
case .sysBoot: return "CAP_SYS_BOOT"
|
|
case .sysNice: return "CAP_SYS_NICE"
|
|
case .sysResource: return "CAP_SYS_RESOURCE"
|
|
case .sysTime: return "CAP_SYS_TIME"
|
|
case .sysTtyConfig: return "CAP_SYS_TTY_CONFIG"
|
|
case .mknod: return "CAP_MKNOD"
|
|
case .lease: return "CAP_LEASE"
|
|
case .auditWrite: return "CAP_AUDIT_WRITE"
|
|
case .auditControl: return "CAP_AUDIT_CONTROL"
|
|
case .setfcap: return "CAP_SETFCAP"
|
|
case .macOverride: return "CAP_MAC_OVERRIDE"
|
|
case .macAdmin: return "CAP_MAC_ADMIN"
|
|
case .syslog: return "CAP_SYSLOG"
|
|
case .wakeAlarm: return "CAP_WAKE_ALARM"
|
|
case .blockSuspend: return "CAP_BLOCK_SUSPEND"
|
|
case .auditRead: return "CAP_AUDIT_READ"
|
|
case .perfmon: return "CAP_PERFMON"
|
|
case .bpf: return "CAP_BPF"
|
|
case .checkpointRestore: return "CAP_CHECKPOINT_RESTORE"
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Linux Implementation
|
|
|
|
#if os(Linux)
|
|
|
|
#if canImport(Musl)
|
|
import Musl
|
|
#elseif canImport(Glibc)
|
|
import Glibc
|
|
#endif
|
|
|
|
import CShim
|
|
|
|
/// Capability type flags
|
|
public struct CapType: OptionSet, Sendable {
|
|
public let rawValue: UInt32
|
|
|
|
public init(rawValue: UInt32) {
|
|
self.rawValue = rawValue
|
|
}
|
|
|
|
// Individual capability sets (for Get/Set/Unset/etc)
|
|
public static let effective = CapType(rawValue: 1 << 0)
|
|
public static let permitted = CapType(rawValue: 1 << 1)
|
|
public static let inheritable = CapType(rawValue: 1 << 2)
|
|
public static let bounding = CapType(rawValue: 1 << 3)
|
|
public static let ambient = CapType(rawValue: 1 << 4)
|
|
|
|
// Bulk operation flags (for Apply/Fill/Clear)
|
|
public static let caps = CapType(rawValue: 1 << 8) // CAPS - effective, permitted, inheritable
|
|
public static let bounds = CapType(rawValue: 1 << 9) // BOUNDS - bounding set
|
|
public static let ambs = CapType(rawValue: 1 << 10) // AMBS - ambient capabilities
|
|
}
|
|
|
|
private struct CapabilityHeader {
|
|
var version: UInt32
|
|
var pid: Int32
|
|
|
|
init(pid: Int32 = 0) {
|
|
self.version = 0x2008_0522
|
|
self.pid = pid
|
|
}
|
|
}
|
|
|
|
private struct CapabilityData {
|
|
var effective1: UInt32
|
|
var permitted1: UInt32
|
|
var inheritable1: UInt32
|
|
var effective2: UInt32
|
|
var permitted2: UInt32
|
|
var inheritable2: UInt32
|
|
|
|
init(
|
|
effective1: UInt32 = 0,
|
|
permitted1: UInt32 = 0,
|
|
inheritable1: UInt32 = 0,
|
|
effective2: UInt32 = 0,
|
|
permitted2: UInt32 = 0,
|
|
inheritable2: UInt32 = 0
|
|
) {
|
|
self.effective1 = effective1
|
|
self.permitted1 = permitted1
|
|
self.inheritable1 = inheritable1
|
|
self.effective2 = effective2
|
|
self.permitted2 = permitted2
|
|
self.inheritable2 = inheritable2
|
|
}
|
|
}
|
|
|
|
/// Interface with Linux capabilities
|
|
/// https://linux.die.net/man/7/capabilities
|
|
public struct LinuxCapabilities: Sendable {
|
|
private var effectiveSet: UInt64 = 0
|
|
private var permittedSet: UInt64 = 0
|
|
private var inheritableSet: UInt64 = 0
|
|
private var boundingSet: UInt64 = 0
|
|
private var ambientSet: UInt64 = 0
|
|
|
|
public init() {}
|
|
|
|
/// Get the highest supported capability from the kernel
|
|
public static func getLastSupported() throws -> CapabilityName {
|
|
guard let data = try? String(contentsOfFile: "/proc/sys/kernel/cap_last_cap", encoding: .ascii),
|
|
let lastCap = UInt32(data.trimmingCharacters(in: .whitespacesAndNewlines))
|
|
else {
|
|
throw LinuxCapabilities.Error.invalidCapabilitySet("failed to read /proc/sys/kernel/cap_last_cap")
|
|
}
|
|
|
|
guard let capability = CapabilityName.allCases.first(where: { $0.capValue == lastCap }) else {
|
|
throw LinuxCapabilities.Error.invalidCapabilitySet("no capability found for kernel max cap \(lastCap)")
|
|
}
|
|
|
|
return capability
|
|
}
|
|
|
|
/// Set keep caps
|
|
public static func setKeepCaps() throws {
|
|
let result = CZ_prctl_set_keepcaps()
|
|
if result != 0 {
|
|
throw LinuxCapabilities.Error.prctlFailed(errno: errno, operation: "PR_SET_KEEPCAPS")
|
|
}
|
|
}
|
|
|
|
/// Clear keep caps
|
|
public static func clearKeepCaps() throws {
|
|
let result = CZ_prctl_clear_keepcaps()
|
|
if result != 0 {
|
|
throw LinuxCapabilities.Error.prctlFailed(errno: errno, operation: "PR_CLEAR_KEEPCAPS")
|
|
}
|
|
}
|
|
|
|
/// Load current process capabilities from kernel
|
|
public mutating func load() throws {
|
|
let data = try getCurrentCapabilities()
|
|
self.effectiveSet = UInt64(data.effective1)
|
|
self.permittedSet = UInt64(data.permitted1)
|
|
self.inheritableSet = UInt64(data.inheritable1)
|
|
}
|
|
|
|
/// Check if capability is present in the given set
|
|
public func get(which: CapType, what: CapabilityName) -> Bool {
|
|
let bit = UInt64(1) << what.capValue
|
|
|
|
if which.contains(.effective) {
|
|
return (effectiveSet & bit) != 0
|
|
} else if which.contains(.permitted) {
|
|
return (permittedSet & bit) != 0
|
|
} else if which.contains(.inheritable) {
|
|
return (inheritableSet & bit) != 0
|
|
} else if which.contains(.bounding) {
|
|
return (boundingSet & bit) != 0
|
|
} else if which.contains(.ambient) {
|
|
return (ambientSet & bit) != 0
|
|
}
|
|
return false
|
|
}
|
|
|
|
/// Set capabilities in the given sets
|
|
public mutating func set(which: CapType, caps: [CapabilityName]) {
|
|
let mask = caps.reduce(UInt64(0)) { result, cap in
|
|
result | (UInt64(1) << cap.capValue)
|
|
}
|
|
|
|
if which.contains(.effective) {
|
|
effectiveSet |= mask
|
|
}
|
|
if which.contains(.permitted) {
|
|
permittedSet |= mask
|
|
}
|
|
if which.contains(.inheritable) {
|
|
inheritableSet |= mask
|
|
}
|
|
if which.contains(.bounding) {
|
|
boundingSet |= mask
|
|
}
|
|
if which.contains(.ambient) {
|
|
ambientSet |= mask
|
|
}
|
|
}
|
|
|
|
/// Unset capabilities from the given sets
|
|
public mutating func unset(which: CapType, caps: [CapabilityName]) {
|
|
let mask = caps.reduce(UInt64(0)) { result, cap in
|
|
result | (UInt64(1) << cap.capValue)
|
|
}
|
|
|
|
if which.contains(.effective) {
|
|
effectiveSet &= ~mask
|
|
}
|
|
if which.contains(.permitted) {
|
|
permittedSet &= ~mask
|
|
}
|
|
if which.contains(.inheritable) {
|
|
inheritableSet &= ~mask
|
|
}
|
|
if which.contains(.bounding) {
|
|
boundingSet &= ~mask
|
|
}
|
|
if which.contains(.ambient) {
|
|
ambientSet &= ~mask
|
|
}
|
|
}
|
|
|
|
/// Fill all bits of given capability types
|
|
public mutating func fill(kind: CapType) {
|
|
if kind.contains(.caps) {
|
|
effectiveSet = 0xFFFF_FFFF_FFFF_FFFF
|
|
permittedSet = 0xFFFF_FFFF_FFFF_FFFF
|
|
inheritableSet = 0
|
|
}
|
|
if kind.contains(.bounds) {
|
|
boundingSet = 0xFFFF_FFFF_FFFF_FFFF
|
|
}
|
|
if kind.contains(.ambs) {
|
|
ambientSet = 0xFFFF_FFFF_FFFF_FFFF
|
|
}
|
|
}
|
|
|
|
/// Clear all bits of given capability types
|
|
public mutating func clear(kind: CapType) {
|
|
if kind.contains(.caps) {
|
|
effectiveSet = 0
|
|
permittedSet = 0
|
|
inheritableSet = 0
|
|
}
|
|
if kind.contains(.bounds) {
|
|
boundingSet = 0
|
|
}
|
|
if kind.contains(.ambs) {
|
|
ambientSet = 0
|
|
}
|
|
}
|
|
|
|
/// Apply capabilities to current process
|
|
public func apply(kind: CapType) throws {
|
|
// Apply bounding set (requires CAP_SETPCAP)
|
|
if kind.contains(.bounds) {
|
|
try applyBoundingSet()
|
|
}
|
|
|
|
// Apply main capabilities (effective, permitted, inheritable)
|
|
if kind.contains(.caps) {
|
|
try applyMainCapabilities()
|
|
}
|
|
|
|
// Apply ambient capabilities
|
|
if kind.contains(.ambs) {
|
|
try applyAmbientCapabilities()
|
|
}
|
|
}
|
|
|
|
private func applyBoundingSet() throws {
|
|
let currentData = try getCurrentCapabilities()
|
|
let hasSetPCap = (currentData.effective1 & (1 << CapabilityName.setpcap.capValue)) != 0
|
|
|
|
if hasSetPCap {
|
|
// Get the last supported capability to avoid trying to drop unsupported ones
|
|
let lastSupported = try Self.getLastSupported()
|
|
|
|
for cap in CapabilityName.allCases {
|
|
// Skip capabilities higher than what the kernel supports
|
|
guard cap.capValue <= lastSupported.capValue else { continue }
|
|
|
|
let capBit = UInt64(1) << cap.capValue
|
|
if (boundingSet & capBit) == 0 {
|
|
let result = CZ_prctl_capbset_drop(cap.capValue)
|
|
if result != 0 && errno != EINVAL {
|
|
throw Error.prctlFailed(errno: errno, operation: "PR_CAPBSET_DROP")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func applyMainCapabilities() throws {
|
|
let data = CapabilityData(
|
|
effective1: UInt32(effectiveSet & 0xFFFF_FFFF),
|
|
permitted1: UInt32(permittedSet & 0xFFFF_FFFF),
|
|
inheritable1: UInt32(inheritableSet & 0xFFFF_FFFF)
|
|
)
|
|
|
|
try setCapabilities(data: data)
|
|
}
|
|
|
|
private func applyAmbientCapabilities() throws {
|
|
// Clear all ambient capabilities first
|
|
let clearResult = CZ_prctl_cap_ambient_clear_all()
|
|
if clearResult != 0 && errno != EINVAL {
|
|
throw Error.prctlFailed(errno: errno, operation: "PR_CAP_AMBIENT_CLEAR_ALL")
|
|
}
|
|
|
|
// Get the last supported capability to avoid trying to set unsupported ones
|
|
let lastSupported = try Self.getLastSupported()
|
|
|
|
// Set each ambient capability
|
|
for cap in CapabilityName.allCases {
|
|
// Skip capabilities higher than what the kernel supports
|
|
guard cap.capValue <= lastSupported.capValue else { continue }
|
|
|
|
let capBit = UInt64(1) << cap.capValue
|
|
if (ambientSet & capBit) != 0 {
|
|
let result = CZ_prctl_cap_ambient_raise(cap.capValue)
|
|
if result != 0 && errno != EINVAL {
|
|
throw Error.prctlFailed(errno: errno, operation: "PR_CAP_AMBIENT_RAISE")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func getCurrentCapabilities() throws -> CapabilityData {
|
|
var header = CapabilityHeader()
|
|
var data = CapabilityData()
|
|
|
|
let result = withUnsafeMutablePointer(to: &header) { headerPtr in
|
|
withUnsafeMutablePointer(to: &data) { dataPtr in
|
|
CZ_capget(headerPtr, dataPtr)
|
|
}
|
|
}
|
|
|
|
if result != 0 {
|
|
throw Error.capgetFailed(errno: errno)
|
|
}
|
|
|
|
return data
|
|
}
|
|
|
|
private func setCapabilities(data: CapabilityData) throws {
|
|
var header = CapabilityHeader()
|
|
var mutableData = data
|
|
|
|
let result = withUnsafeMutablePointer(to: &header) { headerPtr in
|
|
withUnsafeMutablePointer(to: &mutableData) { dataPtr in
|
|
CZ_capset(headerPtr, dataPtr)
|
|
}
|
|
}
|
|
|
|
if result != 0 {
|
|
throw Error.capsetFailed(errno: errno)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension LinuxCapabilities {
|
|
public enum Error: Swift.Error, CustomStringConvertible {
|
|
case unsupportedCapability(name: String)
|
|
case capsetFailed(errno: Int32)
|
|
case capgetFailed(errno: Int32)
|
|
case prctlFailed(errno: Int32, operation: String)
|
|
case invalidCapabilitySet(String)
|
|
|
|
public var description: String {
|
|
switch self {
|
|
case .unsupportedCapability(let name):
|
|
return "unsupported capability: \(name)"
|
|
case .capsetFailed(let errno):
|
|
return "capset failed with errno \(errno): \(String(cString: strerror(errno)))"
|
|
case .capgetFailed(let errno):
|
|
return "capget failed with errno \(errno): \(String(cString: strerror(errno)))"
|
|
case .prctlFailed(let errno, let operation):
|
|
return "prctl(\(operation)) failed with errno \(errno): \(String(cString: strerror(errno)))"
|
|
case .invalidCapabilitySet(let message):
|
|
return "invalid capability set configuration: \(message)"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|