OpenSSHのchrootの動作

opensshが接続を受け付けて、chrootする際の挙動に関するメモ

  • session.cを見る

do_setusercontext(struct passwd *pw)

chrootの一番の肝は safely_chroot()

  • if (getuid() == 0 || geteuid() == 0) ... 実行ユーザーがrootであることのチェック
  • if (setlogin(pw->pw_name) < 0)
  • if (setgid(pw->pw_gid) < 0) {
  • if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
  • permanently_set_uid(pw);
  • safely_chroot()
# passwdのエントリと現在の実行ユーザー/IDが同一かどうか
# rootのまんまでないようにする?
	if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
		fatal("Failed to set uids to %u.", (u_int) pw->pw_uid);
ユーティリティ的な関数
  • tilde_expand_filename(options.chroot_directory, pw->pw_uid)
  • chroot_path = percent_expand(tmp, "h", pw->pw_dir, "u", pw->pw_name, (char *)NULL))
    • %表記の展開。 %u と %h をサポート
if (setusercontext(lc, pw, pw->pw_uid,
        (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) {
        perror("unable to set user context");
        exit(1)

safely_chroot()

opensshのchrootの肝。

  • if (*path != '/') ... 絶対パスや否かをチェック
  • パスの長さのチェック
  • 各々のパスコンポーネントがディレクトリとして存在するかどうかをチェックする
  • if( st.st_uid != 0 || (st.st_mode & 022) != 0)... パーミッションがrootで755かどうかのチェック
  • if (!S_ISDIR(st.st_mode)) ... ディレクトリかどうかのチェック
  • chdir(path)
  • chroot(path) ... chdirしてからchroot(2)
  • chdir(/) ... chrootした後、/にすることでchrootが有効かどうか試している

その他

  • if (setpcred(pw->pw_name, (char **)NULL) == -1)
  • do_pam_setcred(use_privsep)