2. メーリングリスト・サーバーの構造分析

一般的なメーリングリスト・サーバーにおいて求められる主な機能は、以下の ようなものである。

fml では、上記のような機能を Perl 言語により実装している。fml の詳細仕様に ついては fml 公式 web サイト にある ドキュメント類を参照されたい。

ここでは調査対象とした fml-3.0pl8 (以下、「現行 fml」と称する) の実装について、 特に本開発に関連が大きい部分について報告する。

2.1 現行 fml の動作仕様

現行 fml は、上記に挙げた一般的なメーリングリスト・サーバーの機能を持つと 同時に、以下のような特徴を持っている。

  1. 送信を受け付けるメンバーと、配信対象のメンバーを別個に管理できる。
  2. メーリングリスト管理者からのメールにより、リモートでメンバー管理が できる。
  3. PGP による暗号化メールの送受信、およびメール送信者の認証を行なう 機能を持っている。

上記各機能について、以下に詳しく説明する。

2.1.1 送受信に関するメンバーの独立管理について

現行 fml では登録メンバーについて、以下の2つのデーターベースを持っている。

actives
現行 fml では、許可されたメンバー以外からのメールがメーリングリスト宛に送られて きた場合の動作について、以下の動作を選択できる。
  1. メーリングリストの登録メンバーにそのまま配信する。
  2. メンバーにはメールを配信せず、管理者宛にメールの配信があったことを 通知する。

この動作において、メールの送付を許可されたメンバーのメールアドレスが記述 されているデーターベースが actives である。

members
メーリングリスト・サーバーがメールを受信した後に、配送対象とするメール アドレスが記述されているデーターベースが members である。

actives と members が別個に管理されることにより、複数の投稿拠点を持ちながら、 メールは特定の一箇所でしか読まないようなメンバーのサポートが可能となっている。

これらのデーターベースは、メールアドレスを列挙した単なるリストに過ぎないので、 直接メーリングリスト・サーバーが管理するのではなく、ディレクトリ・サーバー などを用いてより安全に管理し、メーリングリスト・サーバーの機能から分離する ことが可能であると考えられる。

2.1.2 メーリングリストのリモート管理

メーリングリスト管理者が、メーリングリスト・サーバーの運用されているホストに 直接ログインして作業を行なえないような場合が考えられるが、このようなケースに 対応するために、現行 fml ではメーリングリストのリモート管理を行なうための機能 がサポートされている。

現行 fml の各種データーベースは、メーリングリストを運用するホスト上にある プレーンテキストファイルによって管理されている。したがって、fml のリモート 管理機構では、マシンに直接ログインすることなく、各種データーベースファイル の書き換えを行なうことになり、潜在的にセキュリティ的な危険を含んでいる。

リモート管理のためのコマンドを送る手段も、管理用パスワードを含んだ電子メール の送信であるため、この点についてもセキュリティ的な危険を含んでいる。

管理用パスワードを含んだメールを送信する危険を回避するため、現行 fml では PGP を用いた電子署名による認証機能、およびコマンドメールの暗号化について もサポートしている。

2.1.3 PGP による暗号化および認証機能のサポート

現行 fml では、既に PGP による送受信メールの暗号化、および電子署名による メール発信者の認証機能が実装されている。

しかし、 「本開発のアプローチについて」 に おいて述べた通り、PGP では、送信文の復号化および送信者の認証に使用される 公開鍵は、メール送信者によってのみ保証される。そのため身元の保証について、 機能的に不充分な点がある。

本開発の要求する機能レベルを実現するためには、暗号化および認証について、 他の適当な実装に置き換える必要があると考えられる。

2.2 PGP を使用した暗号化/認証機能部分のソース解析

以下に、現行 fml に対して本開発に必要な機能追加を行なうにあたって、 現行 fml のソースコード中で特に重要と思われる部分について、抜粋して コメントを添付する。

現行 fml では、PGP による暗号化機能を使用するかどうかを制御するための パラメーターとして、以下の変数を、各メーリングリスト用の設定ファイル (config.pl) に設定することができる

[付属ドキュメント doc/op.jp より]
27.2 PGPを使って暗号化したメーリングリストにする
$USE_ENCRYPTED_DISTRIBUTION (default 0)

