使用自定义 openssl 构建 curl、httpd 和其他程序,同时避免使用默认系统 openssl

使用自定义 openssl 构建 curl、httpd 和其他程序,同时避免使用默认系统 openssl

我一直在尝试升级旧 Linux 机器(Debian 7.5 wheezy)上的一些程序。我想保持默认系统库不变,并在其旁边添加一些自定义版本。它对大多数库都运行良好,只有 OpenSSL 让我很为难(libssl.so + libcrypt.so)。

这是我的 OpenSSL 配置行:

# ./config --prefix=/usr/local --openssldir=/usr/local/openssl no-gost shared zlib no-ssl2 -fPIC

然后对于其他程序,例如 cURL:

# ./configure LDFLAGS="-L/usr/local/ssl/lib -ldl" --with-ssl --enable-shared

但是我得到了错误的 OpenSSL 版本:

# curl --version
curl 7.44.0 (x86_64-unknown-linux-gnu) libcurl/7.44.0 OpenSSL/1.0.1e

我知道 libssl.so 在该系统的以下文件夹中有副本:

/usr/lib/libssl.so
/usr/lib/x86_64-linux-gnu/libssl.so
/usr/local/lib/libssl.so
/usr/local/ssl/lib/libssl.so

它们都是相同的自定义编译版本,除了/usr/lib/x86_64-linux-gnu/libssl.so这似乎是我的默认系统库(v1.0.5e)。

我想知道如何指示配置脚本以避免查看内部/lib/x86_64-linux-gnu?我可以只更改 /usr/lib/x86_64-linux-gnu/libssl.so 符号链接并将其指向我的构建吗?这样安全吗?

谢谢,

答案1

涉及两种不同的链接器操作,构建时间(用于查找库和“符号”,由ld或等效执行),以及运行(由 执行ld.so)将库加载到进程内存中并执行搬迁

-L在构建期间使用时,ld仅确认库和所需符号(通常是组成 API 的函数)可用。在运行时,可以使用具有不同路径的库。LD_LIBRARY_PATHLD_PRELOAD路径-R-rpath)是控制这一点的一些方法——最后一种是在构建时指定这一点的方法(因此它是二进制文件的属性,并且不太依赖于用户环境)。

对于任何库,功能、API 和 ABI 都可能发生变化,如果链接器找到或加载“错误”的二进制文件,则会导致错误或不稳定。即使符号可以按预期匹配(否则您会在执行前收到错误),但在进程执行过程中,轮子可能会脱落。对此有解决方案,即对库文件名进行版本控制,以及对库中的符号进行版本控制,尽管这些在这里没有多大用处。(一个复杂的问题是,所有 OpenSSL 1.0.x 系列中的SHLIB_MAJOR/都是“1.0.0”,因此除非您破解 .,否则无法轻易区分这些库。)SHLIB_MINORMakefile

Curl 本身会检查此类问题,它会将构建时libcurl版本与运行时版本进行比较,并且可能会发出:

警告:curl 和 libcurl 版本不匹配。功能可能会受到影响。

当当代ld注意到依赖项中可能存在冲突的库时,它会在构建时执行类似操作。

为一个干净的构建,无需任何运行时环境技巧,您可以:

  1. 构建 OpenSSL 时确保 RPATH 是明确的,这是因为libssl.so链接到libcrypto.so、 和 安装到特定路径
  2. 使用正确的链接时间和运行时规范构建包(即 curl),以便它找到具体的 libssl.so/libcrypto.so

修复此问题的一个廉价而又卑鄙的方法是chrpath在构建后使用用户更改二进制文件中的 ELF RPATH,我一般不建议这样做,对于 curl 绝对不建议这样做,请继续阅读。

现在来看看复杂之处:curl 支持许多协议,其中一些协议是通过其他库提供的,并且当协议支持 TLS(或使用加密函数)时,它往往也会链接到libssl.soand/or libcrypto.so,例如 LDAP 和 SSH。你不要想要的是:

  curl ↦ libcurl.so → libldap.so → libssl.so
       ↦ lib...
       ↦ libcrypto.so
       ↳ libssl.so

wherecurl本身会使用一个版本,但libldap.so可能期望加载不同的版本;如果存在 ABI 不兼容,这也可能在运行时失败。您可能在符号或加载顺序方面运气不佳,最终会加载两个(或更多!)版本的库。

