Recently I decided to do some restructuring of a Postfix/Amavis/ClamAV
installation and ran into a puzzling situation. After some head-scratching, I
came up with a solution and thought I'd share it, in case it should be helpful
to others (that's also why I'm posting here, rather than to the developers
list).
FWIW, the setup is Amavis 2.12.1 on Linux with Perl 5.30, and ClamAV 0.103.2.
The packages are loosely based on Fedora but locally-built.
Now, as for the setup:
* There are two user accounts, "amavis" and "clamscan".
* Both are members of "clamgroup".
* The ClamAV socket is owned by user "clamscan",
* and "clamgroup" has r+w permission on it.
Unfortunately, that won't fly, at least not with $daemon_group = "amavis" in
amavisd.conf. That brings the infamous "Permission denied" on the socket.
However, with $daemon_group = "clamgroup", all is well, and the amavisd
daemon can talk to clamd, as expected. Incidentally, that means that
things on disk are OK.
But I didn't like that "solution", so I wondered why amavisd seems to ignore
the supplementary groups.
In the end, inserting a call to initgroups() into the drop_priv() function took
care of that.
The patch that I am applying to my local build is given below. In
case it gets garbled, the really relevant line in full is:
Unix::Groups::FFI::initgroups($desired_user);
This is inserted in front of the setgid/setuid calls (at line 18889 or
so).
I have also sprinkled some logging statements in between. They are helpful to
see the difference between
running it with and without the initgroups() call being made.
The possible downside is that this call introduces a dependency on
perl-Unix-Groups-FFI, even in situations where no privilege drop is
desired/required.
For me, that is no problem. If it is, it might be possible to add a Y/N config
variable like $daemon_extra_groups, and pull in initgroups() only if this is
set to true. But that would be confusing. I think that the user/group concept
should be honored out of the box. Other than the extra dependency, I don't see
any reason why not.
Compare to ClamAV, where they used to have an "AllowSupplementaryGroups"
setting. This is now obsolete and it always "allows supplementary groups".
Comments welcome, if any.
Best,
Luc Pardon
--- a/amavisd.orig 2021-05-15 11:19:01.419229284 +0200
+++ b/amavisd 2021-05-15 11:20:15.608210946 +0200
@@ -239,6 +239,7 @@
MIME::Decoder::Base64 MIME::Decoder::Binary MIME::Decoder::QuotedPrint
MIME::Decoder::NBit MIME::Decoder::UU MIME::Decoder::Gzip64
Net::LibIDN Net::Server Net::Server::PreFork
+ Unix::Groups::FFI
));
# with earlier versions of Perl one may need to add additional modules
# to the list, such as: auto::POSIX::setgid auto::POSIX::setuid ...
@@ -18886,8 +18887,22 @@
}
defined $gid or die "drop_priv: No such group: $desired_group\n";
$( = $gid; $) = "$gid $gid"; # real and effective GID
+
+ my @groups = Unix::Groups::FFI::getgrouplist($desired_user)
+ or print "drop_priv: Can't getgrouplist for $desired_user:
$!";
+ do_log(5, "Grouplist for $desired_user: " . join(', ',@groups) );
+
+ my $rc = Unix::Groups::FFI::initgroups($desired_user);
+ do_log(5, "initgroups($desired_user) returns $rc");
+
POSIX::setgid($gid) or die "drop_priv: Can't setgid to $gid: $!";
+ @groups = Unix::Groups::FFI::getgroups() or print "drop_priv: Can't
getgroups: $!";
+ do_log(5, "getgroups() after setgid() : " . join(', ',@groups) );
+
POSIX::setuid($uid) or die "drop_priv: Can't setuid to $uid: $!";
+ @groups = Unix::Groups::FFI::getgroups() or print "drop_priv: Can't
getgroups: $!";
+ do_log(5, "getgroups() after setuid() : " . join(', ',@groups) );
+
$> = $uid; $< = $uid; # just in case
# print STDERR "desired user=$desired_user ($uid), current: EUID: $> ($<)\n";
# print STDERR "desired group=$desired_group ($gid), current: EGID: $) ($()\n";