< libdist.pl : 配送ルーチン > 35行目 : $ENCRYPTED_DISTRIBUTION_TYPE eq 'pgp'

  1. 署名の検証
    libdist.pl 39 行目
    # check PGP signature
    if (&PGPGoodSignatureP(*e, 1)) {
    libpgp.pl 22 行目
    &PgpInit(*e) || return 0;

    Bodyのチェック 実行ファイルの存在確認

    libpgp.pl 26 行目
    &open2(RPGP, WPGP, "$PGP $PgpOpts -f 2>&1") || &Log("PGP: $!");

    RPGP : PGPコマンドからの標準出力
    WPGP : PGPコマンドへの標準入力
    -f : PGPコマンドをフィルタとして起動
    2>&1 : 標準エラー出力を標準出力へリダイレクト

  2. 復号化
    libdist.pl 41 行目
    &PGPDecode(*e);
    libpgp.pl 87 行目
    $_ = &DoPGPDecode($pgp_buf);
    libpgp.pl 155 行目
    # 2>&1 is required to detect "Good signature"
    &open2(RPGP, WPGP, "$PGP $PgpOpts -f 2>/dev/null")||&Log("PGPDecode: $!");
  3. 暗号化(署名つき)
    libdist.pl 42 行目
    &PGPEncode(*e);
    libpgp.pl 219 行目
    $e{'Body'} = &DoPGPEncode($e{'pgp:buf'});
    libpgp.pl 184 行目
    # pgp scan to find myself,
    # so PLEASE ATTENTION "DO NOT SEND IT TO MYSELF";
    $count = &PgpScan(*e, *whom) || 0;

    $whom : 配送メンバリスト(キーリング中の全てのユーザID)

    libpgp.pl 229 行目
    open(RPGP, "$PGP $PgpOpts -kv 2>&1|") || &Log("PGP: $!");

    -kv : 鍵ホルダーの中身の表示

    libpgp.pl 191 行目
    &open2(RPGP, WPGP, "$PGP $PgpOpts -f -sea $whom 2>$tmpbuf") ||

    -sea : 署名付きでASCII形式に暗号化
    $whom : 配送メンバリスト(キーリング中の全てのユーザID)
    $tmpbuf : 署名付きでASCII形式に暗号化したときのメッセージ

  4. %Envelope
    $e{'pgp:buf'} : 復号化されたメール
    $e{'OriginalBody'} : 受信した暗号メール
    $e{'Body'} : 送信用に暗号化されたメール
    $e{'Header'} : オリジナル ヘッダ
    $e{'Hdr'} : 配送するメールのヘッダ
  5. 受信したメールの保存
    受信したメールの保存時には、以下のような処理を行なっている。
    1. PGPでの署名検証、復号、暗号処理
      libdist.pl, L.39, &DoDistribute
    2. set and save ID、現在時刻の設定
    3. サマリーの保存とログの出力
    4. ヘッダの調整と設定
    5. ヘッダの生成
    6. スプール処理
      libdist.pl, L.290, &DoDistribute
      libkernsubr.pl L.106, &__Write3
      $e{'Hdr'}, $e{'Body'}をスプール
    7. SMTPによる配送
  6. PGPでの暗号化MLに関する過去記事の取り寄せ

    現行 fml で PGP による暗号化機能を使用した場合には、メンバーにメールを 配送する際に暗号化を行ない、その結果をディスク上に保存する。

    保存したメールを再送信するように要求した場合には、上記のように保存した メールをそのまま送信する。

    したがって、メールを保存した時点でまだメーリングリストのメンバーでは なく後から登録されたメンバーについては、再送信を要求して送られてきた メールには自分の公開鍵で暗号化された秘密鍵が添付されていないので、 復号化することができないという問題がある。

    1. getの処理
      libfml.pl 33 行目
      &DoProcedure : fmlコマンドルーチン
      libfml.pl 362 行目
      &InitProcedure : コマンド関数定義
      %proc コマンド、プロシージャ名のハッシュ $proc{'get'} = 'ProcRetrieveFileInSpool';
      最終的には、%Proceduer = %proc;
      libfml.pl 756 行目
      &ProcRetrieveFileInSpool
      libfml.pl 274 行目
      実際のコマンドに対応する関数の実行
      $status = &$proc($xbuf, *Fld, *e, *misc);

      $xbuf : コマンド(修正したもの)
      @Fld : コマンド(オリジナル)
      *misc : 関数からの出力の受渡し用

      libfml.pl 773 行目
      &ResentForwFileInSpool($proc, *Fld, *e, *misc, *cat, $ar, $mail_file);
      $ar : 配送される過去記事の形式
      $mail_file : スプール中の過去記事のファイル名
      liblop.pl 257 行目
      過去記事を配送
      &Sendmail($e{'Addr2Reply:'}, "", $body);
  7. 暗号化メーリングリストのためのパラメータ

    現在のfmlでは以下のようになっている

    • 暗号化を行うかどうかのフラグ
      $USE_ENCRYPTED_DISTRIBUTION
      0 : 暗号化しない
      0以外 : 暗号化を行う
    • 暗号化のタイプ
      $ENCRYPTED_DISTRIBUTION_TYPE
      "pgp" : PGPによる暗号化

    上記以外の暗号化タイプは、現在未定義。S/MIME による暗号化に対応 させる場合、以下のような変更が必要である。

    • 設定可能な暗号化タイプの追加
      $ENCRYPTED_DISTRIBUTION_TYPE
      "smime" : S/MIMEによる暗号化
    • S/MIMEでの送信時のコンテント暗号化アルゴリズム指定変数の追加
      $SMIME_CONTENT_ENC_ALGO
      "rc2/40" : RC2鍵長40bit (デフォルト)
      "3des" : トリプル DES
    • S/MIMEでの送信時のダイジェストアルゴリズム指定変数の追加
      $SMIME_DIGEST_ALGO
      "sha-1" : SHA-1 (デフォルト)
      "md5" : MD5
  8. E-maiアドレスの正規化

    RFC882 において、メールヘッダーのFromフィールド内のE-mailアドレスの表記 には様々な表記方法が許されるが、fml では active データーベースおよび members データーベースに記述されているメールアドレスとの照合を行なう ために、様々なメールアドレスの表記の正規化を行なっている。

    正規化の例)
    From: foo
    From: foo@bar.co.jp (foo)

    上記アドレスは、いずれもfoo@bar.co.jpとして正規化される。 ただし、foo bar <(foo)foo@bar.co.jp>形式には未対応である。

    fml.pl, 1231 行目, sub Conv2mailbox
    # Hayakawa Aoi
    if ($mb =~ /^\s*(.*)\s*.*$/io) { $e{'tmp:x'} = $1; return $2;}
    # Aoi@aoi.chan.panic (Chacha Mocha no cha nu-to no 1)
    if ($mb =~ /^\s*(\S+)\s*\((.*)\)$/io || $mb =~ /^\s*(\S+)\s*(.*)$/io) {
    $e{'tmp:x'} = $2, return $1;

    }

  9. メンバーリストのチェック

    メンバーリストに特定のアドレスが含まれているかどうかをチェックする部分 については、以下のようにサブルーチン化されている。

    fml.pl, 1583 行目, sub Lookup

