PHPのcurlでCURLINFO_CERTINFOが取れなくて困った話

# 端緒
某CMSで下記画面のような警告メッセージが表示された。サーバ証明書は自己署名ではないし証明書チェーンも問題ないはず、そもそも[Qualys SSL Labs SSL Server Test](https://www.ssllabs.com/ssltest/analyze.html)でA+判定を貰っているのだからおおむね設定に問題はない。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/63695/43470dcc-8d74-1f11-3034-abaa54f1b769.png)

# 調査
## PHPのテストコードで試して見る
CMSの海外フォーラムを検索してみると同様の問題が報告されているが、原因不明のまま…。とりあえずCMSのコードを調査してみるが問題はなさそうに見える。php-curlに問題があるのではと見当をつけて下記のテスト用コードを実行してみる。

```php:curltest.php
<?php
$curl = curl_init('https://www.google.com/');

curl_setopt($curl, CURLOPT_CERTINFO, 1);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

curl_exec($curl);
var_dump(curl_getinfo($curl, CURLINFO_CERTINFO));
curl_close($curl);
```

```
# php curltest.php
array(0) {
}
```

ダメじゃん…

## サーバ環境
環境は以下のような感じ。CentOS7にremi-php71リポジトリを使ってPHP 7.1を入れている。

```
# cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)

# php --version
PHP 7.1.12 (cli) (built: Dec  1 2017 13:53:12) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.1.12, Copyright (c) 1999-2017, by Zend Technologies

# php --ri curl

curl

cURL support => enabled
cURL Information => 7.29.0
Age => 3
Features
AsynchDNS => Yes
CharConv => No
Debug => No
GSS-Negotiate => Yes
IDN => Yes
IPv6 => Yes
krb4 => No
Largefile => Yes
libz => Yes
NTLM => Yes
NTLMWB => Yes
SPNEGO => No
SSL => Yes
SSPI => No
TLS-SRP => No
Protocols => dict, file, ftp, ftps, gopher, http, https, imap, imaps, ldap, ldaps, pop3, pop3s, rtsp, scp, sftp, smtp, smtps, telnet, tftp
Host => x86_64-redhat-linux-gnu
SSL Version => NSS/3.28.4
ZLib Version => 1.2.7
libSSH Version => libssh2/1.4.3

# curl --version
curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.28.4 zlib/1.2.7 libidn/1.28 libssh2/1.4.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp
Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz unix-sockets

# yum list installed php curl libcurl
インストール済みパッケージ
curl.x86_64                      7.29.0-42.el7_4.1                   @updates
libcurl.x86_64                   7.29.0-42.el7_4.1                   @updates
php.x86_64                       7.1.12-4.el7.remi                   @remi-php71
```

## libcurlのテストコードで確認
とりあえずPHPのcurlモジュールが不具合を起こしている可能性を考え、libcurlで問題がない事を確認したい。ここで問題が無ければPHP-curlまたはPHPの設定に原因があると切り分けができる。

テストにはlibcurl-develに含まれるテスト用プログラムを使う。
本来はURLに*https://www.example.com*が指定されているが、*https://www.google.com/*に書き換えている。
記載してないが、Development Toolsとか必要なパッケージは適当にインストールしている。
コンパイルして実行してみるが**0 certs!とか出てる！**

```
# yum install libcurl-devel
# cd /usr/share/doc/libcurl-devel-7.29.0/
# cp certinfo.c certinfo_google.c
# vi certinfo_google.c
(CURLOPT_URLの指定するURLを書き換える)
# `curl-config --cc --cflags --libs` -o certinfo_google certinfo_google.c
# ./certinfo_google
0 certs!
```

## libcurlを更新
libcurlが怪しくなってきたので更新してみる。libcurl-develでインストールされるソースだと(自分には)makeできそうにないので、GitHubからダウンロードしたものを使う。

```
# cd /usr/local/src/
# wget https://github.com/curl/curl/releases/download/curl-7_57_0/curl-7.57.0.tar.gz
# tar xvzf curl-7.57.0.tar.gz
# cd curl-7.57.0/
# ./buildconf
# ./configure
# make
# make test
# make install
# hash -r
# curl --version
curl 7.57.0 (x86_64-unknown-linux-gnu) libcurl/7.57.0 OpenSSL/1.0.2k zlib/1.2.7
Release-Date: 2017-11-29
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile NTLM NTLM_WB SSL libz UnixSockets HTTPS-proxy
```

更新完了。続いてcertinfo.cで再テストしてみる。

```
# cd /usr/local/src/curl-7.57.0/docs/examples
# vi certinfo.c
(CURLOPT_URLの指定するURLを書き換える)
# make
# ./certinfo
3 certs!
Subject:C = US, ST = California, L = Mountain View, O = Google Inc, CN = www.google.com
Issuer:C = US, O = Google Inc, CN = Google Internet Authority G2
Version:2
(snip)
```

certinfoが取得出来るようになっているが、ここで**certinfo_google.cの時のようにコンパイルするとcertinfoが取得出来ない**。ライブラリが問題だと思われるが詳細不明。

## php-curlモジュールを更新
php-develを取得するがphp-curl用のソースが含まれていないのでGitHubから拾ってくる。
remi-php71リポジトリのphp-curlは念のためにバックアップしておく。

```
# yum install php-devel --enablerepo=remi,remi-php71
# cd /usr/local/src/
# wget https://github.com/php/php-src/archive/php-7.1.12.tar.gz
# tar xvzf php-7.1.12.tar.gz
# cp -ra php-src-php-7.1.12/ext/curl /usr/include/php/ext/
# cd /usr/include/php/ext/curl/
# phpize
# ./configure
# make
# make test

# cp /usr/lib64/php/modules/curl.so /usr/lib64/php/modules/curl.so.bak
# cp /usr/include/php/ext/curl/modules/curl.so /usr/lib64/php/modules/curl.so
# php --ri curl

curl

cURL support => enabled
cURL Information => 7.57.0
Age => 4
Features
AsynchDNS => Yes
CharConv => No
Debug => No
GSS-Negotiate => No
IDN => No
IPv6 => Yes
krb4 => No
Largefile => Yes
libz => Yes
NTLM => Yes
NTLMWB => Yes
SPNEGO => No
SSL => Yes
SSPI => No
TLS-SRP => No
HTTP2 => No
GSSAPI => No
KERBEROS5 => No
UNIX_SOCKETS => Yes
PSL => No
Protocols => dict, file, ftp, ftps, gopher, http, https, imap, imaps, ldap, ldaps, pop3, pop3s, rtsp, smb, smbs, smtp, smtps, telnet, tftp
Host => x86_64-unknown-linux-gnu
SSL Version => OpenSSL/1.0.2k
ZLib Version => 1.2.7
```

## 再びPHPでテストしてみる。

```
# php curltest.php
array(3) {
  [0]=>
  array(19) {
    ["Subject"]=>
    string(79) "C = US, ST = California, L = Mountain View, O = Google Inc, CN = www.google.com"
    ["Issuer"]=>
    string(57) "C = US, O = Google Inc, CN = Google Internet Authority G2"
    ["Version"]=>
    string(1) "2"
(snip)
```

certinfoの取得がうまく行ったし、CMSの警告メッセージも消えた（確認前にhttpdやphp-fpmを再起動しておく）。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/63695/386d63c4-354e-8f66-c43c-ba0c8fee9ee8.png)

## 元に戻す方法
元に戻すにはlibcurlのアンインストールとphp-curlの書き戻しをおこなう。*make uninstall*すればyumでインストールされていたバージョンに戻る。

```
# cd /usr/local/src/curl-7.57.0/
# make uninstall
# hash -r

# cp /usr/lib64/php/modules/curl.so.bak /usr/lib64/php/modules/curl.so
# systemctl restart httpd
```
curl.soをバックアップしていなかった時はphpを上書きインストールする。

```
# yum reinstall php-common --enablerepo=remi,remi-php71
```


# 備考
libcurlのアップデート後にもcertinfo.cのコンパイル方法によってはcertinfoの確認に失敗する。しかし、libcurlのアップデートを行わずにphp-curlだけ作り直してもNGだったためlibcurlの更新作業は不可欠のはず。
certinfoが取得出来ない不具合についてググっても参考になる情報がほとんど出てこなかったのは有名すぎる仕様だから？