システムコールの機能を制限するシステムコールtame(2) - OpenBSD 5.8
現段階で最新のOpenBSD安定版のリリースは「OpenBSD 5.8」ですが、このバージョンには「tame(2)」という新しいシステムコールが導入されています。このシステムコールは、このシステムコールが実行されたあとに実行されるシステムコールの機能を制限するというものです。
tame(2)システムコールを呼んだ後、そのプロセスは「制限された操作モデル」または「操作機能が減らされた操作モデル」に入り、通常のシステムコールが提供している機能のうちいくつかが使えなくなります。利用できる機能を制限することで、プロセスをより危険性のない状態で実行しよう、というのがアイディアの根幹です。
図 tame(2)システムコール in OpenBSD 5.8
# uname -sr
OpenBSD 5.8
# man tame | head -20
TAME(2) System Calls Manual TAME(2)
NAME
tame - restrict system operations
SYNOPSIS
#include <sys/tame.h>
int
tame(int flags);
DESCRIPTION
The current process is forced into a restricted-service operating mode.
A few subsets are available, roughly described as computation, memory
management, read-write operations on file descriptors, opening of files,
networking. In general, these modes were selected by studying the
operation of many programs using libc and other such interfaces.
Use of tame() in an application will require at least some study and
understanding of the interfaces called.
#
これはFreeBSDのセキュリティ機能Capsicumによく似ています。よく似ているというか、実施的に提供される機能としてはそのままCapsicumだと考えてもよさそうです。ではなぜCapsicumのインターフェースを踏襲して開発していないかといえば、tame(2)を開発したOpenBSDの開発者Theo de Raadt氏が2015年7月にメーリングリストに投函したこのメール「tame(2) WIP 」がそのままその理由ということになるでしょう。
I have been working for a while on a subsystem to restrict programs into a "reduced feature operating model". Other people have made such systems in the past, but I have never been happy with them. I don't think I am alone.
Generally there are two models of operation. The first model requires a major rewrite of application software for effective use (ie. capsicum).
ここしばらくの間、プログラムを「機能の削減された操作モデル」へ制限するためのサブシステムの開発を行っています。同様の機能はすでにこれまでほかのオペレーティングシステムで実装されているのですが、どうも自分にはしっくりきません。そう考えるのは自分だけじゃないと思います。
ざっくりいってこうした操作には2つのモデルがあります。1つ目のモデルだと、効果的に使用するためにアプリケーションソフトウェアの書き換えが必要になります(Capsicumとか) 。
tame(2) WIPより一部抜粋
Theo de Raadt氏はCapsicumの実装を機能を利用するためにソフトウェアへ追加する変更があると指摘しています。Capsicumはケーパビリティと呼ばれるオペレーティングシステムセキュリティ機能の実装系のひとつで、ソフトウェア側への書き換えが少なく、導入が簡単であるところにポイントがあります。それを指して「書き換えが必要(だから面倒だしな) 」と言い切るあたり、さすがTheo de Raadt氏と言えます。
Capsicumはケーパビリティモデルをかなり洗練された形でPOSIX APIと融合させています。逆に言えば、ケーパビリティモデルという視点が必要ともいえます。Theo de Raadt氏の発想はそうではなく、よく利用する便利な機能制限をフラグにして、ケーパビリティモデルとかそういったことを考えなくも、機能ベースで何を制限するといったように思考し、実装できるようにしよう、というもののように思えます。Capsicumの発想をベースに、さらに扱いやすいエイリアスを追加しよう、といったもののように見えます。
tame(2)からpledge(2)システムコールへ変更 - OpenBSD 5.9
Theo de Raadt氏は2015年10月のコミット で、このシステムコールの名前をtame(2)からpledge(2)に変更するとともに、インターフェースを変更することを伝えました。
図 pledge(2)は次期リリースバージョンOpenBSD 5.9から導入が予定されているシステムコール
# uname -sr
OpenBSD 5.8
# man pledge
/usr/local/man/mandoc.db: No such file or directory
man: No entry for pledge in the manual.
#
tame(テイム)は飼い慣らす、管理下におく、といったニュアンスの言葉です。pledge(プレッジ)は堅い約束、堅い誓いといったニュアンスの言葉です。プログラムを限定されたモードへ突っ込むというニュアンスから(tame)、プログラマがプログラムそのものを制限して動作させるように誓う、約束する(pledge)といったニュアンスの名前に変わったことになります。
tame(2)システムコールのインターフェース
#include <sys/tame.h>
int
tame(int flags);
pledge(2)システムコールのインターフェース
#include <unistd.h>
int
pledge(const char *promises, const char *paths[]);
インターフェースの違いを追ってみますと、tame(2)のときはフラグを指定するだけでしたが、これがpledge(2)では、規制対象一覧とホワイトリストのパス一覧の2つを取るように変更されています。当初考えていたインターフェースよりも、より厳密な指定が必要であるといった判断に変わったのでしょう。このままいくと結局Capsicumと同じインターフェースになっていくような気もしますが、OpenBSDはシンプルを尊ぶ傾向が強いので、このあたりでインターフェースが固まるような気もします。
11月16日現在、pledge(2)の適用済みコマンドたち
Theo de Raadt氏は2015年11月に送付したメール「Status of pledge 」で、OpenBSDのベースシステム(/bin /sbin /usr/bin /usr/sbin and /usr/libexec)にマージされている600個ほどのコマンドのうち、次の368個のコマンドにpledge(2)システムコールを適用したと伝えました。
2015年11月16日までにpledge(2)が適用されたコマンド
ac acpidump addr2line apm apply apropos ar arp as at atq atrm awk
b64decode b64encode banner basename batch bc bgpctl bgpd biff c++
c++filt cal cap_mkdb captoinfo cat cc chgrp chmod chown ci cksum clear
clri cmp co col colrm column comm compress comsat config cp cpio cpp
cron crontab crunchgen csh csplit ctags cu cut date dc dd deroff
dev_mkdb df diff diff3prog dig dirname disklabel dmesg doas du
dvmrpctl echo ed egrep eigrpctl eigrpd encrypt env ex expand expr
fdisk fgen fgrep file find finger fingerd flex flex++ fmt fold from
fsck_ext2fs fsck_ffs fsck_msdos fsdb fsirand fstat ftp ftpd fuser g++
gcc gencat getcap getent getopt getty grep group groupadd groupdel
groupinfo groupmod groups gunzip gzcat gzip head help hexdump host
htpasswd httpd id ident identd ikectl iked indent inetd info infocmp
infokey infotocap install-info iscsictl join jot kdump kill ksh
kvm_mkdb lam last lastcomm ld ldapctl ldapd ldconfig ldpctl ldpd leave
less lesskey lex ln lndir locate locate.bigram locate.code lock
lockspool logger login_activ login_crypto login_passwd login_reject
login_skey login_snk login_tis login_token login_yubikey logname look
ls m4 mailwrapper make makeinfo makewhatis makewhatis man mandoc md5
merge mesg mg mkdir mklocale mktemp more nc ncheck ncheck_ffs
netgroup_mkdb nice nl nm nohup nologin nslookup ntpctl ntpd objcopy
objdump od opencvs openssl ospf6ctl ospfctl ospfd otp-md5 otp-rmd160
otp-sha1 paste patch pax pflogd pgrep ping ping6 pkill portmap pr
printenv printf ps pwd pwd_mkdb radiusctl radiusd radiusd_bsdauth
radiusd_radius ranlib rarpd rcs rcsclean rcsdiff rcsmerge rdate
readelf readlink rebound relayctl relayd renice reset rev ripctl rksh
rlog rm rmdir rmt route route6d rpcgen rs rtadvd savecore scan_ffs scp
script sdiff sed sendbug sftp sh sha1 sha256 sha512 signify size skey
skeyaudit skeyinfo sleep slowcgi smtpctl smtpd snmpctl sort spamdb
spellprog split sshd stat strings strip stty su syslogc syslogd
systrace tail tar tcpbench tcpdump tee telnet test texindex tftp
tftp-proxy tftpd tic time tmux top touch tput tr traceroute
traceroute6 tradcpp tset tsort tty tunefs ul uname uncompress unexpand
unifdef uniq units unvis uptime user useradd userdel userinfo usermod
users uudecode uuencode vi view vipw vis w wall wc what whatis whereis
which who whoami whois write x99token xargs yacc yes ypcat ypldap zcat
zdump zegrep zfgrep zgrep zic zzz ZZZ
pledge(2)システムコールは次のように基本的に1行追加するだけとなっています。このあたりはFreeBSD Capsicumも同じです。システムコールを呼ぶことで制限されたモードに入るといった作りになっています。
pledge(2)システムコールの引数のひとつ目は空白区切りの文字列、2つ目は指定がなければNULLです。ほかのシステムコールと比較すると、かなり特徴的なシステムコールではないでしょうか。
pledge(2)システムコールの適用されたcat(1)コマンドのソースコード(20151117現在のもの)
void cook_buf(FILE *);
void raw_args(char *argv[]);
void raw_cat(int);
int
main(int argc, char *argv[])
{
int ch;
setlocale(LC_ALL, "");
if (pledge("stdio rpath", NULL) == -1)
err(1, "pledge");
while ((ch = getopt(argc, argv, "benstuv")) != -1)
switch (ch) {
case 'b':
bflag = nflag = 1; /* -b implies -n */
break;
case 'e':
eflag = vflag = 1; /* -e implies -v */
break;
case 'n':
まだpledge(2)システムコールが適用されていないコマンドとして次のコマンド一覧も掲載されています。
まだpledge(2)が適用されていないコマンド
gMail a2p accton amd amq apmd atactl aucat audioctl authpf authpf-noip
gbadsect bgplgsh bioctl calendar cdio chat chfn chio chpass chsh cvs
gdhclient dhcpd dhcrelay dump dumpfs dvmrpd edquota eject fdformat fsck
gftp-proxy gcov gdb getconf gpioctl gprof growfs hostapd hotplugd
gifconfig ifstated init installboot iostat ipcs ipsecctl isakmpd iscsid
gkbd keynote kgmon ldattach locale login login_chpass login_lchpass
glogin_radius lpc lpf lpq lpr lprm lptest mail mail.local mailx makedbm
gmakemap map-mbone memconfig midiplay mixerctl mkalias mkhybrid mknetid
gmopa.out mopchk mopd mopprobe moptrace mount mountd mrinfo mrouted
gmtrace mtree mv ndp netstat newfs newfs_ext2fs newfs_msdos newsyslog
gnfsd nfsstat npppctl npppd nsd nsd-checkconf nsd-checkzone nsd-control
gntalkd ospf6d pac passwd pcidump pppstats procmap pstat quot quota
gquotacheck quotaoff quotaon radioctl rbootd rdist rdistd rdump
grepquota restore revnetgroup ripd rpc.bootparamd rpc.lockd rpc.rquotad
grpc.rstatd rpc.rusersd rpc.rwalld rpc.statd rpc.yppasswdd rpcinfo
grrestore rup rusers rwall sa sasyncd scsi sensorsd sftp-server
gshowmount skeyinit sndiod snmpd spamd spamd-setup spamlogd sqlite3 ssh
gssh-add ssh-agent ssh-keygen ssh-keyscan ssh-keysign ssh-pkcs11-helper
gstdethers stdhosts swapctl swapon sysctl systat table-ldap
gtable-passwd table-sqlite talk trpt unbound unbound-anchor
gunbound-checkconf unbound-control unbound-host usbdevs usbhidaction
gusbhidctl vacation vmstat vnconfig watchdogd wsconscfg wsconsctl
gwsfontload wsmoused ypbind ypmatch yppoll yppush ypserv ypset yptest
gypwhich ypxfr
true(1)やhostname(1)のようにソースコードが簡単すぎるものやpledge(2)システムコールの適用ができないコマンドなどは上記一覧には掲載されていないという説明もあります。あまりにシンプルなコマンドはpledge(2)システムコールを適用する意味がほとんどありませんし、reboot(2)やmount(2)などのシステムコールを使用するコマンドに対してはpledge(2)システムコールは適用することができません。uhmやperlなども適用対象外とされています。
適用対象となるコマンドの66%にpledge(2)システムコールが適用されていることになります。
実装速度の速さとセキュリティ機能の徹底さ
カーネルにおける機能の実装自体はFreeBSDの方が先行していますが、FreeBSDはCapsicumを適用するコマンドをかなり限定しています。高いセキュリティ効果が期待できるコマンドから随時Capsicumの実装を適用しています。
一方、OpenBSDでは実装に一年とかかっておらず、ベースシステムのコマンドの6割以上にすでにpledge(2)システムコールが適用されています。OpenBSD 5.9がリリースされる頃にも、もっとたくさんのコマンドにpledge(2)システムコールが適用されているでしょう。
OpenBSDの開発チームは規模の上ではFreeBSDの開発チームよりもこぢんまりとしていますが、開発速度が速く、それがセキュリティを強化するものである場合には今回のようにかなり早いタイミングで取捨が進むところがあります。逆に、FreeBSDの開発チームは新機能の導入や既存のコマンドの変更などには、ある程度の実証が必要といったように慎重な傾向があるように思います。
FreeBSD CapsicumはGoogle Chromeのセキュリティ機能を実装するにあたって効果的に機能しますが、同様の制限がpledge(2)で実装できるかどうかはよくわかりません。このあたりの比較検討は今後開催されるカンファレンスやサミットなどで話がでてくるのではないかと思いますので、機会を捉えて追加で取り上げたいと思います。