web-dev-qa-db-ja.com

set-uid実行可能ファイルと結果のプロセスユーザー

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番目のケースでは設定されていないのはなぜですか?

9
BowPark

@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
5
Rui F Ribeiro

@ 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の動作をリセットせず、それらの権限がインストールされていないことを前提としています。

5
Thomas Dickey

ソケットが開かれた後はrootである必要がなくなったため、2番目のプロセスのuidはすでにリセットされています。

チェックしたときに、passwdユーティリティには引き続きroot権限が必要です。

これを確認したい場合は、uidのリセットが速すぎて、リセット前にuidを確認する機会を他の人に与えることができないため、ソースコードを確認する必要があります。

4
schily