TCP Wrapperの構築

TCP Wrapperの構築のメモ

TCP Wrapperとは hosts.allow hosts.deny ファイルによって
リモートからの TCP接続を制限するアクセス制御機構です (Wikipediaが詳しい)

inetd やら SSH やらいろんなソフトに使われています
IPv6対応版の TCP Wrapperが存在するということで試してみました ビルドの手順をレポートします

検証環境
CPU PhenomII X4 905e (2.5GHz 4コア)
マザーボード GA-880GM-USB3
メモリ 8GB (DDR3-1066)
イーサネット Realtek8111D (R8168 GbE)
グラフィック Radeon HD 4250 オンボード
サウンド Realtek ALC892 codec (HD) オンボード
Linuxカーネルバージョン 2.6.37.1 (x86_64)
GCC 4.5.2
glibc 2.13.1

構築

公式サイトである ftp.porcupine.org にソースがあります
「IPV6 version by Casper Dik (tcp_wrappers_7.6-ipv6.4.tar.gz)」 というのがあるので
これをダウンロードします

ソースアーカイブを展開します

$ tar -xzf tcp_wrappers_7.6-ipv6.4.tar.gz
$ cd tcp_wrappers_7.6-ipv6.4
$
 

ソースコードは 一般的な configureスクリプトが付属していないため
手動で Makefileをカスタマイズする必要があります

REAL_DAEMON_DIR=/usr/sbin
〜
IPV6 = -DHAVE_IPV6
〜
TABLES  = -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\"
〜
CFLAGS  = -O -DFACILITY=$(FACILITY) $(ACCESS) $(PARANOID) $(NETGROUP) \
        $(BUGS) $(SYSTYPE) $(AUTH) $(UMASK) \
        -DREAL_DAEMON_DIR=\"$(REAL_DAEMON_DIR)\" $(STYLE) $(KILL_OPT) \
        -DSEVERITY=$(SEVERITY) -DRFC931_TIMEOUT=$(RFC931_TIMEOUT) \
        $(UCHAR) $(TABLES) $(STRINGS) $(TLI) $(EXTRA_CFLAGS) $(DOT) \
        $(VSYSLOG) $(HOSTNAME) $(IPV6)  -fPIC
 

まず REAL_DAEMON_DIR が設定されてないのでこれを設定する必要があります
これは tcpd などの実行プログラムのインストール先を指定します

また IPv6対応させるために IPV6 の記述を有効化しています

TABLES は既に設定されているので内容の確認です
hosts.allow hosts.deny のパスを修正したい場合はここをカスタマイズします

CFLAGSには -fPIC を追加しています
これは後述にもありますが 共有ライブラリ libwrap.so.0 を作るのに必要です

Makefileを終えたら make コマンドでビルドを開始します
ただし引数に OSの種類 (linux とか freebsd など) を指定する必要があります

$ CC=gcc make linux
make[1]: ディレクトリ `/home/admin/tcp_wrappers_7.6-ipv6.4' に入ります
gcc -O -DFACILITY=LOG_MAIL       -DHOSTS_ACCESS -DPARANOID  -DGETPEERNAME_BUG -DBROKEN_FGETS -DLIBC_CALLS_STRTOK   -DDAEMON_UMASK=022 -DREAL_DAEMON_DIR=\"/usr/sbin\"   -DSEVERITY=LOG_INFO      -DRFC931_TIMEOUT=10  -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\"   -DBROKEN_SO_LINGER -DUSE_STRERROR  -Dvsyslog=myvsyslog -DALWAYS_HOSTNAME -DHAVE_IPV6  -fPIC -c tcpd.c
gcc -O -DFACILITY=LOG_MAIL       -DHOSTS_ACCESS -DPARANOID  -DGETPEERNAME_BUG -DBROKEN_FGETS -DLIBC_CALLS_STRTOK   -DDAEMON_UMASK=022 -DREAL_DAEMON_DIR=\"/usr/sbin\"   -DSEVERITY=LOG_INFO      -DRFC931_TIMEOUT=10  -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\"   -DBROKEN_SO_LINGER -DUSE_STRERROR  -Dvsyslog=myvsyslog -DALWAYS_HOSTNAME -DHAVE_IPV6  -fPIC -c hosts_access.c
hosts_access.c: In function ‘ipv6_mask’:
hosts_access.c:408:5: error: ‘uchar_t’ undeclared (first use in this function)
hosts_access.c:408:5: note: each undeclared identifier is reported only once for each function it appears in
hosts_access.c:408:14: error: ‘p’ undeclared (first use in this function)
hosts_access.c:408:27: error: expected expression before ‘)’ token
hosts_access.c:419:27: error: expected expression before ‘)’ token
make[1]: *** [hosts_access.o] エラー 1
make[1]: ディレクトリ `/home/admin/tcp_wrappers_7.6-ipv6.4' から出ます
make: *** [linux] エラー 2
$
 

