The good old "permission denied", the ignored group memberships, and a proposed solution

Luc Pardon amavisuser at
Sat May 15 17:55:19 CEST 2021

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:


This is inserted in front of the setgid/setuid calls (at line 18889 or

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.


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(', ', at 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(', ', at 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(', ', at 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";

More information about the amavis-users mailing list