システム奮闘記:その48

ftpサーバー設定



Tweet

(2006年4月13日に掲載)
はじめに

  ftpといえば、データ転送のコマンドとして使われる。

  最近では、データの盗聴を防止するために、scpが使われたりする。
  scpコマンドについては「システム奮闘記:その39」をご覧ください。
 (SSHによるリモートコントロールの話)

  scpを使う理由は、認証ID、パスワード、そして通信データの暗号化をして、
途中の経路で、通信の中身の盗聴を防ぐために使われているが、
社内のサーバー間でデータ転送のやりとりをする限りは・・・

  慣れ親しんだftpを使うのらー!!

  なのだ (^^)
  他の企業なら「内部犯行の危険性を考えろ」と言われそうだが、

  うちの会社にそんな技術を持った人はいないのだ!

  なのだ (^^)

  というわけで今回は、ftpの話や、ftpサーバー構築の話をしたいと思います。

まずは、ftpのデータ転送について概略を説明します。 ftpのデータ転送方法には2種類ある。 Portモード(Activeモードとも呼ばれる)と、Passiveモードだ。
ftpを使ったデータ転送の仕組み・その1 (Portモード)
ftpを使ったデータ転送の仕組み(Portモード)
Portモードでは、コマンド操作の通信にポート「21」
データ転送の通信ポートに「20」を割り当てている。
ftpを使ったデータ転送の仕組み・その2(Passiveモード)
ftpを使ったデータ転送の仕組み(Passiveモード)
Passiveモードでは、コマンド操作の通信にポート「21」
データ転送の通信ポートに「N」を割り当てている。
「N」は、1024以上の任意の番号です。

  なぜ、コマンド操作の通信ポートと、データ転送の通信ポートを
2つに分けているのかについては、後述しています。


  さて、RedHat7.3を愛用している私。
  パッケージとしてついているのは、wu-ftpdと呼ばれる物だ。
  だが、RedHat7.3自体のサポートは2003年12月に終了している。
  つまり、rpmファイルの提供がストップしているのだ。

  簡単にソースコンパイルできるソフトは、コンパイルをしているが、
wu-ftpdは、rpmを使っていた上、ソース自体、どこにあるのか知らなかった。
  他のソフトなら探すのだが、ftpの場合、探す気すらなかった。
  なぜなら・・・

  社内LANのだけにしか使わへんもーん (^^)

  なのだ。

  つまり外部に向けて、ftpサーバーを構築する気が全くなかったため、
セキュリティーの事を全く考えていなかったのだ。

  だが、2006年になり、別の観点からソースで入れ直したいと思うようになった。
  それは、ある程度、Linuxの仕組みや、C言語がわかるようになったからだ。

  そこで、ソースコンパイルに挑戦してみる事にした。

ftpとの出会い

ところで、本題に入る前に、ftpについて、私の過去の話をしたいと思います。 ftpとの出会いは、1994年の大学時代に遡る。 某先端大学院大学のサイトで、おねーちゃん画像が大量にあったため 大量のダウンロードしていた。 だが、当時はUNIXのハードディスクは貴重で、10Mといえば ディスクの食い過ぎだった。 しかも、データの中身が中身なものだから、情報センターの職員から 何を考えているねん! と怒られた。 そこでセンターの職員からハードディスクの中身をフロッピーに移す方法を 教えてもらった。 その時、MS-DOSの端末からftpコマンドでUNIXに接続して、 フロッピーディスクのあるドライブにデータを落とし込む方法だった。
ftpを使ってデータを落とし込む方法
ftpを使ってデータを落とし込む方法

  だが、ftpコマンドを使っても、端末のハードディスクにデータが
落とし込まれる。

  そこでセンターの職員に聞きに行くと、端末側のディレクトリーを変更して
フロッピードライブを指定しないといけない事を教えてもらった。
  もちろん、操作方法を教えてもらう。

クライアント側のディレクトリー変更をする時のコマンド
ftp> lcd a:¥
Local directory now A:¥.
ftp>
上のコマンドの「¥」は、実際には半角文字なのですが
UNIX系のOSでは表記できないので、ここでは表記させるためだけに
全角文字を使いました。

  さて、無事にフロッピーディスクに落とし込む事ができたのだが、
そのデータが見れない。
  そこでセンターの職員に聞きにいくと、「データがバイナリだから
ftpをする時、binコマンドを打ってね」と言われた。

バイナリデータをする時、binコマンドを使う
ftp> bin
200 Type set to I
ftp> 
データにアスキーと、バイナリの2種類があるのは知っていたが、
ftpでデータ転送を行う時に指定する必要があるのは知らなかった。

UNIX同士での転送なら、binコマンドは不要だ。
それについては、後述しています。

  さて、無事、大量のおねーちゃん画像をフロッピーに保管できた。
  フロッピーを使って、何人かの友人に渡したりしたものだ。

  だが、数年後、データを整理するため見てみると・・・

  コレクションのデータが見られへん (TT)

  だった。
  この時、フロッピーにはデータを長期間保管する事ができないのを
身をもって体験したのだった。


ftpサーバー設定の必要性を感じる

時は流れ社会人になり、そして2000年に会社でLinuxを導入した。 最初に入れたのは、RedHat6.1で、その次はRedHat6.2だった。 この2つは、wu-ftpサーバーを使っている上、デフォルトで サーバーが立ち上がる状態だった。 そのため・・・ ftpサーバーはデフォルトで立ち上がる! という思い込みがあった。 その前に、ftpサーバーという言葉は知っていても、サーバーを立ち上げる という意識は全くなかった。 UNIX系では、ftpコマンドを使えば、何もしなくても、 自由にお互いデータ転送ができるものだと思い込んでいたのだ。 だが、2003年2月に、RedHat7.3を導入。 RedHat7.3の場合、デフォルトではwu-ftpdは入っていない。 なので、CD-ROMから、RPMファイルを読み出して、インストールしないと いけないのだ。 だが、そんな事を知らなかった私は、RedHat7.3を入れた時、 RedHat7.3を入れたサーバーへftpの接続ができないため なんでやねん (TT) と混乱した事があった。 設定本に、wu-ftpdを入れる必要がある事が書いてあったので、 CD-ROMに入っているRPMファイルでインストールを行った。 それ以来、ftpサーバーという物を意識するようになった。

ftpに関して誤解していた話

他にも、ftpに関して誤解をしていた事があった。 Linuxを導入した後、多少はネットワークのお勉強をしたので、 ポートというのを覚えた。 だが、ネットワークの事がわかっていなかったせいもあり、 以下のような誤った認識で覚えていたのだ。
ftpの誤った認識・その1
ftpのポート番号について誤解していた点
コマンド操作も、データ通信も全て通信ポート「21」で
やりとりされる物だと思い込んでいた。

  だが、そんな思い込みが修正される時が来た。
  2003年2月にインターネットの回線を、ISDN回線からADSL回線へ
切り替える時だった。
  ドツボにハマるのだけは避けたいと思い、ルーターや通信パケットの
勉強をしている時、コマンド操作のポートと、データ転送のポートが
別にある事と、通信データのポートには2種類ある事を知った。

  Portモード(Activeモードとも呼ばれる)と、Passiveモードの2種類だ。

  だが、その時も、とんでもない誤解をしていたのだった。