2.3 MIME デコード部分のソース解析

一般的にメーリングリスト・サーバーにおいては、メールの送受信に最して、 メールの受信、および受信したメールをそのまま登録メンバーに再送信する 機能が実装されていることが必要十分であって、MIME エンコーディングされた メールの MIME 的な解釈は行なう必要がない。

ただし現行 fml では、以下に挙げるような機能をサポートするために、 メール本文の MIME 的な解釈をサポートしている。

  1. メール・アーカイブの HTML 化
    現行 fml では、メンバーから投稿されたメールをディスク上に 保存し、またそれらのメールを HTML に変換して、web サーバ 上から保存したメールを参照できるようにする機能をサポート している。

    たとえば Subject: が base64 エンコードされている場合、可読性 の点で、HTML に変換する際にはデコードされていることが望ましい。

    あるいは、画像が添付されたメールを受信した場合、 base64 エンコードされた画像をメール本文から取り出し、その画像 への適切なリンクを生成する必要がある。

    このような機能をサポートするために、現行の fml では base64 デコーディング機能が実装されている。

  2. 二重コンテンツメールの正規化
    一部のメール送信エージェントでは、プレーンテキストを送信 したにも関わらず、本文とまったく同一の内容を HTML に変換 して送信することがある。

    現行 fml では、このようなメールの受信を拒否したり、余分な HTML 部分を自動的に削除する機能が実装されている。

S/MIME 機能を実装する場合、base64 デコーディング機能はそのまま流用する ことが可能である。 また、MIME Content-type の判別部分にハンドラを追加することにより、 現在サポートされている HTML メール以外に S/MIME メールの判別について 実装することができる。