OpenSSLライブラリの「信頼する認証局の証明書」ファイルの場所はどうやって決まるのか

投稿者: | ↻ : 2019年2月25日

はじめに

OpenSSLライブラリは、SSL/TLS通信のサーバー認証で利用する「信頼する認証局の証明書」情報を通常ファイルから取得しています。

「信頼する認証局の証明書」ファイル(以降、CA証明書ファイル)の場所は、2段階で決定します。

OpenSSLライブラリのビルド時にデフォルトパスを設定し、OpenSSLライブラリを利用するプログラムでデフォルトパスを使うか、デフォルト以外のパスを指定します。

検証環境

  • macOS Sierra 10.12.6
  • OpenSSLライブラリソースファイル (2018/8/29最新) 1.1.1-pre10-dev
    github – openssl

OpenSSLライブラリ側

デフォルトパス

CA証明書ファイルのデフォルト位置は、OpenSSLライブラリのビルド時にオプションで設定します。opensslコマンドのversionサブコマンドによりビルド済みのOpenSSLライブラリの設定を確認できます。

$ openssl version -d
OPENSSLDIR: "/usr/local/etc/openssl"

ビルド時に設定できるのはCA証明書ファイルが配置されるディレクトリだけで、ファイル名はハードコーディングされていて、“cert.pem”になります。

実際に試すには、Githubからopensslのソースファイルを取得してMakefileを作成するためのconfigスクリプトに--openssldirオプションを指定します。

$ git clone https://github.com/openssl/openssl
$ cd openssl
$ ./config --openssldir=/foo/bar

macOS上でconfigスクリプトを実行すると、共有ライブラリの依存関係の埋め込みを行うためのオプションがデフォルトで指定されます。しかし、linux上でconfigスクリプトを実行すると、このオプションがデフォルトで指定されないため明示的に指定します。
「./config –openssldir=/foo/bar ‘-Wl,-rpath,$(LIBRPATH)’」

環境変数LD_LIBRARYやldconfigを利用する方法では、既存の環境に影響を与えてしまうので避けた方がよさそうです。

環境変数(SSL_CERT_FILE)により実行時に設定

環境変数SSL_CERT_FILEに、CA証明書ファイルのパスを設定すれば実行時にデフォルト位置を上書きして変更できます。この環境変数にはディレクトリではなくファイルのパスを設定するので任意のファイル名を設定できます。 OpenSSLライブラリでは、こちらもデフォルトパスとして扱われています。

OpenSSLライブラリを利用するプログラム側

デフォルトパスを利用する場合

デフォルトパスを利用して、CA証明書情報を読み込むためのAPIとしてSSL_CTX_set_default_verify_fileまたは、SSL_CTX_set_default_verify_pathsがあります。

#include <openssl/ssl.h>
int SSL_CTX_set_default_verify_file(SSL_CTX *ctx);
int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx);

SSL_CTX_set_default_verify_fileはデフォルトパスにあるCA証明書ファイルから情報を読み込みます。

ビルド時の設定より、環境変数SSL_CERT_FILEの設定が優先されます。しかし、環境変数に設定されたファイルでエラーが発生しても、ビルド時の設定に切り替わることはありません。

環境変数が設定されているか否かで切り替わる処理となっています。./crypto/x509/by_file.cby_file_ctrl関数が該当の処理になります。

static int by_file_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp,
                        long argl, char **ret)
{
    int ok = 0;
    const char *file;

    switch (cmd) {
    case X509_L_FILE_LOAD:
        if (argl == X509_FILETYPE_DEFAULT) {
            file = getenv(X509_get_default_cert_file_env());
            if (file)
                ok = (X509_load_cert_crl_file(ctx, file,
                                              X509_FILETYPE_PEM) != 0);

            else
                ok = (X509_load_cert_crl_file
                      (ctx, X509_get_default_cert_file(),
                       X509_FILETYPE_PEM) != 0);

            if (!ok) {
                X509err(X509_F_BY_FILE_CTRL, X509_R_LOADING_DEFAULTS);
            }
... 省略

デフォルト以外のパスを指定して利用する場合

CA証明書ファイルのパスを指定して、CA証明書情報を読み込むためのAPIとしてSSL_CTX_load_verify_locationsがあります。

#include <openssl/ssl.h>
int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath);

詳細はmanコマンドで確認できます。第二引数CAfileにCA証明書ファイルのパスを指定した場合、そのファイルからCA証明書情報を読み込みます。第三引数のCApathについては対応する使い方が少々特殊(利点が分からない)なので今回は無視します。

第二引数CAfile、第三引数CApathの両方ともにNULLを指定した場合はエラーとなります。自動的にデフォルトパスの利用とはなりません。

デフォルトパスと、指定したパスの違い

デフォルトパスにファイルが無い場合や、読み込みに失敗したしてもエラーとなりません。内部的にはエラーになっていますが、SSL_CTX_set_default_verify_fileSSL_CTX_set_default_verify_pathsでCA証明書ファイルに対するエラーを揉み消しています。これはmanコマンドのドキュメントや、ソースコードのコメントに記載されています。

SSL_CTX_set_default_verify_fileが記載されているドキュメントでないと、情報が古いためGithub上のソースファイルを参照する必要があります。

まとめ

OpenSSLライブラリを利用しているCurlやGitなどのコマンドで、SSL/TLSに関連する設定や環境変数が、どこに影響するのかが不明で障害時の対応に手間取ったためソースファイルから該当の処理を探りました。

今回の調査で環境変数SSL_CERT_FILE(およびSSL_CERT_DIR)がOpenSSLライブラリの機能で利用されるものだと分かりました。なので、OpenSSLライブラリを利用したプログラムは全て、この機能の影響を受けるとになります。

異なるパスに配置されたOpenSSLライブラリを参照していても、環境変数は全てに影響してしまうので使う範囲に注意が必要な機能です。

ここからOpenSSLライブラリを利用しているCurlの実装を追っていきます。