未定義 uchar_t でいきなりこけました
ソースコードを確認したところ uchar_t は hosts_access.c の一部でしか使われてなく
文字列を 1バイト毎に操作する目的で使われているだけのようです

対処としては uchar_t を unsigned charに置換します
Makefileの CFLAGSに -Duchar_t=’unsigned char’ を追加します

CFLAGS  = -O -DFACILITY=$(FACILITY) $(ACCESS) $(PARANOID) $(NETGROUP) \
        $(BUGS) $(SYSTYPE) $(AUTH) $(UMASK) \
        -DREAL_DAEMON_DIR=\"$(REAL_DAEMON_DIR)\" $(STYLE) $(KILL_OPT) \
        -DSEVERITY=$(SEVERITY) -DRFC931_TIMEOUT=$(RFC931_TIMEOUT) \
        $(UCHAR) $(TABLES) $(STRINGS) $(TLI) $(EXTRA_CFLAGS) $(DOT) \
        $(VSYSLOG) $(HOSTNAME) $(IPV6)  -fPIC -Duchar_t='unsigned char'
 

再度 makeします

$ CC=gcc make linux
〜
gcc -O -DFACILITY=LOG_MAIL       -DHOSTS_ACCESS -DPARANOID  -DGETPEERNAME_BUG -DBROKEN_FGETS -DLIBC_CALLS_STRTOK   -DDAEMON_UMASK=022 -DREAL_DAEMON_DIR=\"/usr/sbin\"   -DSEVERITY=LOG_INFO      -DRFC931_TIMEOUT=10  -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\"   -DBROKEN_SO_LINGER -DUSE_STRERROR  -Dvsyslog=myvsyslog -DALWAYS_HOSTNAME -DHAVE_IPV6  -fPIC -Duchar_t='unsigned char' -o tcpd tcpd.o libwrap.a 
libwrap.a(misc.o): In function `tcpd_gethostbyname':
misc.c:(.text+0x1e0): undefined reference to `freehostent'
misc.c:(.text+0x1fc): undefined reference to `getipnodebyname'
misc.c:(.text+0x227): undefined reference to `freehostent'
misc.c:(.text+0x243): undefined reference to `getipnodebyname'
collect2: ld はステータス 1 で終了しました
make[1]: *** [tcpd] エラー 1
make[1]: ディレクトリ `/home/admin/tcp_wrappers_7.6-ipv6.4' から出ます
make: *** [linux] エラー 2
$
 

今度は freehostent() や getipnodebyname() の未解決エラーです
これら関数は IPv6の正引きを行うものですが 最近の glibcには含まれておらず
getaddrinfo() と getnameinfo() の利用が推奨されています

TCP Wrapperでは回避策が用意されており
getipnodebyname() の代わりに gethostbyname2() を使う -DUSE_GETHOSTBYNAME2 があります
Makefileを再度修正して IPV6の行を以下のようにします

# If your system does not have getipnodebyname() but uses the obsolete
# gethostbyname2() instead, use this (AIX)
IPV6 = -DHAVE_IPV6 -DUSE_GETHOSTBYNAME2
 

で 再度 makeすると

