Ubuntu 14.04では、passwd
実行可能ファイルは
-rwsr-xr-x 1 root root 47032 gen 27 01:50 /usr/bin/passwd
ping
実行可能ファイルは
-rwsr-xr-x 1 root root 44168 mag 7 2014 /bin/ping
したがって、(両方の場合)実行中のプロセスのuidは、通常のユーザーから実行された場合でも、root
になるはずです。 user1
からpasswd
を実行すると、実際には次のようになります。
$ ps -aux | grep passwd
root 4317 0.0 0.0 85940 2004 pts/0 S+ 10:24 0:00 passwd
しかし、user1
からping
を実行すると、同じではありません。
$ ps -aux | grep ping
user1 4362 0.0 0.0 6500 632 pts/0 S+ 10:29 0:00 ping 192.168.8.1
最初のケースではプロセスのuidがroot
に設定され、2番目のケースでは設定されていないのはなぜですか?
@schilyが言うように、pingユーティリティ(およびその他)では、ルートパーミッションは不要になった後に削除されます。これはセキュリティ上の理由から行われます。
Ping.cから--main()-ユーザーrootは、getuidおよびsetuid呼び出しで削除されます。
getuid()は現在のユーザーを取得し、rootがsetuid()を実行すると、プロセスのuidが変更されます。
/*
* Pull this stuff up front so we can drop root if desired.
*/
if (!(proto = getprotobyname("icmp"))) {
(void)fprintf(stderr, "ping: unknown protocol icmp.\n");
exit(2);
}
if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
if (errno==EPERM) {
fprintf(stderr, "ping: ping must run as root\n");
}
else perror("ping: socket");
exit(2);
}
#ifdef SAFE_TO_DROP_ROOT
setuid(getuid()); /* HERE RETURNING TO THE USER */
#endif
@ rui-f-ribeiroの発言の一般的な意味は正しいですが、詳細は正しくありません。詳細は重要です。 Ubuntuはこれらのパッケージを使用します:
Pingユーティリティは、pingとping6で共有されるlimit_capabilities
という名前の関数のアクセス許可をリセットします。関連するコードのチャンクは次のようになります。
if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
perror("ping: prctl");
exit(-1);
}
if (setuid(getuid()) < 0) {
perror("setuid");
exit(-1);
}
if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
perror("ping: prctl");
exit(-1);
}
cap_free(cap_p);
cap_free(cap_cur_p);
#endif
uid = getuid();
euid = geteuid();
#ifndef CAPABILITIES
if (seteuid(uid)) {
perror("ping: setuid");
exit(-1);
}
#endif
つまり(ソースコードを読んで)、ping
はいくつかの特権操作を実行し、特権を削除しますが、好みに応じてさまざまな方法で動作するように構築できます。
興味深いことに、変更ログは次のように述べています。
iputils (3:20121221-2) unstable; urgency=low
* Enable the CAP_NET_RAW capability and strip the setuid bit on ping and
ping6 binaries if possible.
passwd
のストーリーは似ていますが、詳細が異なります。これはshadowツールスイートの一部であり、change_root
で特権を削除する可能性があります。
/* Drop privileges */
if ( (setregid (getgid (), getgid ()) != 0)
|| (setreuid (getuid (), getuid ()) != 0)) {
fprintf (stderr, _("%s: failed to drop privileges (%s)\n"),
Prog, strerror (errno));
exit (EXIT_FAILURE);
}
しかし、これは特別な場合にのみ行われます。
/*
* process_root_flag - chroot if given the --root option
*
* This shall be called before accessing the passwd, group, shadow,
* gshadow, useradd's default, login.defs files (non exhaustive list)
* or authenticating the caller.
*
* The audit, syslog, or locale files shall be open before
*/
通常の場合、特権を持っていることを確認し、それらを削除しません(特権を必要としない他に何もすることがないため):
if (setuid (0) != 0) {
(void) fputs (_("Cannot change ID to root.\n"), stderr);
SYSLOG ((LOG_ERR, "can't setuid(0)"));
closelog ();
exit (E_NOPERM);
}
ほとんどのユーティリティは、setuid/setgidの動作をリセットせず、それらの権限がインストールされていないことを前提としています。
ソケットが開かれた後はrootである必要がなくなったため、2番目のプロセスのuidはすでにリセットされています。
チェックしたときに、passwd
ユーティリティには引き続きroot権限が必要です。
これを確認したい場合は、uidのリセットが速すぎて、リセット前にuidを確認する機会を他の人に与えることができないため、ソースコードを確認する必要があります。