【最新のOpenSSLに対応】uw-imapのlibc-client.soを得るまでの手順

最新のOpenSSL3.4.1を基本的な基盤にしてPHP環境を構築したい。併せてPHPにはIMAPサポートもさせたいときに、必要になるlibc-client.soを得るまでの手順になります。

libc-client.soを含むuw-imap自体がかなり大昔のソフトウェアと言う事もあり、コンパイル手順自体が既にロストテクノロジー状態で、libc-client.soを得るまで結構苦労しました。

二度とハマりたくないので記事に残します。

以下、手順になります。

uw-imapのソースコードとパッチを入手します。
```
wget https://github.com/uw-imap/imap/archive/refs/tags/imap-2007f_upstream.tar.gz
wget https://raw.githubusercontent.com/openwrt/packages/master/libs/uw-imap/patches/010-imap-2007f-openssl-1.1.patch
```

パッチが入手できないときは、以下の内容を保存して使って下さい。

```
From c3f68d987c00284d91ad6599a013b7111662545b Mon Sep 17 00:00:00 2001
From: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
Date: Fri, 2 Sep 2016 21:33:33 +0000
Subject: [PATCH] uw-imap: compile against openssl 1.1.0

I *think* I replaced access to cert->name with certificate's subject name. I
assume that the re-aranged C-code is doing the same thing. A double check
wouldn't hurt :)

Signed-off-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
---
 src/osdep/unix/ssl_unix.c | 28 +++++++++++++++++-----------
 1 file changed, 17 insertions(+), 11 deletions(-)

--- a/src/osdep/unix/ssl_unix.c
+++ b/src/osdep/unix/ssl_unix.c
@@ -59,7 +59,7 @@ typedef struct ssl_stream {
 static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
 static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags);
 static int ssl_open_verify (int ok,X509_STORE_CTX *ctx);
-static char *ssl_validate_cert (X509 *cert,char *host);
+static char *ssl_validate_cert (X509 *cert,char *host, char *cert_subj);
 static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat);
 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
                               long *contd);
@@ -210,6 +210,7 @@ static char *ssl_start_work (SSLSTREAM *
   BIO *bio;
   X509 *cert;
   unsigned long sl,tl;
+  char cert_subj[250];
   char *s,*t,*err,tmp[MAILTMPLEN];
   sslcertificatequery_t scq =
     (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
@@ -266,14 +267,19 @@ static char *ssl_start_work (SSLSTREAM *
   if (SSL_write (stream->con,"",0) < 0)
     return ssl_last_error ? ssl_last_error : "SSL negotiation failed";
                                /* need to validate host names? */
-  if (!(flags & NET_NOVALIDATECERT) &&
-      (err = ssl_validate_cert (cert = SSL_get_peer_certificate (stream->con),
-                               host))) {
-                               /* application callback */
-    if (scq) return (*scq) (err,host,cert ? cert->name : "???") ? NIL : "";
+  if (!(flags & NET_NOVALIDATECERT)) {
+
+       cert_subj[0] = '\0';
+       cert = SSL_get_peer_certificate(stream->con);
+       if (cert)
+               X509_NAME_oneline(X509_get_subject_name(cert), cert_subj, sizeof(cert_subj));
+       err = ssl_validate_cert (cert, host, cert_subj);
+       if (err)
+               /* application callback */
+               if (scq) return (*scq) (err,host,cert ? cert_subj : "???") ? NIL : "";
                                /* error message to return via mm_log() */
-    sprintf (tmp,"*%.128s: %.255s",err,cert ? cert->name : "???");
-    return ssl_last_error = cpystr (tmp);
+       sprintf (tmp,"*%.128s: %.255s",err,cert ? cert_subj : "???");
+       return ssl_last_error = cpystr (tmp);
   }
   return NIL;
 }
@@ -313,7 +319,7 @@ static int ssl_open_verify (int ok,X509_
  * Returns: NIL if validated, else string of error message
  */

-static char *ssl_validate_cert (X509 *cert,char *host)
+static char *ssl_validate_cert (X509 *cert,char *host, char *cert_subj)
 {
   int i,n;
   char *s,*t,*ret;
@@ -322,9 +328,9 @@ static char *ssl_validate_cert (X509 *ce
                                /* make sure have a certificate */
   if (!cert) ret = "No certificate from server";
                                /* and that it has a name */
-  else if (!cert->name) ret = "No name in certificate";
+  else if (cert_subj[0] == '\0') ret = "No name in certificate";
                                /* locate CN */
-  else if (s = strstr (cert->name,"/CN=")) {
+  else if (s = strstr (cert_subj,"/CN=")) {
     if (t = strchr (s += 4,'/')) *t = '\0';
                                /* host name matches pattern? */
     ret = ssl_compare_hostnames (host,s) ? NIL :
```

ソースコードを展開して、パッチを当てます。

```
tar xvfz imap-2007f_upstream.tar.gz
cd imap-imap-2007f_upstream/
patch -p1 < ../010-imap-2007f-openssl-1.1.patch
```

下記パラメータにてmakeを実行します。これでもかと言うくらいopensslを配置しているディレクトリ情報を渡します。

```
make lnp SSLTYPE=unix \
         EXTRACFLAGS="-fPIC -I/var/home/lib/openssl/include -I/var/home/lib/openssl/include/openssl" \
         EXTRALDFLAGS="-L/var/home/lib/openssl/lib64 -Wl,-rpath=/var/home/lib/openssl/lib64" \
         SSLLIB=/var/home/lib/openssl/lib64

make lnp c-client
```

そして、肝心なlibc-client.soのコンパイルを行います。

```
cd c-client
gcc `cat CFLAGS` `cat LDFLAGS` -shared -o libc-client.so.2007 ./*.o
```

うまく行っていれば、libc-client.so.2007とc-client.aと各種ヘッダーファイルが作成されているので、それらを運用環境に配置します。最後のシンボリックリンク作成は忘れないようにして下さい。PHPをコンパイルした時にlibc-client.soでリンクしにくるので、シンボリックリンクを作成しておかないとPHPコンパイル時点で詰む事になります。

```
mkdir /var/home/lib/c-client
mkdir /var/home/lib/c-client/lib64
mkdir /var/home/lib/c-client/include
cp *.h /var/home/lib/c-client/include
cp linkage.c /var/home/lib/c-client/include
cp libc-client.so.2007 /var/home/lib/c-client/lib64
cp c-client.a /var/home/lib/c-client/lib64
cd /var/home/lib/c-client/lib64
ln -s libc-client.so.2007 libc-client.so
```

以上です！お疲れ様でした！