$ CC=gcc make linux
〜
scaffold.c:28:14: error: conflicting types for ‘malloc’
scaffold.c: In function ‘dup_hostent’:
scaffold.c:62:2: 警告: incompatible implicit declaration of built-in function ‘exit’
scaffold.c: In function ‘check_dns’:
scaffold.c:154:5: 警告: incompatible implicit declaration of built-in function ‘free’
scaffold.c: In function ‘clean_exit’:
scaffold.c:176:5: 警告: incompatible implicit declaration of built-in function ‘exit’
make[1]: *** [scaffold.o] エラー 1
make[1]: ディレクトリ `/home/admin/tcp_wrappers_7.6-ipv6.4' から出ます
make: *** [linux] エラー 2
$
 

malloc() の定義が矛盾していると言われました
scaffold.c を確認修正する必要あります

#ifndef INADDR_NONE
#define INADDR_NONE     (-1)            /* XXX should be 0xffffffff */
#endif

// extern char *malloc();

/* Application-specific. */

#include "tcpd.h"
#include "scaffold.h"
 

char *malloc() の宣言が既に異なってるのでコメントアウトしました
glibcで宣言されている void *malloc( size_t ) が有効になるはずです

$ CC=gcc make linux
〜
gcc -O -DFACILITY=LOG_MAIL       -DHOSTS_ACCESS -DPARANOID  -DGETPEERNAME_BUG -DBROKEN_FGETS -DLIBC_CALLS_STRTOK   -DDAEMON_UMASK=022 -DREAL_DAEMON_DIR=\"/usr/sbin\"   -DSEVERITY=LOG_INFO      -DRFC931_TIMEOUT=10  -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\"   -DBROKEN_SO_LINGER -DUSE_STRERROR  -Dvsyslog=myvsyslog -DALWAYS_HOSTNAME -DHAVE_IPV6 -DUSE_GETHOSTBYNAME2  -fPIC -Duchar_t='unsigned char' -o tcpdchk tcpdchk.o fakelog.o inetcf.o scaffold.o libwrap.a 
make[1]: ディレクトリ `/home/admin/tcp_wrappers_7.6-ipv6.4' から出ます
$
 

やっとコンパイルが通りました
作られたファイルは
バイナリである safe_finger tcpd tcpdchk tcpdmatch try-from と
ライブラリ本体である libwrap.a
Cヘッダファイル tcpd.h
マニュアルページ tcpd.8 tcpdchk.8 tpcdmatch.8 hosts_access.5 hosts_options.5 hosts_access.3

これらをインストールします
しかも make install が使えなかったので 手動でインストールします

$ su
# cp safe_finger /usr/sbin/
# cp tcpd /usr/sbin/
# cp tcpdchk /usr/sbin/
# cp tcpdmatch /usr/sbin/
# cp try-from /usr/sbin/
# cp libwrap.a /usr/lib64/
# cp tcpd.h /usr/include/
# cp tcpd.8 /usr/share/man/man8/
# cp tcpdchk.8 /usr/share/man/man8/
# cp tcpdmatch.8 /usr/share/man/man8/
# cp hosts_access.5 /usr/share/man/man5/
# cp hosts_options.5 /usr/share/man/man5/
# cp hosts_access.3 /usr/share/man/man3/
#
 

もうひとつ 共有ライブラリである libwrap.so.0 が欲しいのですが作られていません

不足ファイルの作成

まず /etc/hosts.allow /etc/hosts.deny を作成します

hosts.denyから

ALL : ALL
 

これにより hosts.allowにマッチしないアクセスは 全て遮断されます
hosts.allowです

ALL : 127.0.0.1
ALL : 192.168.0.0/255.255.255.0
ALL : [::1/128]
ALL : [fe80::/16]
 

許可するアドレスとして
IPv4では ループバック 127.0.0.1 と LANのセグメント 192.168.0.0/24
IPv6では リンクローカル ::1 と サイトローカル fe80::
以後 アクセス許可したい アドレス ネットワークが増加すれば追記する形にします

最後に libwrap.so.0 ですがこれを作る方法は ソースアーカイブに記述はなさそうです
ただ現状 本検証環境(Gentoo stage3ベース)では libwrap.so.0 が存在し使われています
(どうやって作ったのでしょうか?)

