Solaris 10 操作系统中的最小权限模型
Amy Rich,2005 年 2 月
目录:
大多数 UNIX 操作系统运行大量具有超级用户权限的系统进程,从而为程序提供了读取和修改诸如其他进程、内存和 I/O 设备的功能。虽然这为系统进程提供了执行任务所需的权限,但
也为它们提供了不必要的访问权限,使它们能够访问系统中其他受保护的部分。很多软件漏洞通过一些程序错误(如缓冲区溢出和数据损坏),依靠这种提升的权限来获取计算机的超级用户访问权限。为了解决这一问题,S
olaris 10 操作系统中添加了一个新的
最小权限模型 ,该模型仅为指定的进程提供一部分超级用户权限,而不是使其具有所有权限的完全访问权限。
最小权限模型是利用 Sun 在 Trusted Solaris 中积累的丰富经验发展而来的,并借鉴了此产品中使用的更严格的安全模型。Solaris 10
操作系统中的最小权限模型可方便地允许普通用户执行一些操作,如挂载文件系统、启动绑定到较低编号端口的守护进程以及更改文件所有权等。另一方面,它还对系统进行保护,以
禁止启动以前使用完全超级用户权限运行的程序,因为它们只需要具有某些操作的有限访问权限,例如,绑定到低于 1024 的端口、在用户主目录中读取和写入内容,或者访问以太网设备。在最小权限模型中,由
于仅在极少数情况下需要使用以完全超级用户权限运行的 setuid 根二进制文件和守护进程,因此,程序中的漏洞不再造成完全超级用户安全危害。由于编程错误(如缓冲区溢出)而
导致的损害可以限制在非超级用户的范围内,而非超级用户对于读取或写入受保护的系统文件或停机等这些重要功能不具备访问权限。
Solaris 10 操作系统中的最小权限模型包含大约 50 个精细划分的权限和基本权限集。所定义的权限被划分到
contract、
cpc、
dtrace、
file、
ipc、
net、
proc 以及
sys 组中。基本权限集包含按传统安全模型为非特权进程授予的所有权限:
proc_fork、
proc_exec、
proc_session、
proc_info 和
file_link_any。
进程权限集
每个进程在其内核凭证中包含四个权限集:
可继承权限集 (I):在
exec 上继承的权限。
允许权限集 (P):进程的最大权限集。
有效权限集 (E):当前生效的权限,它是
P 的子集。
限制权限集 (L):进程及其子进程可以获取的权限的上限。对
L 权限集所做的任何更改在下次执行
exec 时才会生效。
每个进程还具有一个权限识别状态 (PAS),可将其设置为可识别权限 (PA) 或无法识别权限 (NPA)。权限识别是一种机制,传统应用程序可通过此机制保持与传统完全权限模型的完全兼容性。如果 EUID、RUID 或 SUID 中的任何一个为
0(超级用户),则为具有
NPA 状态的传统应用程序授予
L 权限集中的所有权限。
当进程调用
exec(2) 时,内核将尝试放弃权限识别。对于非特权进程,
I、
P 和
E 通常与基本权限集相同,
L 通常是所定义的完整权限集。有关该实现基本原理更加详细的说明,请参见
blogs.sun.com 上
Casper Dik's
weblog (Casper Dik 的博客)和
Process
Rights Management Tutorial (进程权限管理教程)。
启动进程后,它将使用
权限处理函数 在权限集中添加或删除权限。始终可以删除权限,但只能将位于允许权限集中的权限添加到有效权限集和可继承权限集中。因此,可
继承权限集可能大于允许权限集。限制权限集可能永远也不会扩大。
ppriv(1) 命令
可通过
ppriv(1) 程序查看和修改进程权限集及其属性。
ppriv(1) 程序参数遵循以下语法:
/usr/bin/ppriv -l [-v] [privilege-specification...]
/usr/bin/ppriv [-v] [-S] [-D | -N] [-s spec] [pid | core]
/usr/bin/ppriv -e [-D | -N] [-s spec] command [arg...]
上述选项的定义如下:
-D:为所提供的进程或命令打开权限调试。
-e:将其余参数解释为一个命令行,并使用指定的权限属性和权限集运行该命令行。
-l:在
stdout 中列出当前定义的所有权限。
-N:为所提供的进程或命令关闭权限调试。
-s spec:根据
spec 修改进程的权限集。可以使用多个
-s 选项修改相同的权限集,但前提是要么为单个权限集恰好分配一个权限,要么添加或删除任意数量的权限。即,为
一个权限集分配权限与添加或删除权限是互斥的。
spec 是格式为 [
AEILP][
+-=]
privsetspec 的规范(不含空格),其中:
AEILP:包含一个或多个字母,表示要更改的权限集。这些字母不区分大小写,例如,
a 或
A 表示所有权限集。
+-=:表示一个修饰符,用于在
privsetspec 中指定的权限集中相应地添加 (+)、删除 (-) 或分配列出的权限 (=)。
privsetspec:表示一个以逗号分隔的权限集规范 (priv1,
priv2, ...),如
priv_str_to_set(3C) 中所述。
-S:简短。为权限集报告尽可能简短的输出字符串。缺省设置是可移植的输出。请参见
priv_str_to_set(3C)。
-v:详细。使用权限名称报告权限集。
查看进程权限
让我们使用
ppriv(1) 命令查看几个进程,以了解它们之间存在的差异(具体取决于它们是否具有 PA 状态以及是否修改了权限)。在第一个示例中,我
们查看的是一个无法识别权限的第三方用户进程
screen。它没有将
flags 设置为
PRIV_AWARE,并且以相同方式将
P、
I 和
E 均定义为基本权限集。权限集
L 是所定义的完整权限集:
ppriv -v 1746
1746: screen -R
flags = <none>
E: file_link_any,proc_exec,proc_fork,proc_info,proc_session
I: file_link_any,proc_exec,proc_fork,proc_info,proc_session
P: file_link_any,proc_exec,proc_fork,proc_info,proc_session
L: contract_event,contract_observer,cpc_cpu,dtrace_kernel,
dtrace_proc,dtrace_user,file_chown,file_chown_self,
file_dac_execute,file_dac_read,file_dac_search,
file_dac_write,file_link_any,file_owner,file_setid,
ipc_dac_read,ipc_dac_write,ipc_owner,net_icmpaccess,
net_privaddr,net_rawaccess,proc_audit,proc_chroot,
proc_clock_highres,proc_exec,proc_fork,proc_info,
proc_lock_memory,proc_owner,proc_priocntl,
proc_session,proc_setid,proc_taskid,proc_zone,
sys_acct,sys_admin,sys_audit,sys_config,sys_devices,
sys_ipc_config,sys_linkdir,sys_mount,sys_net_config,
sys_nfs,sys_res_config,sys_resource,sys_suser_compat,
sys_time
在第二个示例中,我们查看的是
inetd 进程。它无法识别权限,但已修改了其
P 和
E 权限集以便与
L 相匹配,因为它是使用 EUID/RUID/SUID 0 运行的:
pcred 193
193: e/r/suid=0 e/r/sgid=0
ppriv -v 193
193: /usr/lib/inet/inetd start
flags = <none>
E: contract_event,contract_observer,cpc_cpu,dtrace_kernel,
dtrace_proc,dtrace_user,file_chown,file_chown_self,
file_dac_execute,file_dac_read,file_dac_search,
file_dac_write,file_link_any,file_owner,file_setid,
ipc_dac_read,ipc_dac_write,ipc_owner,net_icmpaccess,
net_privaddr,net_rawaccess,proc_audit,proc_chroot,
proc_clock_highres,proc_exec,proc_fork,proc_info,
proc_lock_memory,proc_owner,proc_priocntl,proc_session,
proc_setid,proc_taskid,proc_zone,sys_acct,sys_admin,
sys_audit,sys_config,sys_devices,sys_ipc_config,
sys_linkdirsys_mount,sys_net_config,sys_nfs,
sys_res_config,sys_resource,sys_suser_compat,
sys_time
I: file_link_any,proc_exec,proc_fork,proc_info,proc_session
P: contract_event,contract_observer,cpc_cpu,dtrace_kernel,
dtrace_proc,dtrace_user,file_chown,file_chown_self,
file_dac_execute,file_dac_read,file_dac_search,
file_dac_write,file_link_any,file_owner,file_setid,
ipc_dac_read,ipc_dac_write,ipc_owner,net_icmpaccess,
net_privaddr,net_rawaccess,proc_audit,proc_chroot,
proc_clock_highres,proc_exec,proc_fork,proc_info,
proc_lock_memory,proc_owner,proc_priocntl,proc_session,
proc_setid,proc_taskid,proc_zone,sys_acct,sys_admin,
sys_audit,sys_config,sys_devices,sys_ipc_config,
sys_linkdir,sys_mount,sys_net_config,sys_nfs,
sys_res_config,sys_resource,sys_suser_compat,sys_time
L: contract_event,contract_observer,cpc_cpu,dtrace_kernel,
dtrace_proc,dtrace_user,file_chown,file_chown_self,
file_dac_execute,file_dac_read,file_dac_search,
file_dac_write,file_link_any,file_owner,file_setid,
ipc_dac_read,ipc_dac_write,ipc_owner,net_icmpaccess,
net_privaddr,net_rawaccess,proc_audit,proc_chroot,
proc_clock_highres,proc_exec,proc_fork,proc_info,
proc_lock_memory,proc_owner,proc_priocntl,proc_session,
proc_setid,proc_taskid,proc_zone,sys_acct,sys_admin,
sys_audit,sys_config,sys_devices,sys_ipc_config,sys_linkdir,
sys_mount,sys_net_config,sys_nfs,sys_res_config,
sys_resource,sys_suser_compat,sys_time
在第三个示例中,我们查看的是可识别权限的
fmd(1M) 进程。请注意
flags 设置以及
E、
I、
P 和
L 的限制:
ppriv -v 306
306: /usr/lib/fm/fmd/fmd
flags = PRIV_AWARE
E: file_dac_execute,file_dac_read,file_dac_search,
file_dac_write,file_link_any,file_owner,proc_exec,
proc_fork,proc_info,proc_owner,proc_priocntl,proc_session,
sys_admin,sys_config,sys_devices,sys_res_config
I: file_dac_execute,file_dac_read,file_dac_search,
file_dac_write,file_link_any,file_owner,proc_exec,
proc_fork,proc_info,proc_owner,proc_priocntl,proc_session,
sys_admin,sys_config,sys_devices,sys_res_config
P: file_dac_execute,file_dac_read,file_dac_search,
file_dac_write,file_link_any,file_owner,proc_exec,
proc_fork,proc_info,proc_owner,proc_priocntl,proc_session,
sys_admin,sys_config,sys_devices,sys_res_config
L: file_dac_execute,file_dac_read,file_dac_search,
file_dac_write,file_link_any,file_owner,proc_exec,
proc_fork,proc_info,proc_owner,proc_priocntl,proc_session,
sys_admin,sys_config,sys_devices,sys_res_config
在最后一个示例中,我们查看的是
lockd(1M) 进程,它将 flags 设置为
PRIV_AWARE,并且其访问权限非常有限:
ppriv -v 161
161: /usr/lib/nfs/lockd
flags = PRIV_AWARE
E: sys_nfs
I: none
P: sys_nfs
L: none
调试进程权限
通常,系统管理员希望为某些用户授予特定的权限,以使他们能够执行系统任务。如果用户没有正确的权限,系统将输出错误,而不会执行该任务。假设已从
/usr/sbin/traceroute 文件中删除了 SUID 位,以便普通用户无法使用它。任何系统管理员应该能够从其个人帐户中使用
traceroute,而无需成为超级用户。由于没有正确的权限,用户在尝试使用
traceroute 连接到
www 主机时,将会出现以下错误:
traceroute www
traceroute: icmp socket: Permission denied
要确定各种命令中缺少的权限,请在 shell 中使用
ppriv(1) 的调试功能:
ppriv -D $$
traceroute www
traceroute[2885]: missing privilege "net_icmpaccess" (euid = 1001,
syscall = 230) for "devpolicy" needed at so_socket+0xa4
traceroute: icmp socket: Permission denied
现在,我们清楚地知道具有 UID 1001 的用户缺少
PRIV_NET_ICMPACCESS
权限,因此,可以为其授予此权限,以便 UID 1001 能够成功运行
traceroute。在诊断出问题后,请确保在该 shell 中关闭调试功能:
ppriv -N $$
用户也可以每次只调试一个进程,而不是在 shell 中调试每一个命令:
ppriv -D -e dtrace -D test.d
权限和基于角色的访问控制 (RBAC)
RBAC 功能(在 Solaris 操作系统 8 和更高版本中提供)用于为角色或用户分配特定权限。Solaris RBAC 配置是通过以下四个主文件控制的:
/etc/security/exec_attr、
/etc/security/prof_attr、
/etc/security/auth_attr 和
/etc/user_attr。
exec_attr(4) 指定了与配置文件关联的执行属性。这通常包括用户和组 ID、命令以及缺省/限制权限。
prof_attr(4) 包含一组执行配置文件名称、描述以及其他属性。
auth_attr(4) 包含授权定义和描述。
user_attr(4) 包含用户和角色定义,以及为其分配的授权、配置文件和项目。为了更好地了解 RBAC 的运行方式,请阅读上面提到的手册页、
rbac(5)、
policy.conf(4) 和
chkauthattr(3SECDB) 手册页,以及
Solaris 10 System Administrator Collection (
Solaris 10 系统管理员文档集)中的
Roles, Rights Profiles, and
Privileges (角色、权限配置文件和权限)部分。
要允许用户组使用
DTrace,系统管理员应创建一个角色,它能够访问
DTrace 权限或将权限直接分配给用户。下面创建了一个 "
debug" 角色,并为其授予了相应的权限:
roleadd -u 201 -d /export/home/debug -P "Process Management" debug
rolemod -K defaultpriv=basic,dtrace_kernel,dtrace_proc,dtrace_user debug
现在,使用
usermod 将所需的用户添加到
debug 角色中:
usermod -R debug username
具有
debug 角色的用户现在可以使用
su 访问
debug(需要提供相应的口令),并运行所需的
DTrace 命令。
系统管理员也可以直接为用户分配权限,而不是添加角色并通过
su 使用户能够访问角色。要成功执行以下命令,必须注销用户:
usermod -K defaultpriv=basic,dtrace_kernel,dtrace_proc,dtrace_user username
如果还需要其他权限,请再次运行
dtrace 命令
ppriv 以找出这些权限。
也可以将 RBAC 与最小权限模型结合使用,以便更安全地运行需要绑定到特权端口的守护进程(如
httpd)。实际上,很多此类程序并不需要拥有某些操作的超级用户访问权限(侦听 1024 以下端口的操作除外),因此,如果为运行
net_privaddr 进程的角色/用户授予了超级用户访问权限,则不需要再使用 EUID 0 运行进程。
所定义的权限集
尽管
privileges(5) 手册页中已列出了按照新的最小权限模型定义的权限,但我们还是在下面列出了一些权限,因为系统管理员很可能会使用这些权限:<
/p>
PRIV_CPC_CPU
允许进程访问每个 CPU 的硬件性能计数器。
PRIV_DTRACE_PROC
允许使用
DTrace 进程级别跟踪。允许在用户有权访问的进程中设置并启用进程级别跟踪探测器。
PRIV_DTRACE_USER
允许使用
DTrace 用户级别跟踪。允许使用系统调用和配置文件
DTrace 提供程序来检查用户有权访问的进程。
PRIV_DTRACE_KERNEL
允许使用
DTrace 内核级别跟踪。
PRIV_FILE_CHOWN
允许进程更改文件的所有者用户 ID。允许进程将文件的组 ID 更改为进程的有效组 ID 或补充组 ID 以外的内容。
PRIV_FILE_CHOWN_SELF
允许进程分发其文件。运行具有此权限的进程时,就好像
{_POSIX_CHOWN_RESTRICTED} 未生效一样。
PRIV_FILE_DAC_READ
允许进程读取文件或目录;如果没有此权限,文件或目录的权限位或 ACL 将禁止进程拥有读取权限。
PRIV_FILE_DAC_SEARCH
允许进程搜索目录;如果没有此权限,目录的权限位或 ACL 将禁止进程拥有搜索权限。
PRIV_FILE_DAC_WRITE
允许进程写入文件或目录;如果没有此权限,文件或目录的权限位或 ACL 将禁止进程拥有写入权限。如果有效 UID 不为 0,则需要使用所有权限才能写入 UID 0 拥有的文件。
PRIV_FILE_OWNER
允许不是文件所有者的进程修改该文件的访问权限和修改时间。允许不是目录所有者的进程修改该目录的访问权限和修改时间。对于父目录设置了“执行后保存文本图像”(sticky) 位的文件或目录,允
许不是文件或目录所有者的进程对其进行删除或重命名。允许不是文件所有者的进程在该文件上挂载
namefs。允许不是文件或目录所有者的进程修改该文件或目录的权限位或 ACL。
PRIV_NET_ICMPACCESS
允许进程发送和接收 ICMP 数据包。
PRIV_NET_PRIVADDR
允许进程绑定到特权端口号。特权端口号为 1-1023(传统的 UNIX 特权端口)以及那些标记为 "
udp/tcp_extra_priv_ports" 的端口,但 NFS 保留使用的端口除外。
PRIV_PROC_SETID
允许进程任意设置其 UID,假定 UID 0 要求声明所有权限。
PRIV_PROC_ZONE
允许进程跟踪其他区域中的进程,或者向其发送信号。请参见
zones(5)。
PRIV_SYS_ADMIN
允许进程执行系统管理任务,如设置节点和域名,以及指定
coreadm(1M) 和
nscd(1M) 设置。
PRIV_SYS_CONFIG
允许进程执行各种系统配置任务。允许使用特定于文件系统的管理过程,如文件系统配置 ioctl、配额调用、创建和删除快照以及处理 PCFS 引导扇区。
PRIV_SYS_DEVICES
允许进程创建设备专用的文件。允许进程成功调用某个内核模块,该模块调用内核
drv_priv(9F) 函数以检查允许的访问。允许进程直接打开实际的控制台设备。允许进程打开已按独占方式打开的设备。
PRIV_SYS_MOUNT
允许进程挂载和卸载文件系统,如果没有此权限,将限制执行此操作(即,除
namefs 以外的大多数文件系统)。允许进程添加和删除交换设备。
权限集 C 函数
可以使用各种 C 函数调用来查看和修改权限。有关在您自己的代码中添加权限识别功能的详细信息,请参见下面的手册页:
getpflags(2)、
setpflags(2):获取或设置进程标志
getppriv(2)、
setppriv(2):获取或设置权限集
priv_addset(3C)
priv_allocset(3C)、
priv_copyset(3C)、
priv_delset(3C)、
priv_emptyset(3C)、
priv_fillset(3C)、
priv_freeset(3C)、
priv_intersect(3C)、
priv_inverse(3C)、
priv_isemptyset(3C)、
priv_isequalset(3C)、
priv_isfullset(3C)、
priv_ismember(3C)、
priv_issubset(3C)、
priv_union(3C):权限集处理函数
priv_set(3C)、
priv_ineffect(3C):更改权限集并检查是否设置了权限
priv_str_to_set(3C)、
priv_set_to_str(3C)、
priv_getbyname(3C)、
priv_getbynum(3C)、
priv_getsetbyname(3C)、
priv_getsetbynum(3C)、
priv_gettext(3C):权限名称函数
priv_getbyname(9F):将权限名称映射到编号
priv_policy(9F)、
priv_policy_only(9F)、
priv_policy_choice(9F):检查、报告和审计权限
资源