通常有三种方法可以解决这个问题:

  1. 在构建时关闭所有不需要的功能,对于 curl 来说,至少 LDAP 和 SSH2 必须关闭
  2. 如果不可能的话,请构建新版本的依赖库,这些库也链接到新的 OpenSSL
  3. 如果这不可能,您可能能够使用.a库进行一些静态链接。对于 curl,这与仅仅使用 不同--enable-static。它可以工作,我很久以前就这样做过,但它可能在这里不起作用,所以我不会进一步提及它;而且它可能会导致很多麻烦,尤其是使用 PIC/PIE 时。

GnuTLS 可能会让事情变得更加复杂,尽管同时使用这两个库不是问题(至少到目前为止我还没有看到过);但对于其他旨在实现 ABI 兼容性的库(如 LibreSSL)来说,情况并非如此。Curl 确实费了一番功夫才在其构建中只使用了一个 SSL 库(据我所知,它目前支持 8 个),但它不知道例如libssh2可能链接到什么。

因此,根据版本,您可以尝试分别构建 OpenSSL 和 curl:

# OpenSSL
./config --prefix=/usr/local --openssldir=/usr/local/openssl no-gost \
          shared zlib no-ssl2 -fPIC -Wl,-R,/usr/local/openssl
 make depend && make && make install_sw

# curl
./configure --with-ssl=/usr/local/openssl --enable-shared \
            --enable-ldap=no --without-libssh2 \
            CFLAGS=-Wl,-R,/usr/local/openssl/lib
make && make install

(尽管文档表明相反情况,--with-ldap=no并且--without-ldap没有任何有用的效果,使用--enable-ldap=no. libssh2(对于 scp)通常默认不启用。)

安装后,ldd /usr/local/bin/curl应该不会有任何意外:对预期libssl.so和的每个的引用都恰好一个libcrypto.so

OpenSSL 构建(非 autoconf)将RPATH在二进制文件中正确设置openssl,但默认情况下不会在库中设置。库的定位方式因平台而异(OpenSSL 构建中的大部分 RPATH 处理都是诱饵:它适用于 Linux 以外的平台)。这对于 curl 来说可能不是必需的,但不会引起问题。

当您使用 autoconf 时,configure无法保证非默认库位置会在二进制文件中产生匹配的 RPATH,CFLAGS/LDFLAGS通常可以解决这个问题。但是,在这里,使用 OpenSSL 的两步过程,config无法很好地处理像 这样的变量LDFLAGS,因此改为在行末添加config,并Configure在创建 时正确处理它Makefile

您可以使用以下命令检查 ELF 可执行文件或库运行时路径:

readelf --dynamic elfbinary | grep PATH

您可以使用以下方式调试库加载:

LD_DEBUG=files,libs /usr/local/bin/curl ...

(请注意,原始问题已经过时,OpenSSL 1.0.x 不再受官方支持,尽管发行版可能有所不同。此处的说明通常适用于为特定目的构建 OpenSSL 1.0.x 的特殊版本。构建stunnel二进制文件以反向代理旧网络设备,以便它可以与当代浏览器一起使用就是一个很好的例子。)

您可以在我对这个问题的回答中找到进一步的解释和说明: 获取 ld 来选择正确的库

答案2

我发现一个被标记为“不是最好的方法”但对我有用的解决方案是编辑 LD_LIBRARY_PATH 系统变量,例如在 <user>/.bashrc 中:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

答案3

我尝试按照你的建议去做,结果出现了分段错误!所以,不安全!

/usr/lib/i686/cmov$ sudo ln -s /usr/lib/libssl.so.1.0.0 libssl.so.0.9.8
/usr/lib/i686/cmov$ ls -la
total 1684
drwxr-xr-x 2 root root    4096 Jun 16 14:15 .
drwxr-xr-x 3 root root    4096 Oct 22  2012 ..
-rw-r--r-- 1 root root 1393308 Feb 11  2013 libcrypto.so.0.9.8
lrwxrwxrwx 1 root root      24 Jun 16 14:15 libssl.so.0.9.8 -> /usr/lib/libssl.so.1.0.0
-rw-r--r-- 1 root root  310296 Feb 11  2013 libssl.so.0.9.8.bk
:/usr/lib/i686/cmov$ sudo /etc/init.d/nginx restart
Restarting nginx: nginx/usr/sbin/nginx: /usr/lib/i686/cmov/libssl.so.0.9.8: no version information available (required by /usr/sbin/nginx)
Segmentation fault

相关内容