| Red Hat responded to me. Red Hat asserts (and I have verified) that RHEL7 did build libselinux.so.1 with the rpm_execcon() function. The specific line in the RHEL7 libselinux.spec file (for every version of the RHEL7 libselinux package) is:
# FIXME: export DISABLE_RPM="y"
|
This is trivial to verify on a RHEL7 system, using the stock Ruby and libselinux-ruby packages:
$ /usr/bin/irb |
irb(main):001:0> require 'selinux' |
=> true |
irb(main):002:0> Selinux.rpm_execcon(1, 2, 3, 4) |
TypeError: Expected argument 1 of type char const *, but got Fixnum 2 |
in SWIG method 'rpm_execcon' |
from (irb):2:in `rpm_execcon' |
from (irb):2 |
from /usr/bin/irb:12:in `<main>' |
irb(main):003:0>
|
For RHEL 8.0, someone acted the FIXME comment, and removed the rpm_execcon() function. But since libselinux is an ABI Compatibility level 1 package starting with RHEL7: https://access.redhat.com/articles/rhel-abi-compatibility …removing rpm_execcon() broke the ABI compatibility promise, and thus had to be reverted. That is why it was brought back in RHEL 8.1. But there is another change in play that landed in libselinux 2.7 (between RHEL7's libselinux 2.5 and RHEL8's libselinux 2.9). That change was the removal of the rpm_execcon() function from the Python and Ruby bindings. Via commit 581dde73:
commit 581dde735af139daaefb5888bff3ec7ca2470dee |
Author: Nicolas Iooss <nicola...@m4x.org> |
Date: Sat Nov 5 21:55:32 2016 +0100 |
|
libselinux: remove rpm_execcon from SWIG wrappers |
|
The Python wrapper of rpm_execcon() has several flaws: |
* An invalid call like selinux.rpm_execcon() triggers a segmentation |
fault. |
* The size of the buffer which is allocated to copy argv and envp is |
too small to hold all the values. |
* This allocated memory is leaked if one argument of rpm_execon() is not |
a sequence of bytes. |
|
The Ruby wrapper has no such flaws but can not be used as it is because |
it misses some glue code to convert argv and envp arguments to char |
*const [] values (even though the destructor is present!). |
|
As it is not possible to remove rpm_execcon() without changing |
libselinux soname (it would be an ABI break) like b67fefd991dd |
("libselinux: set DISABLE_RPM default to y.") tried to do, disable this |
interface locally in the SWIG wrappers. |
|
Signed-off-by: Nicolas Iooss <nicola...@m4x.org>
|
So starting with RHEL8, the Ruby selinux.so bindings no longer include rpm_execcon(), even though libselinux.so.1 does. I assert that the real problem here is how the Ruby selinux.so module (that is packaged in the puppet-agent package) is being built between 6.10 and 6.11. Specifically, since Fedora has never supplied a libselinux.so.1 that includes rpm_execcon(), this should not work:
$ cat /etc/fedora-release |
Fedora release 31 (Thirty One) |
|
$ rpm -q puppet-agent |
puppet-agent-6.10.1-1.el8.x86_64 |
|
$ /opt/puppetlabs/puppet/bin/irb |
irb(main):001:0> require 'selinux' |
=> true |
irb(main):002:0> result = Selinux.rpm_execcon(1,2,3,4) |
Traceback (most recent call last): |
3: from /opt/puppetlabs/puppet/bin/irb:11:in `<main>' |
2: from (irb):2 |
1: from (irb):2:in `rpm_execcon' |
TypeError (Expected argument 1 of type char const *, but got Integer 2) |
in SWIG method 'rpm_execcon'
|
The rpm_execcon() function is not available in libselinux.so.1. There is no way Ruby could have resolved that symbol. Furthermore, the fact that the RHEL8 puppet-agent selinux.so library knows Selinux.rpm_execcon() means that it was not built from the stock RHEL8 libselinux packages, as the stock RHEL8 selinux.so does not know that symbol:
$ cat /etc/redhat-release |
Red Hat Enterprise Linux release 8.1 (Ootpa) |
|
$ /usr/bin/irb |
irb(main):001:0> require 'selinux' |
=> true |
irb(main):002:0> result = Selinux.rpm_execcon(1,2,3,4) |
Traceback (most recent call last): |
2: from /usr/bin/irb:11:in `<main>' |
1: from (irb):2 |
NoMethodError (undefined method `rpm_execcon' for Selinux:Module)
|
The version of Ruby is identical between Puppet agent 6.10 and 6.11:
$ /opt/puppetlabs/puppet/bin/ruby --version |
ruby 2.5.7p206 (2019-10-01 revision 67816) [x86_64-linux]
|
…so this behavior difference cannot be explained by a change in Ruby. At this point, the only possible explanation is that up through 6.10, the selinux.so Ruby library that the puppet-agent package has shipped with (which does not seem to correspond to any selinux.so Ruby library as supplied by Red Hat) has inlined the rpm_execcon() function entirely, so it never mattered whether it was actually present in libselinux.so.1. But starting with 6.11, selinux.so no longer inlines rpm_execcon(). So if 6.11 is built on a distribution where the system libselinux.so.1 library does not contain rpm_execcon(), then selinux support is not available. The file sizes of the 6.10 and 6.11 selinux.so objects tend to support the hypothesis that the 6.10 objects have code that 6.11 does not:
Finally, if I replace the 6.11 selinux.so with the 6.10 version (as I have done above), then 6.11's SELinux support behaves no differently than 6.10, right down to providing the Selinux.rpm_execcon() function. My guess is that the selinux.so Ruby library that is packaged in puppet-agent is built from an out-of-tree libselinux fork, where the build process just plucks out the Ruby selinux.so library and leaves everything else. If that is indeed the case, then the only thing that should be necessary to resolve this problem is to pull commit 581dde73 into that build tree. |