先ほどの TCP Wrapperのビルドの過程を参考にします

ar rv libwrap.a hosts_access.o options.o shell_cmd.o rfc931.o eval.o hosts_ctl.o refuse.o percent_x.o clean_exit.o setenv.o fromhost.o fix_options.o socket.o tli.o workarounds.o update.o misc.o diag.o percent_m.o myvsyslog.o
amd64-linux-gnu-ar: creating libwrap.a
a - hosts_access.o
a - options.o
a - shell_cmd.o
a - rfc931.o
a - eval.o
a - hosts_ctl.o
a - refuse.o
a - percent_x.o
a - clean_exit.o
a - setenv.o
a - fromhost.o
a - fix_options.o
a - socket.o
a - tli.o
a - workarounds.o
a - update.o
a - misc.o
a - diag.o
a - percent_m.o
a - myvsyslog.o
ranlib libwrap.a
 

この部分です libwrap.a をアーカイブするのに必要なオブジェクトが分かります
単純に考えれば これを参考に下記のように直接 libwrap.so.0 が作れるはずです

# gcc -shared -o /usr/lib64/libwrap.so.0 -Wl,-soname,libwrap.so.0  hosts_access.o options.o shell_cmd.o rfc931.o eval.o hosts_ctl.o refuse.o percent_x.o clean_exit.o setenv.o fromhost.o fix_options.o socket.o tli.o workarounds.o update.o misc.o diag.o percent_m.o myvsyslog.o
#
 

(注意 上記コマンドで作った libwrap.so.0 はうまく動作しません)
たまたま SSHが動作検証に使えたのでリモート 192.168.0.3 から本システム 192.168.0.254
にアクセスしてみます

$ ssh 192.168.0.254
ssh_exchange_identification: Connection closed by remote host
$
 

? 許可されません
先ほど libwrap.so.0 を作ったシステムで sshd を実行してみると分かりますが
libwrap.so.0 がうまく作れていません

# sshd -h
sshd: symbol lookup error: sshd: undefined symbol: deny_severity
#
 

このエラーメッセージの意味するところは
libwrap.so.0 で deny_severity (と allow_severity)が定義されていないため
sshd と libwrap.so.0 の実行時リンクに失敗しているのです

次のようにして deny_severity allow_severity を定義済にした libwrap.so.0 を作ります

# echo 'int allow_severity;  int deny_severity;' | gcc -shared -o /usr/lib64/libwrap.so.0 -Wl,-soname,libwrap.so.0  hosts_access.o options.o shell_cmd.o rfc931.o eval.o hosts_ctl.o refuse.o percent_x.o clean_exit.o setenv.o fromhost.o fix_options.o socket.o tli.o workarounds.o update.o misc.o diag.o percent_m.o myvsyslog.o -x c -
 

これにより作られた libwrap.so.0 は allow_severity deny_severity が解決されています

# nm /usr/lib64/libwrap.so.0 | grep severity
00000000002085cc B allow_severity
00000000002085c8 B deny_severity
0000000000003af0 t severity_map
0000000000003b3c t severity_option
#
 

また 遠隔 192.168.0.3 からの sshアクセスに成功しました

$ ssh 192.168.0.254

$ 
 

さらに検証可能ならば /etc/hosts.allow を一時的に修正した検証を行い
192.168.0.0/255.255.255.0 の行を削除すると リモートからのログインができなくなる
というような動作まで確認するとよいでしょう

とりあえず libwrap.so.0 は完成としますが 心に引っかかる点として
libwrap.a は未だに allow_severity deny_severity が未解決のままである

# nm /usr/lib64/libwrap.a | grep severity
                 U allow_severity
                 U deny_severity
0000000000000000 t severity_map
000000000000004c t severity_option
                 U deny_severity
#
 

というところが 後々問題になるかもといったところです
そもそも標準で libwrap.so.0 を作る手段が提供されていないといった点も 引っかかりますが

IPv6サーバツールへ戻る