Freebsd的capability能力模型与linux的设计有很大不同, linux将capability与privile权限混合到一起实现,而freebsd将其分离出来,使系统的访问控制模型更加精细。
Freebsd12定义了50多种能力类型,主要集中在文件系统和网络,这两个子系统的能力区分很精细:
sys/capsicum.h
#define CAP_READ CAPRIGHT(0, 0x0000000000000001ULL)
#define CAP_WRITE CAPRIGHT(0, 0x0000000000000002ULL)
#define CAP_SEEK_TELL CAPRIGHT(0, 0x0000000000000004ULL)
#define CAP_SEEK (CAP_SEEK_TELL | 0x0000000000000008ULL)
#define CAP_PREAD (CAP_SEEK | CAP_READ)
#define CAP_PWRITE (CAP_SEEK | CAP_WRITE)
#define CAP_MMAP CAPRIGHT(0, 0x0000000000000010ULL)
#define CAP_MMAP_R (CAP_MMAP | CAP_SEEK | CAP_READ)
#define CAP_MMAP_W (CAP_MMAP | CAP_SEEK | CAP_WRITE)
#define CAP_MMAP_X (CAP_MMAP | CAP_SEEK | 0x0000000000000020ULL)
#define CAP_MMAP_RW (CAP_MMAP_R | CAP_MMAP_W)
#define CAP_MMAP_RX (CAP_MMAP_R | CAP_MMAP_X)
#define CAP_MMAP_WX (CAP_MMAP_W | CAP_MMAP_X)
#define CAP_MMAP_RWX (CAP_MMAP_R | CAP_MMAP_W | CAP_MMAP_X)
#define CAP_CREATE CAPRIGHT(0, 0x0000000000000040ULL)
#define CAP_FEXECVE CAPRIGHT(0, 0x0000000000000080ULL)
#define CAP_FSYNC CAPRIGHT(0, 0x0000000000000100ULL)
#define CAP_FTRUNCATE CAPRIGHT(0, 0x0000000000000200ULL)
#define CAP_LOOKUP CAPRIGHT(0, 0x0000000000000400ULL)
#define CAP_FCHDIR CAPRIGHT(0, 0x0000000000000800ULL)
#define CAP_FCHFLAGS CAPRIGHT(0, 0x0000000000001000ULL)
#define CAP_CHFLAGSAT (CAP_FCHFLAGS | CAP_LOOKUP)
#define CAP_FCHMOD CAPRIGHT(0, 0x0000000000002000ULL)
#define CAP_FCHMODAT (CAP_FCHMOD | CAP_LOOKUP)
#define CAP_FCHOWN CAPRIGHT(0, 0x0000000000004000ULL)
#define CAP_FCHOWNAT (CAP_FCHOWN | CAP_LOOKUP)
#define CAP_FCNTL CAPRIGHT(0, 0x0000000000008000ULL)
#define CAP_FLOCK CAPRIGHT(0, 0x0000000000010000ULL)
#define CAP_FPATHCONF CAPRIGHT(0, 0x0000000000020000ULL)
#define CAP_FSCK CAPRIGHT(0, 0x0000000000040000ULL)
#define CAP_FSTAT CAPRIGHT(0, 0x0000000000080000ULL)
#define CAP_FSTATAT (CAP_FSTAT | CAP_LOOKUP)
#define CAP_FSTATFS CAPRIGHT(0, 0x0000000000100000ULL)
#define CAP_FUTIMES CAPRIGHT(0, 0x0000000000200000ULL)
#define CAP_FUTIMESAT (CAP_FUTIMES | CAP_LOOKUP)
#define CAP_LINKAT_TARGET (CAP_LOOKUP | 0x0000000000400000ULL)
#define CAP_MKDIRAT (CAP_LOOKUP | 0x0000000000800000ULL)
#define CAP_MKFIFOAT (CAP_LOOKUP | 0x0000000001000000ULL)
#define CAP_MKNODAT (CAP_LOOKUP | 0x0000000002000000ULL)
#define CAP_RENAMEAT_SOURCE (CAP_LOOKUP | 0x0000000004000000ULL)
#define CAP_SYMLINKAT (CAP_LOOKUP | 0x0000000008000000ULL)
#define CAP_UNLINKAT (CAP_LOOKUP | 0x0000000010000000ULL)
#define CAP_ACCEPT CAPRIGHT(0, 0x0000000020000000ULL)
#define CAP_BIND CAPRIGHT(0, 0x0000000040000000ULL)
#define CAP_CONNECT CAPRIGHT(0, 0x0000000080000000ULL)
#define CAP_GETPEERNAME CAPRIGHT(0, 0x0000000100000000ULL)
#define CAP_GETSOCKNAME CAPRIGHT(0, 0x0000000200000000ULL)
#define CAP_GETSOCKOPT CAPRIGHT(0, 0x0000000400000000ULL)
#define CAP_LISTEN CAPRIGHT(0, 0x0000000800000000ULL)
#define CAP_PEELOFF CAPRIGHT(0, 0x0000001000000000ULL)
#define CAP_RECV CAP_READ
#define CAP_SEND CAP_WRITE
#define CAP_SETSOCKOPT CAPRIGHT(0, 0x0000002000000000ULL)
#define CAP_SHUTDOWN CAPRIGHT(0, 0x0000004000000000ULL)
#define CAP_BINDAT (CAP_LOOKUP | 0x0000008000000000ULL)
#define CAP_CONNECTAT (CAP_LOOKUP | 0x0000010000000000ULL)
#define CAP_LINKAT_SOURCE (CAP_LOOKUP | 0x0000020000000000ULL)
#define CAP_RENAMEAT_TARGET (CAP_LOOKUP | 0x0000040000000000ULL)
#define CAP_MAC_GET CAPRIGHT(1, 0x0000000000000001ULL)
#define CAP_MAC_SET CAPRIGHT(1, 0x0000000000000002ULL)
#define CAP_SEM_GETVALUE CAPRIGHT(1, 0x0000000000000004ULL)
#define CAP_SEM_POST CAPRIGHT(1, 0x0000000000000008ULL)
#define CAP_SEM_WAIT CAPRIGHT(1, 0x0000000000000010ULL)
#define CAP_EVENT CAPRIGHT(1, 0x0000000000000020ULL)
#define CAP_KQUEUE_EVENT CAPRIGHT(1, 0x0000000000000040ULL)
#define CAP_IOCTL CAPRIGHT(1, 0x0000000000000080ULL)
#define CAP_TTYHOOK CAPRIGHT(1, 0x0000000000000100ULL)
#define CAP_PDGETPID CAPRIGHT(1, 0x0000000000000200ULL)
#define CAP_PDWAIT CAPRIGHT(1, 0x0000000000000400ULL)
#define CAP_PDKILL CAPRIGHT(1, 0x0000000000000800ULL)
#define CAP_EXTATTR_DELETE CAPRIGHT(1, 0x0000000000001000ULL)
#define CAP_EXTATTR_GET CAPRIGHT(1, 0x0000000000002000ULL)
#define CAP_EXTATTR_LIST CAPRIGHT(1, 0x0000000000004000ULL)
#define CAP_EXTATTR_SET CAPRIGHT(1, 0x0000000000008000ULL)
#define CAP_ACL_CHECK CAPRIGHT(1, 0x0000000000010000ULL)
#define CAP_ACL_DELETE CAPRIGHT(1, 0x0000000000020000ULL)
#define CAP_ACL_GET CAPRIGHT(1, 0x0000000000040000ULL)
#define CAP_ACL_SET CAPRIGHT(1, 0x0000000000080000ULL)
#define CAP_KQUEUE_CHANGE CAPRIGHT(1, 0x0000000000100000ULL)
#define CAP_KQUEUE (CAP_KQUEUE_EVENT | CAP_KQUEUE_CHANGE)
比如对网络的能力控制,可以看到freebsd对bind,listen,accept等网络操作都做了限制, 而linux却只有CAP_NET_BIND_SERVICE、CAP_NET_BROADCAST、CAP_NET_ADMIN、CAP_NET_RAW四种能力,限制的主要是特权能力。Freebsd虽然没有在capability模型中对这些特权进行限制,但是它是在privilege权限模型中进行限制的,freebsd定义了500多个权限类型,对操作系统的每个子系统都做了更加精细的权限控制。
sys/priv.h:
#define PRIV_NET_BRIDGE 390 /* Administer bridge. */
#define PRIV_NET_GRE 391 /* Administer GRE. */
#define _PRIV_NET_PPP 392 /* Removed. */
#define _PRIV_NET_SLIP 393 /* Removed. */
#define PRIV_NET_BPF 394 /* Monitor BPF. */
#define PRIV_NET_RAW 395 /* Open raw socket. */
#define PRIV_NET_ROUTE 396 /* Administer routing. */
#define PRIV_NET_TAP 397 /* Can open tap device. */
#define PRIV_NET_SETIFMTU 398 /* Set interface MTU. */
#define PRIV_NET_SETIFFLAGS 399 /* Set interface flags. */
#define PRIV_NET_SETIFCAP 400 /* Set interface capabilities. */
#define PRIV_NET_SETIFNAME 401 /* Set interface name. */
#define PRIV_NET_SETIFMETRIC 402 /* Set interface metrics. */
#define PRIV_NET_SETIFPHYS 403 /* Set interface physical layer prop. */
#define PRIV_NET_SETIFMAC 404 /* Set interface MAC label. */
#define PRIV_NET_ADDMULTI 405 /* Add multicast addr. to ifnet. */
#define PRIV_NET_DELMULTI 406 /* Delete multicast addr. from ifnet. */
#define PRIV_NET_HWIOCTL 407 /* Issue hardware ioctl on ifnet. */
#define PRIV_NET_SETLLADDR 408 /* Set interface link-level address. */
#define PRIV_NET_ADDIFGROUP 409 /* Add new interface group. */
#define PRIV_NET_DELIFGROUP 410 /* Delete interface group. */
#define PRIV_NET_IFCREATE 411 /* Create cloned interface. */
#define PRIV_NET_IFDESTROY 412 /* Destroy cloned interface. */
#define PRIV_NET_ADDIFADDR 413 /* Add protocol addr to interface. */
#define PRIV_NET_DELIFADDR 414 /* Delete protocol addr on interface. */
#define PRIV_NET_LAGG 415 /* Administer lagg interface. */
#define PRIV_NET_GIF 416 /* Administer gif interface. */
#define PRIV_NET_SETIFVNET 417 /* Move interface to vnet. */
#define PRIV_NET_SETIFDESCR 418 /* Set interface description. */
#define PRIV_NET_SETIFFIB 419 /* Set interface fib. */
#define PRIV_NET_VXLAN 420 /* Administer vxlan. */
#define PRIV_NET_SETLANPCP 421 /* Set LAN priority. */
#define PRIV_NET_SETVLANPCP PRIV_NET_SETLANPCP /* Alias Set VLAN priority */
#define PRIV_NET80211_GETKEY 440 /* Query 802.11 keys. */
#define PRIV_NET80211_MANAGE 441 /* Administer 802.11. */
#define _PRIV_NETATALK_RESERVEDPORT 450 /* Bind low port number. */
#define PRIV_NETATM_CFG 460
#define PRIV_NETATM_ADD 461
#define PRIV_NETATM_DEL 462
#define PRIV_NETATM_SET 463
#define PRIV_NETBLUETOOTH_RAW 470 /* Open raw bluetooth socket. */
#define PRIV_NETGRAPH_CONTROL 480 /* Open netgraph control socket. */
#define PRIV_NETGRAPH_TTY 481 /* Configure tty for netgraph. */
#define PRIV_NETINET_RESERVEDPORT 490 /* Bind low port number. */
#define PRIV_NETINET_IPFW 491 /* Administer IPFW firewall. */
#define PRIV_NETINET_DIVERT 492 /* Open IP divert socket. */
#define PRIV_NETINET_PF 493 /* Administer pf firewall. */
#define PRIV_NETINET_DUMMYNET 494 /* Administer DUMMYNET. */
#define PRIV_NETINET_CARP 495 /* Administer CARP. */
#define PRIV_NETINET_MROUTE 496 /* Administer multicast routing. */
#define PRIV_NETINET_RAW 497 /* Open netinet raw socket. */
#define PRIV_NETINET_GETCRED 498 /* Query netinet pcb credentials. */#define PRIV_NETINET_ADDRCTRL6 499 /* Administer IPv6 address scopes. */
#define PRIV_NETINET_ND6 500 /* Administer IPv6 neighbor disc. */
#define PRIV_NETINET_SCOPE6 501 /* Administer IPv6 address scopes. */
#define PRIV_NETINET_ALIFETIME6 502 /* Administer IPv6 address lifetimes. */
#define PRIV_NETINET_IPSEC 503 /* Administer IPSEC. */
#define PRIV_NETINET_REUSEPORT 504 /* Allow [rapid] port/address reuse. */
#define PRIV_NETINET_SETHDROPTS 505 /* Set certain IPv4/6 header options. */
#define PRIV_NETINET_BINDANY 506 /* Allow bind to any address. */
#define PRIV_NETINET_HASHKEY 507 /* Get and set hash keys for IPv4/6. */
#define _PRIV_NETIPX_RESERVEDPORT 520 /* Bind low port number. */
#define _PRIV_NETIPX_RAW 521 /* Open netipx raw socket.
#define _PRIV_NETIPX_RESERVEDPORT 520 /* Bind low port number. */
#define _PRIV_NETIPX_RAW 521 /* Open netipx raw socket. */
#define PRIV_NETNCP 530 /* Use another user's connection. */
#define PRIV_NETSMB 540 /* Use another user's connection. */
在对capability的使用方式上,freebsd定义了一个系统调用sys_cap_enter需要进程主动进入能力模式,而linux从最初的init进程开始就始终处在能力模式之下。
kern/sys_capability.c:
int
sys_cap_enter(struct thread *td, struct cap_enter_args *uap)
{
struct ucred *newcred, *oldcred;
struct proc *p;
if (IN_CAPABILITY_MODE(td))
return (0);
newcred = crget();
p = td->td_proc;
PROC_LOCK(p);
oldcred = crcopysafe(p, newcred);
newcred->cr_flags |= CRED_FLAG_CAPMODE;
proc_set_cred(p, newcred);
PROC_UNLOCK(p);
crfree(oldcred);
return (0);
}
sys_cap_enter只是在进程的cred结构体里做了CRED_FLAG_CAPMODE标记。当进程处在capability模式下,进程所能使用的系统调用也是有限制的:
kern/subr_syscall.c
static inline int
syscallenter(struct thread *td)
{
#ifdef CAPABILITY_MODE
if (IN_CAPABILITY_MODE(td) &&
!(sa->callp->sy_flags & SYF_CAPENABLED)) {
error = ECAPMODE;
goto retval;
}
#endif
}
只能使用标记为SYF_CAPENABLED的系统调用。
在进程进入能力模式后,它不能执行磁盘二进制文件, 这也是与linux的另一个不同点。
kern/kern_exec.c:
static int
do_execve(struct thread *td, struct image_args *args, struct mac *mac_p)
{
if (args->fname != NULL) {
#ifdef CAPABILITY_MODE
if (IN_CAPABILITY_MODE(td)) {
error = ECAPMODE;
goto exec_fail;
}
#endif
}
在看下linux的capability能力,它与privilege是混合定义的,一共有三十几个:
#define CAP_CHOWN 0
#define CAP_DAC_OVERRIDE 1
#define CAP_DAC_READ_SEARCH 2
#define CAP_FOWNER 3
#define CAP_FSETID 4
#define CAP_KILL 5
#define CAP_SETGID 6
#define CAP_SETUID 7
#define CAP_SETPCAP 8
#define CAP_LINUX_IMMUTABLE 9
#define CAP_NET_BIND_SERVICE 10
#define CAP_NET_BROADCAST 11
#define CAP_NET_ADMIN 12
#define CAP_NET_RAW 13
#define CAP_IPC_LOCK 14
#define CAP_IPC_OWNER 15
#define CAP_SYS_MODULE 16
#define CAP_SYS_RAWIO 17
#define CAP_SYS_CHROOT 18
#define CAP_SYS_PTRACE 19
#define CAP_SYS_PACCT 20
#define CAP_SYS_ADMIN 21
#define CAP_SYS_BOOT 22
#define CAP_SYS_NICE 23
#define CAP_SYS_RESOURCE 24
#define CAP_SYS_TIME 25
#define CAP_SYS_TTY_CONFIG 26
#define CAP_MKNOD 27
#define CAP_LEASE 28
#define CAP_AUDIT_WRITE 29
#define CAP_AUDIT_CONTROL 30
#define CAP_SETFCAP 31
#define CAP_MAC_OVERRIDE 32
#define CAP_MAC_ADMIN 33
#define CAP_SYSLOG 34
#define CAP_WAKE_ALARM 35
#define CAP_BLOCK_SUSPEND 36
#define CAP_AUDIT_READ 37
最后在看下windows,NT内核是没有capability模型的,但是它有privilege模型。
Winnt.h:
#define SE_CREATE_TOKEN_NAME
#define SE_ASSIGNPRIMARYTOKEN_NAME
#define SE_LOCK_MEMORY_NAME
#define SE_INCREASE_QUOTA_NAME
#define SE_UNSOLICITED_INPUT_NAME
#define SE_MACHINE_ACCOUNT_NAME
#define SE_TCB_NAME
#define SE_SECURITY_NAME
#define SE_TAKE_OWNERSHIP_NAME
#define SE_LOAD_DRIVER_NAME
#define SE_SYSTEM_PROFILE_NAME
#define SE_SYSTEMTIME_NAME
#define SE_PROF_SINGLE_PROCESS_NAME
#define SE_INC_BASE_PRIORITY_NAME
#define SE_CREATE_PAGEFILE_NAME
#define SE_CREATE_PERMANENT_NAME
#define SE_BACKUP_NAME
#define SE_RESTORE_NAME
#define SE_SHUTDOWN_NAME
#define SE_DEBUG_NAME
#define SE_AUDIT_NAME
#define SE_SYSTEM_ENVIRONMENT_NAME
#define SE_CHANGE_NOTIFY_NAME
#define SE_REMOTE_SHUTDOWN_NAME
#define SE_UNDOCK_NAME
#define SE_SYNC_AGENT_NAME
#define SE_ENABLE_DELEGATION_NAME
#define SE_MANAGE_VOLUME_NAME
#define SE_IMPERSONATE_NAME
#define SE_CREATE_GLOBAL_NAME
#define SE_TRUSTED_CREDMAN_ACCESS_NAME
#define SE_RELABEL_NAME
#define SE_INC_WORKING_SET_NAME
#define SE_TIME_ZONE_NAME
#define SE_CREATE_SYMBOLIC_LINK_NAME
#define SE_DELEGATE_SESSION_USER_IMPERSONATE_NAME