ftpの誤った認識・その2 (Portモード)
Portモードで誤解していた内容
ftpの誤った認識・その3 (Passiveモード)
Passiveモードで誤解していた内容
データ通信を行う時のポートだが、サーバー側のポートなのに
私はクライアント側が開いたポートだと誤解をした。

  さて、上のような誤解をした状態で「システム奮闘記:その18」を書き
以下の絵を使っていた。
 (ルーターの設定のお勉強)

ftpの使用ポートで誤解していた点 (Portモード)
ftpのデータ転送でPortモードで誤解していた点
普通に見た感じでは、サーバーのポート「20」から
データが転送されている絵に見える。

だが、この時、私は、データ転送の際、クライアントの
ポート「20」へ向かって通信しているつもりで描いた絵なのだ。

  つまり、相当、勘の鋭い人は、私が大嘘を書いている事に
気づいていたのかもしれない (^^;;

  さて、この誤りに気づいたのは、2004年5月だった。
  中学の友人Y君とのメールのやりとりがキッカケだった。

  Y君に2つの通信モードがある事を聞かれて、上の通信方法の
知識を書いたら、間違えを指摘された。

Y君からのメール
Port モードだと

   Client        Server
          ======> 21        ftp  接続要求
          ======>           PORT コマンド(n番ポートを指定)
        n <-----  20        ftp-data 接続要求(※これが FireWall不可)
          <----->           データ転送

Passive だと

   Client        Server
          ======> 21        ftp  接続要求
          ======>           PASV コマンド
          <======           ftp-data ポート番号通知(n番ポートを指定)
          ------> n         ftp-data 接続要求
          <----->           データ転送

というのが正解.

  この時、Y君に、ftpの場合、コマンド操作とデータ通信のポートの
2種類があるのかも教えてもらった。

私が書いたメール
>データ制御とデータ送受信とで異なるポートを使うのはなぜか?

知らない。
以前、本で読んだ時「当時、2つに分ける実装しかできなかった」という
記憶があるけど、確かでない・・・。
Y君からのメール
>  以前、本で読んだ時「当時、2つに分ける実装しかできなかった」という
>   記憶があるけど、確かでない・・・。

ん〜, 正解は
  「〜したいんだけど, それを実現するには2つに分けるしかなかった」
という感じ. 「〜」の部分は「データ転送中でも制御が出来るように」
が正解. 接続が一つだと, データ転送中に制御コマンドを送れないんですよ.

  なるほどと思った。データ転送中にも制御できるようにするために
2つのポートを使って通信経路を確保している。


ftpサーバー設定

まぁ、こんな感じで、思いっきり誤解 & 思い込みをしながらも なんとか正しい知識を身につける事ができた。 さて、RedHat7.3を愛用している私なので、必然的にパッケージのある wu-ftpdと呼ばれるftpサーバーソフトを使っている。 だが、RedHat7.3自体のサポートは2003年12月に終了している。 つまり、rpmファイルの提供がストップしているのだ。 簡単にソースコンパイルできるソフトは、コンパイルをしているが、 wu-ftpdは、rpmを使っていた上、2003年当時は、wu-ftpdのソース自体、 どこにあるのか知らなかった。 他のソフトなら探すのだが、ftpの場合、探す気すらなかった。 なぜなら・・・ 社内LANのだけにしか使わへんもーん (^^) なのだ。 つまり外部に向けて、ftpサーバーを構築する気が全くなかったため、 セキュリティーの事を全く考えていなかったのだ。 その上、普通に使えていたので、問題にする事はなかった。 2006年になるまで、ftpについては、何も調べる事なく過ごしていた。 だが、2006年になり、ふと思った。 そういえば、ftpについて、知らへん事が多そうだ。 wu-ftpdについて調べだしたら、大量のボロが出そうな気がした。 実は、この時、以下の事情があった。 システム奮闘記の、いくつかのネタが立往生していたのだった。 理由は、自分の技術や知識では実験もできない、googleなどで調べても、 内容が理解できない状態だ。 ニッチモサッチもいかない状態だったため、更新ができない状態に 陥る所にあった。 そんな事情があるために、まだボロ出しをしていない場所を攻める事を考えた。 それが、ftpサーバーの話だ。 さて、2006年2月、wu-ftpdを新しいバージョンに入れ直そうと考えた。 まずは、wu-ftpdの設定ファイルで、設定方法を知るために、 「RedHat7.3対応で作るLinux7」(サーバー構築研究会:秀和システム) を取り出す。 だが、ftpサーバーの設定ファイル(/etc/ftpaccess)を見たり、 本の設定部分を見たりするのだが、あまり乗り気が起こらないので、 お得意の「忍法・先送りの術」を使って、まずはインストールから考えた。 2003年12月で、RedHat7.3版のRPMは、RedHat社からの供給は止まっている。 そのため、ソースコンパイルから行う事を考えた。 そこで、wu-ftpのサイト(http://www.wu-ftpd.org/)を探すが・・・ 2003年以降、更新が止まっている (・o・) 目が点になった。 そこで、googleで探していると、Proftpという物を発見した。 ftpサーバーのソフトに種類があったのは知らなかった!! 次のサイトを見ながら、設定を行おうとした。 http://www.aconus.com/~oyaji/ftp/proftpd.htm 読んで行くと問題が発生した。 PAMの設定を触る必要があったのだ! この時、PAMが認証に関する物だという認識はあったのだが、 実際に、どんな物かは知らなかった。 そこで、まずは、PAMのお勉強から入る事にした。 しばらく、PAMのため、ftpのネタはお預けになるが、ネタが増えて良かった (^^) 詳しくは「システム奮闘記;その47」(LinuxのPAM認証の設定入門)をご覧ください。 なんとか、PAMの話が理解(?)できた所で、実際に、ソースコンパイルで インストールを行う事にした。 http://www.proftpd.org/からダウンロードを行った。 安定バージョンが、その時点では、1.2.10、だったので、それを使った。 まずは、ファイルを展開する。
ファイルを展開した様子
[root@xxx proftpd-1.2.10]# ls
COPYING         README.IPv6          README.ports   doc
CREDITS         README.LDAP          RELEASE_NOTES  include
ChangeLog       README.PAM           acconfig.h     install-sh
INSTALL         README.Solaris2.5x   aclocal.m4     lib
Make.rules.in   README.Unixware      config.guess   modules
Makefile.in     README.capabilities  config.h.in    proftpd.spec
NEWS            README.classes       config.sub     sample-configurations
README          README.controls      configure      src
README.AIX      README.cygwin        configure.in   stamp-h.in
README.FreeBSD  README.modules       contrib        utils
[root@xxx proftpd-1.2.10]# 

  そして、INSTALLファイルを見てみる。

  英語で書かれている・・・ (--;;

  仕方なく、渋々、英語を読む事にした。

INSTALLファイルの中身 (一部抜粋)
1.  Configure the software.
    Run the GNU autoconf 'configure' script in the top level directory to
    create config.h and all the Makefiles.  In addition to configuring
    ProFTPD to compile on your system, you can use this step to customize
    some parameters of or add optional features to ProFTPD.  There are many
    configuration options.  To use the default values, you would simply run:

        $ ./configure

  ソースコンパイルの場合、makeする前に、インストールするマシンの
環境チェック等を行うために、configureコマンドを使う。

  ふと思った。

  そもそも、configureの意味って何?

  今まで、Apache、PostgreSQL、PHPなどは、ソースコンパイルで
インストールしていたのだが、configureの意味を考えずに
ただ、インストールマニュアルの真似をしていただけなのだ。

  そこで「configure」の意味を英和辞典で調べてみる事にした。

「configure」の意味
 英和辞典で調べてみると名詞の「configuration」が載っていた。
  意味は「形状、輪郭、外形」があった。

  これでは何の事か、イマイチわからんので、英英辞典を調べてみた。
  すると「configure」には以下の意味がある事が書かれていた。

  to arrange something for particular purpose

  日本語の意味だと「特定の目的のために、何かを編集する」となる。
  うーん、私は、翻訳家ではないので、イマイチ、訳は下手だなぁ

  まぁ、英英辞典なので、該当の英単語を回りくどく説明しているため、
それを訳したら、回りくどい日本語になってしまうのは当然だ。

  日本語の単語に当てはめると「編集する」とか「まとめる」になるかなぁ。

  うーん、どうもスッキリしないのだが、なんとなく意味合いがわかった。

  それを踏まえた上で、コンパイルの際に、configureコマンドの意味を
調べてみる事にした。

  すると、わかりやすい

configureコマンドの意味
configureコマンドは、スクリプトで書かれている。
OSの種類、必要なライブラリのディレクトリなどを拾い出しては
Makefileの作成に反映させる役目がある。

オプションをつける事により、コンパイルした後の実行ファイルや
環境ファイルのディレクトリなどを指定する事ができる。

  要するに、インストールするマシンの環境や、インストール先の情報など
様々な情報を「編集(configure)」して、Makefikeを作成するのだ。

  うーん、やはりコマンドを辞書で調べる必要があるなぁと思った。

  それを踏まえた上で、INSTALLファイルを読んで行く。

INSTALLファイルの中身 (一部抜粋)
By default proftpd and ftpshut are installed in /usr/local/sbin/,
ftpcount and ftpwho in /usr/local/bin/, the configuration file in
/usr/local/etc/, and the man pages in /usr/local/man/man?/.  Further,
/usr/local/var/proftpd/ is used to hold the runtime scoreboard files.
訳してみると、以下の感じだ。

何もオプションをつけない状態だと、proftpdデーモン、ftpshutは
/usr/local/sbin/ ディレクトリーに置かれる。
ftpcount、ftpwhoは、 /usr/local/bin/ ディレクトリーに置かれる。
編集などを行うファイル(設定ファイル)は、/usr/local/etc に置かれる

かつて、/usr/local/var/proftpd/ のディレクトリーは、
ランタイムスコアボード(runtime scoreboard)ファイルが置かれていた。

  ランタイムって何?

  調べてみると以下の事だという。

  アプリケーションを実行する時に必要なソフトウェアモジュール(部品)の事

  わかったような、わからんような説明だ。
  「ランタイム・スコアボードファイル」は、ソフトの部品が記述するための
ファイルという類推ができるのだが、この時点では、どういう意味かは
全く見当がつかなかった。
  そのため、単に、何の設定ファイルだろうと思い、調べる事はしなかった。
  このファイルの話は、後述しています。

INSTALLファイルの中身 (一部抜粋)
See the "Directory and file names" section of the './configure --help'
output for the arguments to change these defaults.  For instance,
to place all these directories under /usr/ rather than /usr/local/,
you could use:

    $ ./configure --prefix=/usr

Or, to place the configuration file in /etc/ and the runtime state
files in /var/proftpd/, you would use:

    $ ./configure --sysconfdir=/etc --localstatedir=/var
デーモンやその他プログラムの実行ファイルのインストール先を
/usr/binにする場合は「--prefix=/usr」のオプションをつける。

設定ファイル等を /etcディレクトリに置きたい場合には
「--sysconfdir=/etc」のオプションをつける。

ランタイムスコアボードファイル等のファイルを /var/proftpd
ディレクトリに置きたい場合は「--localstatedir=/var」の
オプションをつける。

  これを見て、別に、デーモン等が置かれる位置を変更する気はないので
オプションは付けない事にした。 

  だが、その他のファイルは、他のサーバーソフトの設定ファイルが
置かれている場所と一緒にした方がわかりやすいと考えて、
configureに、2つのオプションをつけた。

       configure --sysconfdir=/etc --localstatedir=/var

  あとは、特にオプションを付ける必要はないようなので、そのまま、
Makefikeを生成した。makeを実行して、無事、コンパイルが終わった。
  そして、「make install」を行い、無事、インストールが完了した。

  さて、設定を行う。

  まずは、ftpのインストール前に、勉強をしたPAMの設定を行う。
  README.PAMファイルを読んで設定を行う。
  まぁ、README.PAMの丸写しなのだが、/etc/pam.d/ftp ファイルの中身は、
以下の通りなる。

/etc/pam.d/ftp の中身
#%PAM-1.0
auth       required     /lib/security/pam_pwdb.so shadow nullok
account    required     /lib/security/pam_pwdb.so
session    required     /lib/security/pam_pwdb.so

  これを見て「ぬぬっ」と思った。
  pam_pwdb.soなんぞ、見た事がないモジュールだ。
  もちろん、「システム奮闘記:その47」(LinuxのPAM設定入門)の
PAMの話でも触れていない。

  そこで調べて見る事にした。
  すると、UNIXの標準認証のpam_unix.so モジュールに置き換わる物だという。

  なんの事だか、よくわからん (--;;

  調べて行くと、どうやら、NISには使えないが、RADIUSには使える以外は、
pam_unix.so モジュールと働きは変わらないようだ。

  話は前後するのだが、ftpサーバーが起動した後、PAMの部分の設定を
試しに以下のように書き換えてみたが、問題なく動いた。

/etc/pam.d/ftp の中身
#%PAM-1.0
auth       required     /lib/security/pam_unix.so shadow nullok
account    required     /lib/security/pam_unix.so
session    required     /lib/security/pam_unix.so

  何が違うのか明確な物が見えない。
  ただ、言えるのは、pam_pwdb.soモジュールは、RedHat社が出したようで
他のディストリビューターでは装備されているかどうかの保証はない。
  ちなみに、自宅のPlamoLinux4.0には、入ってなかった。
  そのため、pam_unix.soを使うのが無難かもしれない。


  ところで、ftpを許可しないユーザーとして、/etc/ftpusersファイルがある。
  これも実は、PAMの設定の影響なのだ。

/etc/ftpusersファイルで、許可しないユーザーの設定
auth       required     /lib/security/pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed

  これでPAMの設定は整った。
  

  ここで、inetd(xinetd)を使わずに、ftpサーバーを立ち上げる事にした。
  スタンドアローンの状態だ。

  ちなみに、スタンドアローンとは「Stand alone」の意味で、
「一人で立つ」や「独立して」の意味合いになる。
  要するに、inetd(xinetd)に依存しない(つまり独立して)という意味だ。
  何か、ドンピシャの日本語訳があれば良いのでが、思いつかない (--;;


  次に、設定ファイル(/etc/proftpd.conf)の設定を行う。
  ほとんど初期状態のままで使える。

/etc/proftpd.conf の設定
ServerName                      "ProFTPD Default Installation"
ServerType                     standalone
DefaultServer                   on
Port                            21
Umask                           022
MaxInstances                    30
User                            nobody
Group                           nobody
AllowOverwrite          on

<Limit SITE_CHMOD>
  DenyAll
</Limit>

<Anonymous ~ftp>
  User                          ftp
  Group                         ftp
  UserAlias                     anonymous ftp
  MaxClients                    10
  DisplayLogin                  welcome.msg
  DisplayFirstChdir             .message
  <Limit WRITE>
    DenyAll
  </Limit>
</Anonymous>
赤い部分は、スタンドアローンで起動する意味。
青い部分は、デーモンの所有者と、グループ名の指定

  さて、スタンドアローンで起動させる準備は整ったので、
早速、デーモンを立ち上げてみる。


  さて、他のマシンからftpで接続してみる。

他のマシンからftpで接続してみた
[suga@xxx suga]# ftp 192.168.X.Y
Connected to 192.168.X.Y (192.168.X.Y).
220 ProFTPD 1.2.10 Server (ProFTPD Default Installation) [192.168.X.Y]
Name (192.168.X.Y:suga): suga
331 Password required for suga.
Password:
230 User suga logged in.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
227 Entering Passive Mode (192,168,X,Y,225,97).
150 Opening ASCII mode data connection for file list
-rw-r--r--   1 (?)      (?)         61440 Feb 10  2005 aaa.tar
drwxrwxr-x   2 (?)      (?)          4096 Dec  5 01:28 cata
-rw-r--r--   1 (?)      (?)        499653 Feb 10  2005 data.tar.gz
drwxrwxr-x   2 (?)      (?)          4096 Feb 24 07:21 ftp
-rw-r--r--   1 (?)      (?)         35916 Jun 12  2005 lha-1.14i-4.i386.rpm
-rwxr--r--   1 (?)      (?)        165258 Feb 16  2005 ms-fusei.bmp
-rw-r--r--   1 (?)      (?)         60277 Feb 10  2005 program.tar.gz
226 Transfer complete.
ftp>

  どうやら、問題なく動いているようだ。
  実際に、データ転送を行ってみる。

データ転送を行う様子
ftp> bin
200 Type set to I
ftp> get  lha-1.14i-4.i386.rpm
local: lha-1.14i-4.i386.rpm remote: lha-1.14i-4.i386.rpm
227 Entering Passive Mode (192,168,X,Y,225,135).
150 Opening BINARY mode data connection for lha-1.14i-4.i386.rpm (35916 bytes)
226 Transfer complete.
35916 bytes received in 0.00202 secs (1.7e+04 Kbytes/sec)
ftp> 

  データ転送成功! (^^)V

  ワーイワーイと喜ぶ私なのだが、これで終了というわけにいかない。

  そこで、他のマシンからftpした場合、ftpサーバーでの挙動を
見てみる事にした。 
  まず、ftpサーバで、デーモンの動きを見るため、psコマンドを使った。 

ftpサーバー上でpsコマンドを打つと
[suga@yyy suga]# ps aux | more
USER       PID %CPU %MEM   VSZ  RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0  1372   80 ?        S    Mar06   0:09 init [5] 
root         2  0.0  0.0     0    0 ?        SW   Mar06   0:00 [keventd]

(途中、省略)


nobody   13312  0.0  0.4  2208 1088 ?        S    14:25   0:00 [proftpd]


(この後、省略)
青い部分がftpサーバーのデーモン。
nobodyの所有者権限で動いているのがわかる。

  ftpデーモンの権限が、rootではなく、nobodyになっている。

  セキュリティー上、好ましい事なのだ!

  もし、ftpデーモンがrootで動いていると、バッファオーバーフロー攻撃に
遭った場合、簡単にroot権限が奪取される危険性があるからだ。

  ちなみに、wu-ftpのデーモンはrootで動くので乗っ取られたら危険なのだ。

  バッファオーバーフロー攻撃についての詳しい事は、
「システム奮闘記:その44」をご覧下さい。
 (バッファーオーバーフロー攻撃の仕組みと手口)


  さて、スタンドアローンで、ftpサーバーを起動するのは成功だと言える。
  だが、実際には、セキュリティーの問題やメモリの節約のために、
スタンドアローンではなく、inetd(xinetd)経由でサーバーを起動させる。

  渋々、英語のINSTALLファイルを読む事にした。
  inetd(xinetd)経由にする場合の設定が載っていた。
  そこで、xinetd経由にするため、/etc/xinit.d/ のディレクトリーに
以下のファイルを作成した。

/etc/xinit.d/ のディレクトリーに ftpというファイル名を作成
ftpのファイルの中身
service ftp
{
flags           = REUSE
socket_type     = stream
instances       = 50
wait            = no
user            = root
server          = /usr/local/sbin/in.proftpd

log_on_success  = HOST PID
log_on_failure  = HOST RECORD
}

  INSTALLファイルのマニュアルの丸写しだ (^^;;

  ちなみに、xinetdについては「システム奮闘記:その38」をご覧ください。

  次に、/etc/proftpd.confの設定変更だ。

/etc/proftpd.conf の設定
ServerName                      "ProFTPD Default Installation"
ServerType                     inetd
DefaultServer                   on
Port                            21
Umask                           022
MaxInstances                    30
User                            nobody
Group                           nobody
AllowOverwrite          on

<Limit SITE_CHMOD>
  DenyAll
</Limit>

<Anonymous ~ftp>
  User                          ftp
  Group                         ftp
  UserAlias                     anonymous ftp
  MaxClients                    10
  DisplayLogin                  welcome.msg
  DisplayFirstChdir             .message
  <Limit WRITE>
    DenyAll
  </Limit>
</Anonymous>
赤い部分は、inetd(xinetd)経由で、デーモンが起動する意味。

  ところで、上の赤い部分だが、inetdでも、xinetdでも、
設定は同じ「inetd」を入力する。
  この事は本やマニュアルではなく、ある失敗からわかったのだ。

  それは、最初、xinetdで立ち上げる設定をした時、「xinetd」を入れてみた。
  そして、何を勘違いをしたのか、

  proftpdのデーモンを立ち上げたのだ!!

  すると、エラーが出た。

エラーの内容
[root@xxx proftpd-1.2.10]# /usr/local/sbin/proftpd 
 - Fatal: ServerType: type must be either 'inetd' or 'standalone'. on line 8 of '/etc/proftpd.conf'
[root@sxxx proftpd-1.2.10]# 

  このエラーを見て、inetdを使わず、xinetdを使う場合でも、
設定ファイルには「inetd」を入力するのがわかった。

  しばらく、デーモンを立ち上げては、以下のエラーが出る。

エラーの内容
[root@xxx proftpd-1.2.10]# /usr/local/sbin/proftpd 
xxx.****.co.jp - fatal: Socket operation on non-socket
xxx.****.co.jp - (Running from command line? Use `ServerType standalone' in config file!)
[root@xxx proftpd-1.2.10]# 

  何度かエラーを見るうちに気づいたのだった。

  私っておバカ・・・  (^^;;

  xinetdをいれているのだから、接続があった時に、デーモンが立ち上げる。
  基本を忘れてしまっていたのだった (^^;;;

ftpの際のデータ通信について

さて、ftpサーバーの設定は完了したのだが、不満が残る。 一体、ftpコマンドには、何があって、マシン間同士の通信で、 どのようなやりとりがあるのか、全くわからないからだ。 さて、googleで調べてみた。 そして、実際に、実験を行う事にした。 接続先のマシンは、wu-ftpdサーバーなのだが、ftpコマンドは共通なので、 問題はないはず (^^)

Portモードでのftpの通信について

まずは、Portモード(Activeモード)での接続を試みた。
Portモード(Activeモード)での接続の様子
[suga@xxx suga]# telnet 192.168.X.Y ftp
Trying 192.168.X.Y...
Connected to 192.168.X.Y.
Escape character is '^]'.
220 ProFTPD 1.2.10 Server (ProFTPD Default Installation) [192.168.X.Y]
USER suga
331 Password required for suga.
PASS *****
230 User suga logged in.
port 192,168,X,Z,200,200
200 PORT command successful
青い部分はユーザーIDを入力。
赤い部分はパスワードの入力。

ピンクの部分は、データ通信を行うため設定だ。
入力するのは、接続元(クライアント)のIPとポート番号だ。
Portモードでは、クライアントのポート番号をサーバーに知らせるのだ。

  ピンクの部分に着目する。
  port 192,168,X,Z,200,200は、クライアント側のIPとポート番号だ。

  「192.168.X.Z」がIPアドレスなのは納得がいく。
  しかし、「200,200」がポート番号なのは腑に落ちない。

  「a,b」と記述するのは、簡単にポート番号がわからないようにするためらしい。
  ちなみに、この「a,b」からポート番号を求めるには以下の計算を行う。

  ポート番号 = a × 256 + b

  さて、クライアント側のポート番号の入力をするのだが、
なぜ、入力が必要かというと、Portモードの場合、データ転送の接続は
サーバーからクライアントへ接続確立を行うため、サーバー側は
クライアント側のポート番号を知る必要がある。

  つまり、以下の図のようなやりとりがあるのだ。

Portモードの場合の接続関係のやりとり
Portモードの場合のftpでの接続関係のやりとり
サーバーからクライアントへ接続確立を行うという、ちょっと奇妙な事な
やりとりを行っているのだ。

ちなみに、Portモードの場合、ファイヤウォールの問題があると言われる。
それは、例えば、ファイヤウォールの外にあるftpサーバーに接続した時、
外部のサーバは、内部のクライアントに対して、データ通信のための
接続確立を行うのだが、途中のファイヤウォールに引っかかる。
そのため、発信元ポートが「20」の場合のパケットは無条件で通過させる
必要が出てくるのだが、そこを狙った攻撃も考えられるので、
Portモードでのデータのやりとりはお薦めできません。

Portモードの補足(2006/5/3に追加)


  初版では「Portモードの話は、これぐらいにします」で逃げた。
  実は、自分の目でデータ転送の様子を見てみたかったのだが、
どうやって転送データを受けるのか、わからなかったのだ。

  何せ、クライアント側のポートを決めた形で、データの待ち受けを行う
ツールが思いつかなかったのだ。
  telnetだと相手先のポートは指定できても自分もポートまで
指定する事ができないからだ。

  だが、思った。
  転送データだけを待ち受けるプログラムを作って、クライアントに仕掛ければ
良いではないか。

  そこで、以下のプログラムを作る事にした。

Portモードでのデータ受信プログラム (getdata.c)
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>

int main(void)
{
  int r_sockfd , g_sockfd ;
  int r_len , g_len , result ,cport ;
  char filename[] ="getdata.dat";
  char buf[1] ;

  struct sockaddr_in r_address , g_address ;

  FILE *fp ;

  fp = fopen(filename,"w");

  cport = 51400 ;
  r_sockfd = socket(AF_INET,SOCK_STREAM,0);
  r_address.sin_family = AF_INET ;
  r_address.sin_addr.s_addr = INADDR_ANY ;
  r_address.sin_port = htons(cport) ;

  r_len = sizeof(r_address);

  bind(r_sockfd , (struct sockaddr *)&r_address , r_len);

  listen(r_sockfd , 5);

  g_sockfd = accept(r_sockfd ,
                   (struct sockaddr *)&g_address , &g_len);


  printf("Server Port = %d\n",g_address.sin_port);

  while ( read(g_sockfd , buf , 1 ) > 0 )
        {
        fwrite(buf,1,1,fp);
        }

  fclose(fp);
  close(r_sockfd);
  close(g_sockfd);
}
このプログラムだと、クライアント側のポートは「51400」のみ対応
しかも、取り込んだデータはファイル名「getdata.dat」に落とし込まれる。

  さて、実験してみる。
  プログラムを稼働させたまま、telnetを使ってftpサーバーに
ftpコマンドを送り、データの送信を行ってみる。

  もちろん、クライアント側のポートは「51400」にしておく。 

Portモード(Activeモード)での接続の様子
[suga@xxx suga]# telnet 192.168.X.Y ftp
Trying 192.168.X.Y...
Connected to 192.168.X.Y.
Escape character is '^]'.
220 ProFTPD 1.2.10 Server (ProFTPD Default Installation) [192.168.X.Y]
USER suga
331 Password required for suga.
PASS *****
230 User suga logged in.
port 192,168,X,Z,200,200
200 PORT command successful
RETR server.dat
150 Opening ASCII mode data connection for server.dat (22 bytes)
226 Transfer complete.
ピンクの部分は、データ通信を行うため設定だ。
入力するのは、接続元(クライアント)のIPとポート番号だ。
ちなみに、この時のクライアントのポートは、200×256+200 = 51400 だ。
Portモードでは、クライアントのポート番号をサーバーに知らせるのだ。

そして、青い部分は、サーバーにあるファイル「server.dat」を
クライアントへ転送するコマンドだ。

  さて、実際にデータが転送されたのか確認を行う。
  データ待ち受けプログラムでは、ファイル名「getdata.dat」に
転送されたデータが落とし込まれるため、そのファイルを見てみる。

ファイル名「getdata.dat」の中身を見てみる
[suga@xxx ftp]# cat getdata.dat 
From Server to Client
[suga@xxx ftp]# 

  実験成功!  (^^)V

 なのだ。

  ここで補足をを終了します。

Passiveモードでの通信について

さて、次に、Passiveモードでのやりとりを見てみる事にした。
Passiveモードでの接続の様子
[suga@xxx suga]# telnet 192.168.X.Y ftp
Trying 192.168.X.Y...
Connected to 192.168.X.Y.
Escape character is '^]'.
220 ProFTPD 1.2.10 Server (ProFTPD Default Installation) [192.168.X.Y]
USER suga
331 Password required for suga.
PASS *****
230 User suga logged in.
PASV 
227 Entering Passive Mode (192,168,X,Y,95,177)
青い部分はユーザーIDを入力。
赤い部分はパスワードの入力。

ピンクの部分は、PASVコマンドの入力。
すると、サーバーから返答が返ってくる。緑の部分だ。
サーバー側のIPと、ポート番号だ。

ここのポート番号の計算もPortモードの時と同様に
「ポート番号 = a × 256 + b」の方程式になる。
そのため上の場合だと、95×256+177=24497となる。

  そこで、サーバーから指定されたポート番号に接続してみる、

サーバーから指定されたポート番号に接続
[suga@mail suga]$ telnet 192.168.X.Y 24497
Trying 192.168.X.Y...
Connected to 192.168.X.Y.
Escape character is '^]'.

  問題なく接続できたようだ。データを待ち受けている様子だ。
  ちなみに、他のマシンから、このポート番号へtelnetを試みても
接続は拒否されるので、セキュリティーの心配を考える必要はない。


  さて、ここでデータ転送の前に、他のコマンドを打って見る事にした。

コマンド操作側の画面
LIST
150 Opening ASCII mode data connection for file list
226 Transfer complete.
LISTコマンドは、現在のディレクトリーの
ファイル等のリストを出力するコマンドだ。
つまり、「ls」コマンドと同じ物なのだ。

  すると、データ転送側の画面に結果が出力された

データ転送側の画面
-rw-r--r--   1 (?)      (?)         61440 Feb 10  2005 aaa.tar
-rw-r--r--   1 (?)      (?)        499653 Feb 10  2005 data.tar.gz
drwxrwxr-x   2 (?)      (?)          4096 Jun  2  2005 eiko
drwxrwxr-x   2 (?)      (?)          4096 Feb 24 07:21 ftp
-rw-r--r--   1 suga     suga          233 Mar 17 08:40 ftp.dat
-rw-r--r--   1 suga     suga         2168 Mar 17 08:40 ftpkan
drwxrwxr-x   2 suga     suga         4096 Mar 28 02:02 sc
-rw-r--r--   1 (?)      (?)             0 Jul  3  2005 win98-nolog-t.bmp
Connection closed by foreign host.

  ファイルの一覧が出たら、ピンクの文字の如く、接続が切られた。
  もしかして、データ転送を行う度に、毎回、データ転送のポートを
サーバーが指定しているのだろうか。
  そこで、もう一度、同じポートで接続を行ってみると案の定、
接続が拒否された。

接続が拒否される様子
[suga@xxx suga]$ telnet 192.168.X.Y 24497
Trying 192.168.X.Y...
telnet: connect to address 192.168.X.Y: Connection refused
[suga@xxx suga]$ 

  実験の結果、毎回、データ転送がある度に、ポート番号を変えているのだ。

  ここで、2つの事がわかった。

ここでわかった事
(1)
ファイルの転送だけでなく、コマンドの出力結果も
データ通信のポートでやりとりされる。

ただし、HELPコマンドの結果は、コマンド操作のポートで送られる。
(2)
データ通信は1回ごとに、接続が切られるため
ポートが変更される

  さて、他のftpコマンドを使って実験を行う事にする。
  まずは、以下のファイルを、サーバーからクライアントへ転送を行う事にする

転送を行うファイルの中身
[suga@server suga]$ cat server.dat 
From Server to Client
[suga@server suga]$ 

  さて、コマンド操作とデータ通信のポートに接続して、
コマンド操作の画面で、サーバーからファイルの転送コマンドを使ってみた。

コマンド操作の画面
span class="red">RETR server.dat
150 Opening ASCII mode data connection for server.dat (22 bytes)
226 Transfer complete.

 そしてデータ転送の画面。

データ転送の画面
[suga@xxx suga]$ telnet 192.168.X.Y 38792
Trying 192.168.X.Y...
Connected to 192.168.X.Y.
Escape character is '^]'.
From Server to Client
Connection closed by foreign host.
青い部分がファイル(server.dat)の中身になる。
サーバーからのデータ転送は成功している。

  うまくいったので、上機嫌の私は、次にクライアントからサーバーへ
ファイルの転送(アップロード)をやってみる事にした。


  もう一度、コマンド操作とデータ転送のポートに接続を行う。

コマンド操作の画面
span class="red">STOR client.dat
150 Opening ASCII mode data connection for client.dat

  さて、データ転送の画面を見てみると

データ転送の画面
[suga@xxx suga]$ telnet 192.168.X.Y 39064
Trying 192.168.X.Y...
Connected to 192.168.X.Y.
Escape character is '^]'.

  停止した感じだ (--;;

  適当に、文字を入力してみた。

データ転送の画面
[suga@xxx suga]$ telnet 192.168.X.Y 39064
Trying 192.168.X.Y...
Connected to 192.168.X.Y.
Escape character is '^]'.
TEST
AAAA
Client to Server!
.


  何も変化があらへん (--;;

  ニッチモサッチもいかないので、killコマンドでプロセスを停止させた。
  そして、サーバー側を見てみると、client.datファイルができている。
  中身を見てみると・・・

client.datファイルの中身
[suga@server suga]$ cat client.dat 
TEST
AAAA
Client to Server!
.
[suga@server suga]$ 


  データ転送の画面で、私が適当に入力した物が、client.datファイルの
中身として転送されている。

  つまり、コマンド操作の画面で指定したファイルは、あくまでファイルの
指定だけであって、データ転送の画面で、telnetで接続した後は、
データを書き込む必要があるという事だ。


  これらの一連の流れを、ftpのクライアントソフトを使って行う。
  クライアントソフトについては、今回は割愛しまーす。
  「調べろ!」という声があるかと思いますが、堂々と反論します。

  だって、量が膨大になるもーん (^^)

  何せ、システム奮闘記の連載を始めた頃は、知らない部分を突ついても、
あまり掘り出せなかったので、量は多くならなかったのだが、
連載を重ねるうちに、調べるだけの知識がついてきた事もあって、
どんどん膨れ上げるという状態です。
  というわけで、膨張して弾ける前に、止めておきまーす (^^)


ftpのデータ通信をまとめてみる

だいたい、ftpの通信で、どんなやりとりがあるのかが見えてきた。 ふと気づく。 そういえば、私がftpの通信について誤解していた理由が見えてきたのだ。 最初の方に触れました通り、以下の誤解をしていたのだ。
ftpの誤った認識 (Portモード)
ftpの誤った認識 (Portモード)
ftpの誤った認識 (Passiveモード)
ftpの誤った認識 (Passiveモード)
データ通信を行う時のポートだが、サーバー側のポートなのに
私はクライアント側が開いたポートだと誤解をした。

  このような誤解をしていたのは、次のような勘違いが原因だと思う。
  まずは、Portモードの誤解から。

正しい接続の方法(Portモード)
正しい接続の方法(Portモード)
誤解した原因(Portモード)
誤解した原因(Portモード)

  次に、Passiveモードの誤解。

正しい接続の方法(Passiveモード)
正しい接続の方法(Passiveモード)
誤解した原因(Passiveモード)
誤解した原因(Passiveモード)

  どうやら、2003年の初めに、ftpの通信の勉強した時に、
上のような誤解をしていたのが原因だと思う。


ftpでのバイナリとテキストデータの送受信について

さて、最初の方に触れましたが、ftpを使ってのデータ転送の際、 UNIXとWindowsの間でバイナリデータのやりとりがある場合には バイナリデータである事を示すbinコマンドを打つ必要があった。
UNIXとWindowsの間でバイナリデータのやりとりの場合
ftp> bin
200 Type set to I
ftp>

  一体、なぜ、そんな事を行う必要があるのか。
  ftpと出会って10年経つが、全く知らなかったので、調べてみる事にした。

  すると、なるほどと思う事がわかった。
  よく知られている話として、UNIXとWindowsでは改行文字が異なる問題がある。

UNIXとWindowsとの改行文字が違い
UNIX <LF>
Windows <CR><LF>

  そのため、UNIXで編集したテキストデータを、そのままWindowsへ持っていくと
改行されずに表示される問題が起こる。

  ftpの場合、デフォルトではテキストデータの転送になっている。
  UNIXとWindowsとのテキストデータの転送の際、改行文字を自動的に
変換する仕掛けがあるという。
  だが、バイナリデーターを転送する場合、この自動変換が悪さをするため、
自動変換を解除するために、binコマンドを使うという。

  なるほど、そうだったのか!

  と納得した。  

  しかし、実際に自分の目で見て確かめたい。
  そこで、以下のファイルをLinux上に置いて、Windowsへダウンロードさせ
改行文字が変換されているかどうか確かめる事にした。

Linux上に置いたファイルの中身
[suga@xxx suga]$ cat ftp-test.dat
q
qq
qqq
qqqq
qqqqq
qqqqqq
qqqqqqq
[suga@xxx suga]$

  まずは、通常通り、テキストデータという事で、ftpを使って
Windowsパソコンにダウンロードした。
  そして、メモ帳からファイルの中身を見てみる事にした。

メモ帳からファイルの中身を見てみる
メモ帳を使ってlinuxから送られてきたテキストファイルの中身を見てみる
問題なくファイルの中身を見る事ができた。

  さて、次にbinコマンドを使って転送を行ってみる。
  もし、改行文字に変換がなければ、UNIX上で作成したテキストファイルは
Windows上では改行されずに表示される問題が起こる。

メモ帳からファイルの中身を見てみる
メモ帳を使ってlinuxから送られてきたテキストファイルの中身を見てみる
改行されずに表示された。

  binコマンドによって、改行文字の自動変換を
解除している様子を見る事ができた。
  つまり、バイナリデータの場合、改行文字の変換を行うと、
本来、変換すべきでない物を変換してしまう可能性があるため、
変換機能を解除するのだ。

  10年間の謎が解けてご満悦の私 (^^)V


proftpに付属しているコマンド

ftpサーバー側で、何人ftpで接続しているのか、そのユーザーIDで 接続しているのか知る事ができる。 それが、ftpcount、ftpwhoコマンドだったりする。 まずは、ftpcountコマンドを使ってみる事にした。
ftpcountコマンドを使ってみる
ftpで接続ユーザーがいない場合
[suga@server suga]$ ftpcount
inetd FTP connections:
0 users
[suga@server suga]$
ftpで接続ユーザーが1人いる場合
[suga@server suga]$ ftpcount
inetd FTP connections:
Service class                      -   1 user
[suga@server suga]$ 

  ftpの利用ユーザー数が表示される。
  これは、proftpサーバーだけでなく、wu-ftpdでも同じコマンドがあり、
同じ事ができる。

  こんなコマンドがあった事自体、知らなかった  (^^;;


  次に、ftpwhoコマンドを使ってみる。

ftpwhoコマンドの結果
[suga@server suga]$ ftpwho
inetd FTP daemon:
14654 suga     [ 2m46s]   0m6s idle
Service class                      -   1 user
[suga@server suga]$ 

  もっと情報が欲しい場合には、「-v」オプションをつける。

ftpwhoコマンドに「-v」オプションを付けたの結果
[suga@server suga]$ ftpwho -v
inetd FTP daemon:
14654 suga     [ 4m21s]  0m17s idle
        client: xxx.******.co.jp [192.168.X.Z]
        server: 0.0.0.0:21 (ProFTPD Default Installation)
        location: /home/suga

Service class                      -   1 user
[suga@server suga]$ 

  ftpで接続したユーザー情報が得られる。
  ちなみにこのコマンドも、wu-ftpdでも使えるのだが・・・

  コマンドがある事自体、知らなかった  (^^;;

  なのだ。


ランタイム・スコアファイルとは

最後になりましたが、Proftpのインストール時に、ランタイム・スコアファイル と呼ばれるproftpd.scoreboardファイルが出てきたのだが、 インストール時には「わかりませーん」で放置していた。 そこで、一体、どんな物か気になるので、調べてみる事にした。 ネットで検索すると、次の事を書いているサイトが結構あった。
ネットに書かれている内容
もし、Proftpが動かない場合で、/var/log/messageのログファイルに
「error opening scoreboard: No such file or directory」
というエラーが出た場合、次のファイルを作成する。

/var/run/proftpd/proftpd.scoreboard

  しかし、何のためのファイルか、調べても、わからない。
  インストールする時の、INSTALLファイルの説明が書いてあった。
  渋々、英語を読み続ける・・・  

INSTALLファイルの中身 (一部抜粋)
10. Create the runtime state directory.
    In order for the MaxClients and MaxClientsPerHost directives and the
    ftpwho and ftpcount utilities to work, proftpd must have a scoreboard
    file.  The default is '/usr/local/var/proftpd/proftpd.scoreboard', though
    it may have been changed in the configuration process in Step 1.
    The default location also can be overriden at run-time by using the
    ScoreboardFile directive in the proftpd.conf configuration file.
    If the indicated file does not exist, it will be created when the daemon
    starts up.  If you have installed from an installation package, the
    installation scripts may have created the default directory. 
上の事を要約すると次の通りになる。

最大接続ユーザー数に関する事や、ftpwho、ftpcountのコマンドを
動かすのに必要なファイル。
  デフォルト設定では、/usr/local/var/proftpd/proftpd.scoreboardの
ディレクトリに置かれる。
  もし、このファイルがない場合でも、デーモンの起動時に
proftpd.scoreboardファイルが作成される。

  語学が得意でない私なので、訳しにくい所は、ゴマ化して逃げるのら (^^)V

  でも、だいたいの意味はわかる。

  さて、コンパイルの話でも触れましたが、configureを行った時、
次のオプションを付けた。

つけたオプション
--localstatedir=/var

  なので、proftpd.scoreboardファイルは、/var/proftpdのディレクトリにある。

  さて、このファイルの中身を見れば、どんなファイルか推測ができる。
  ただ、バイナリなので、catコマンドの時に「-v」オプションをつける。

  そして、他のホストからftpで接続がある時に、このファイルの中身を
見てみる事にした。

ファイルの中身
[suga@server proftpd]$ cat -v proftpd.scoreboard 
M-oM->M--M-^^B^@^D^AM-,4^@^@M-?n^ZDM-I^O^@^@c^@^@^@M-;^K
^@^@suga^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^U^@^@^@0.0.0.0:21^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
ProFTPD Default Installation^@^@^@^@192.168.X.Y^@^@^@^@
xxx.*********.co.jp^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^
@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@/home/suga^@^@^@^@^@^@^@^
@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@idle^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^^M-n
[suga@server proftpd]$
赤い部分がログインID。キチンと私のIDの「suga」になっている。
青い部分は、接続元(クライアント)のアドレス。
ピンクは、接続元(クライアント)のドメイン名。
緑は、ログイン中のユーザーがいる現在のディレクトリ

  ftpを使っているユーザー情報だというのが読み取れる。
  そのため、ftpwho、ftpcountコマンドを使う際、このファイルが
必要なのがわかる。

  さて、この中のユーザー情報は、4点だけなのだろうか。
  気になる。気になる。調べる方法がないかと考えていると、ふと思った。

  ソースを読めば、ええやん (^^)

  オープンソースのメリットは、ソースが自由に読める事なのだ。
  そこで、proftpのソースを展開したディレクトリに移ってみる事にした。

  そして、include/scoreboard.h のヘッダーファイルを見てみる。
  ヘッダーファイルには、データ構造が定義されている事が多いため、
データ構造を知るには、ヘッダーファイルを見るのが手っ取り早い。

  すると、以下のデータ構造になっていた。

ファイルの頭につけるデータ構造
typedef struct {

  /* Always 0xDEADBEEF */
  unsigned long sch_magic;

  /* Version of proftpd that created the scoreboard file */
  unsigned long sch_version;

  /* PID of the process to which this scoreboard belongs,
  or zero if inetd */
  pid_t sch_pid;

  /* Time when the daemon wrote this header */
  time_t sch_uptime;

} pr_scoreboard_header_t;
ユーザー情報に関するデータ構造
typedef struct {
  pid_t sce_pid;
  uid_t sce_uid;
  gid_t sce_gid;
  char sce_user[32];

  int sce_server_port;
  char sce_server_addr[80], sce_server_label[32];

#ifdef USE_IPV6
  char sce_client_addr[INET6_ADDRSTRLEN];
#else
  char sce_client_addr[INET_ADDRSTRLEN];
#endif /* USE_IPV6 */
  char sce_client_name[PR_TUNABLE_SCOREBOARD_BUFFER_SIZE];

  char sce_class[32];
  char sce_cwd[PR_TUNABLE_SCOREBOARD_BUFFER_SIZE];

  char sce_cmd[5];
  char sce_cmd_arg[PR_TUNABLE_SCOREBOARD_BUFFER_SIZE];

  time_t sce_begin_idle, sce_begin_session;

  off_t sce_xfer_size, sce_xfer_done, sce_xfer_len;
  unsigned long sce_xfer_elapsed;

} pr_scoreboard_entry_t;

  ヘッダーと本体の2つに別れているようだ。
  つまり図にすると、次のようになる。

ファイルに記録されるデータの状態
ファイルに記録されるデータの状態

  これだけ、分かれば、proftpd.scoreboardファイルの中身を
見るためのプログラムは可能だ。
  というわけで以下のソースを書いてみた。

proftpd.scoreboardファイルの中身を見るためのソース
ヘッダーファイル (head.h)
#define PR_TUNABLE_SCOREBOARD_BUFFER_SIZE 80
define INET_ADDRSTRLEN        16

struct sc_header {

  /* Always 0xDEADBEEF */
  unsigned long sch_magic;

  /* Version of proftpd that created the scoreboard file */
  unsigned long sch_version;

  /* PID of the process to which this scoreboard belongs, 
  or zero if inetd */
  pid_t sch_pid;

  /* Time when the daemon wrote this header */
  time_t sch_uptime;
};

struct sc_entry {
  pid_t sce_pid;
  uid_t sce_uid;
  gid_t sce_gid;
  char sce_user[32];

  int sce_server_port;
  char sce_server_addr[80], sce_server_label[32];


  char sce_client_addr[INET_ADDRSTRLEN];
  char sce_client_name[PR_TUNABLE_SCOREBOARD_BUFFER_SIZE];

  char sce_class[32];
  char sce_cwd[PR_TUNABLE_SCOREBOARD_BUFFER_SIZE];

  char sce_cmd[5];
  char sce_cmd_arg[PR_TUNABLE_SCOREBOARD_BUFFER_SIZE];

  time_t sce_begin_idle, sce_begin_session;

  off_t sce_xfer_size, sce_xfer_done, sce_xfer_len;
  unsigned long sce_xfer_elapsed;
};
ソース本体 (scr1.c)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "head.h"
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
  int shsize , sesize , res , i ;
  char name[100] ; 
  struct sc_header shead ;
  struct sc_entry  sentry ;

  res = open(name,O_RDONLY);

  if ( res < 0  )
    {
      fprintf(stderr,"Cannot open %s \n",name);
      exit(1);
    }

  read(res,&shead,sizeof(struct sc_header));

  /*  ヘッダーの値の表示  */
  printf("%d\n",(int)shead.sch_magic);
  printf("%d\n",(int)shead.sch_version);

  /*  情報の本体の表示  */
  read(res,&sentry,sizeof(struct sc_entry));
  printf("pid = %d\n",(int)sentry.sce_pid);
  printf("uid = %d\n",(int)sentry.sce_uid);
  printf("gid = %d\n",(int)sentry.sce_gid);
  printf("name = %s\n",sentry.sce_user);
  printf("server port = %d\n",sentry.sce_server_port);
  printf("server addr = %s\n",sentry.sce_server_addr);
  printf("server label =%s\n",sentry.sce_server_label);
  printf("client addr = %s\n",sentry.sce_client_addr);
  printf("%s\n",sentry.sce_class);
  printf("%s\n",sentry.sce_cwd);
  printf("%s\n",sentry.sce_cmd);
  printf("%s\n",sentry.sce_cmd_arg);
  printf("client name = %s\n",sentry.sce_client_name);

  close(res);

  return(0);
}

  そして、プログラムを実行してみる。
  結果は以下の通りになった。

プログラムの実行結果
-559038737
17039362
pid = 17801
uid = 99
gid = 3003
name = suga
server port = 21
server addr = 0.0.0.0:21
server label = ProFTPD Default Installation
client addr = 192.168.X.Z

/home/suga
idle

client name = xxx.******.co.jp

  ログイン情報を抽出する事ができた。
  この情報があるからこそ、ftpwho、ftpcountコマンドが動くのだ。

  やはりソースを読む事は、ソフトの仕組みを知る上で重要だと思う。
  まぁ、たまたま私のレベルでも読める範囲だったので・・・

  上記のように、エラソーに書けたのだ (^^)V

  もちろん、難しい事を聞かれると「事務員だもーん!」と言って逃げる。


最後に ftpはよく使うソフトの一つなのだが、あまりにも知らない事が発覚。 そこで、今回はftpを取り上げる事にしました。  ftpに関して突っ込んだ話は次章のftpクライアントソフト作成の話で 紹介しています。

次章:「ftpクライアントソフト作成の話」を読む
前章:「LinuxのPAM認証の設定入門」を読む
目次:システム奮闘記に戻る

Tweet