Bug 3586 - [review] perl setuid fails, breaking spamd in FreeBSD, MacOS X
Summary: [review] perl setuid fails, breaking spamd in FreeBSD, MacOS X
Status: RESOLVED FIXED
Alias: None
Product: Spamassassin
Classification: Unclassified
Component: spamc/spamd (show other bugs)
Version: SVN Trunk (Latest Devel Version)
Hardware: Other FreeBSD
: P5 normal
Target Milestone: 3.0.0
Assignee: SpamAssassin Developer Mailing List
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2004-07-10 12:49 UTC by Mike Loiterman
Modified: 2004-07-22 12:08 UTC (History)
1 user (show)



Attachment Type Modified Status Actions Submitter/CLA Status
suggested patch patch None Theo Van Dinter [HasCLA]
new version patch None Theo Van Dinter [HasCLA]

Note You need to log in before you can comment on or make changes to this bug.
Description Mike Loiterman 2004-07-10 12:49:06 UTC
Getting these types of errors after processing messages:

Jul 10 14:44:03 fat_man spamd[1822]: DCC -> check failed: setuid 0 to 1000
failed! at /usr/local/lib/perl5/site_perl/5.6.1/Mail/SpamAssassin/Util.pm line 932.
Jul 10 14:44:28 fat_man spamc[1821]: failed sanity check, 6050 bytes claimed,
17021 bytes seen
Comment 1 Theo Van Dinter 2004-07-10 15:52:27 UTC
Subject: Re:  New: failed sanity check

On Sat, Jul 10, 2004 at 12:49:07PM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
> Jul 10 14:44:03 fat_man spamd[1822]: DCC -> check failed: setuid 0 to 1000
> failed! at /usr/local/lib/perl5/site_perl/5.6.1/Mail/SpamAssassin/Util.pm line 932.
> Jul 10 14:44:28 fat_man spamc[1821]: failed sanity check, 6050 bytes claimed,
> 17021 bytes seen

Hrm.  Well, the sanity check error is caused by spamd being killed from
the first message.  The first message is from spamd not able to change
its real uid to match the effective uid, which I have no idea what to
tell you.  The code is really simple there:

sub setuid_to_euid {
  return if (RUNNING_ON_WINDOWS);
  if ($< != $>) {
    dbg ("setting real uid from $< to match effective uid $>");
    $< = $>;
    if ($< != $>) { die "setuid $< to $> failed!"; }
  }
}

$< is real uid, $> is effective uid.  So for security reasons, we try
dropping root priv, then spamd blows up because the real uid (0) doesn't
equal the effective uid (1000).

So ...  The real issue is: why can't your spamd process drop root privs?
I have no answer to that, as far as I know, root can always change ruid
to another user, but then you can't go back, which is expected.

Unless there's something I'm missing though, this seems like a perl/OS
issue, not an SA one.

Comment 2 Malte S. Stretz 2004-07-10 16:24:34 UTC
Maybe some weird security restriction from FreeBSD? Something like Linux 
Capabilities on That Other OS? I don't think anybody is running FreeBSD here, 
you should ask on your local BSD list. 
 
I keep this bug open as I'm not sure if we should really die there or do so 
just in paranoid mode. 
Comment 3 Mike Loiterman 2004-07-10 17:34:11 UTC
My setup works fine with 2.63.

Seems like, as you said, a strange permissions issue.

Is there anything else I can try?
Comment 4 Malte S. Stretz 2004-07-10 17:57:21 UTC
Some ideas: 
 - Maybe that code was never called in 2.6x? Don't think so, but who knows. 
 - Maybe spamd already dropped privs before but Perl didn't update the uid 
accordingly? That could be caused by the new pre-forked children code and 
smells like a Perl bug. 
Comment 5 Mike Loiterman 2004-07-10 18:29:49 UTC
Is there anything I can do to help troubleshoot?
Comment 6 Theo Van Dinter 2004-07-10 21:00:48 UTC
Subject: Re:  failed sanity check

On Sat, Jul 10, 2004 at 05:57:22PM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
>  - Maybe that code was never called in 2.6x? Don't think so, but who knows. 

As I recall, the code is new in 3.0 to fix up any potential security
issues pre-helper launch.

Comment 7 Mike Loiterman 2004-07-11 18:36:43 UTC
I've rebuilt all the modules used for my SA installation, cleaned out all
preferences, and ran with a plain local.cf file.  Nothing seems to fix this problem.

Should I disable support for all "helpers"?

Is there something in my setup that is causing this problem or is this a bug
between perl and SA?
Comment 8 Theo Van Dinter 2004-07-11 19:37:58 UTC
Subject: Re:  failed sanity check

On Sun, Jul 11, 2004 at 06:36:44PM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
> Should I disable support for all "helpers"?

That will bypass the issue for now, yes.

> Is there something in my setup that is causing this problem or is this a bug
> between perl and SA?

It's between perl and your OS.

Comment 9 Mike Loiterman 2004-07-11 19:46:30 UTC
I'm going to post a message to one of the perl and/or FreeBSD mailing lists. 
How should I phrase the question?  "Why won't perl drop root privilages?"
Comment 10 Theo Van Dinter 2004-07-11 20:00:36 UTC
Subject: Re:  failed sanity check

On Sun, Jul 11, 2004 at 07:46:31PM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
> I'm going to post a message to one of the perl and/or FreeBSD mailing lists. 
> How should I phrase the question?  "Why won't perl drop root privilages?"

Interestingly, the same exact issue occurs on my Mac OS X machine,
but not any of the other platforms I have access to...

root# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $>=1000; p; $<=$>; p;'
RUID: 0, EUID: 0
RUID: 0, EUID: 1000
RUID: 0, EUID: 1000

The end line should read "RUID: 1000, EUID: 1000".

Linux:

# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $>=1000; p; $<=$>; p;' 
RUID: 0, EUID: 0
RUID: 0, EUID: 1000
RUID: 1000, EUID: 1000

Solaris:

# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $>=1000; p; $<=$>; p;'
RUID: 0, EUID: 0
RUID: 0, EUID: 1000
RUID: 1000, EUID: 1000


More interesting bits:

# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $<=1000; p;'
RUID: 0, EUID: 0
RUID: 0, EUID: 0
# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; use POSIX; setuid(1000); p;'
RUID: 0, EUID: 0
RUID: 0, EUID: 0
# cat - > t.c
main() {
  printf("RUID: %d, EUID: %d\n", getuid(), geteuid());
  setuid(1000);
  printf("RUID: %d, EUID: %d\n", getuid(), geteuid());
}
# gcc t.c
# ./a.out
RUID: 0, EUID: 0
RUID: 1000, EUID: 1000

so setuid() obviously works.  just not at all from perl.

Comment 11 Mike Loiterman 2004-07-11 20:39:38 UTC
Mac OS X / FreeBSD...birds of a feather...

So its clearly a FreeBSD / Perl issue.
Comment 12 Sidney Markowitz 2004-07-12 08:39:33 UTC
Googling for hints about this I found

http://freebsd.qmailrocks.org/qmailscanner.htm

which are instructions for installing qmail-scanner, presumably on FreeBSD.

The relevance of this oage is that it has two sets for of instructions for the
cases, quoting:

"How you go about configuring and installing qmail-scanner from this point on
depends on how you server's installation of Perl is configured. For the purposes
of this installation, there are 2 Perl setups.

1. Perl is configured to allow for setuid functions.

2. Perl is not configured for setuid functionality and, in fact, does not permit
it."

It appears that we should find out about these two configurations of perl.

Comment 13 Theo Van Dinter 2004-07-12 09:24:31 UTC
ok, so this increasingly is looking like we'll have to deal with it.  so moving to 3.0 queue.

BTW: from Sidney's post, I dug around a little myself.  the Config module has a d_dosuid option listed.  
In and of itself, it doesn't seem to tell me whether or not I can suid, but:

perl -e 'use Config; foreach("dosuid","seteuid","setresuid","setreuid","suidsafe"){print "$_ = ",(defined 
$Config{"d_$_"} ? $Config{"d_$_"} : "undef"),"\n"}'

Linux:

dosuid = define
seteuid = define
setresuid = define
setreuid = define
suidsafe = undef

Solaris:

dosuid = undef
seteuid = define
setresuid = undef
setreuid = define
suidsafe = define

Mac OS X:

dosuid = undef
seteuid = define
setresuid = undef
setreuid = define
suidsafe = undef

fwiw.
Comment 14 Theo Van Dinter 2004-07-12 09:33:54 UTC
Subject: Re:  failed sanity check

On Mon, Jul 12, 2004 at 09:24:32AM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
> perl -e 'use Config; foreach("dosuid","seteuid","setresuid","setreuid","suidsafe"){print "$_ = ",(defined 
> $Config{"d_$_"} ? $Config{"d_$_"} : "undef"),"\n"}'

So far it looks like we can suid if:

dosuid|suidsafe

More data is welcome.

> Linux:
> dosuid = define
> suidsafe = undef
> 
> Solaris:
> dosuid = undef
> suidsafe = define
> 
> Mac OS X:
> dosuid = undef
> suidsafe = undef

Comment 15 Malte S. Stretz 2004-07-12 10:26:04 UTC
I asked on #perl about this and unfortunately the only response I got was 
  <Roderick>Moonflux:  The program should be changed to set the real uid 
first, then the effective uid. 
 
I don't know what he means with that though :) But while I looked at the man 
page, I found a reference to POSIX::setuid(). Maybe that offers a way to check 
if the routine works? 
Comment 16 Sidney Markowitz 2004-07-12 12:13:28 UTC
Some more googling and reading leads me to think that the link I found is only
talking about setting a perl script file (beginning with #!/usr/bin/perl)
setuid, not about setting the uid from within a perl program.

I haven't found any discussion of the latter.
Comment 17 Theo Van Dinter 2004-07-12 13:58:01 UTC
Subject: Re:  failed sanity check

On Mon, Jul 12, 2004 at 10:26:05AM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
>   <Roderick>Moonflux:  The program should be changed to set the real uid 
> first, then the effective uid. 
>  
> I don't know what he means with that though :) But while I looked at the man 
> page, I found a reference to POSIX::setuid(). Maybe that offers a way to check 
> if the routine works? 

Well, he means:

$<=1000;
$>=$<;

but that doesn't work either, already tested that. ;):

# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $<=1000; p;'
RUID: 0, EUID: 0
RUID: 0, EUID: 0


BTW: I had a friend test out his fbsd box:

default in fbsd 4.9 is perl 5.005:

dosuid = define
seteuid = define
setresuid = define
setreuid = define
suidsafe = undef

RUID: 0, EUID: 0
RUID: 0, EUID: 1000
RUID: 0, EUID: 1000

he also has the perl-5.6.1_15 ports version installed:

dosuid = define
seteuid = undef
setresuid = define
setreuid = define
suidsafe = undef

RUID: 0, EUID: 0
RUID: 0, EUID: 1000
RUID: 1000, EUID: 1000

# uname -a
FreeBSD some.host.name 4.9-RELEASE-p10 FreeBSD 4.9-RELEASE-p10 #0: Fri Jun 18 18:52:47 EDT 2004
root@some.host.name:/usr/obj/usr/src/sys/GENERIC i386


Comment 18 Justin Mason 2004-07-12 14:44:33 UTC
Subject: Re:  failed sanity check 

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


>he also has the perl-5.6.1_15 ports version installed:
>
>dosuid = define
>seteuid = undef
>setresuid = define
>setreuid = define
>suidsafe = undef
>
>RUID: 0, EUID: 0
>RUID: 0, EUID: 1000
>RUID: 1000, EUID: 1000

Good -- that's what I would have expected to happen, what should
be happening, and what's happening elsewhere.

It now strongly suggests a perl bug on *BSD:

    - Linux, perl version 5.8.x: ok
    - Solaris, perl version ???: ok
    - MacOS X, perl version ???: fails
    - FreeBSD, perl version 5.005: fails
    - FreeBSD, perl version 5.6.1: ok

I wonder if freebsd fails to support setting the real uid to non-root if
"seteuid" is defined in Config?

setreuid() would fix this.  could freebsd-ers try:

  perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; use POSIX qw(setreuid);
  POSIX::setreuid(1000,1000); p;'

or setresuid():

  perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; use POSIX qw(setresuid);
  POSIX::setresuid(1000,1000,1000); p;'

however, if those aren't exported, we can't use them :(

PS: the "suidsafe" stuff is for running setuid scripts, not for this
situation, so it can be discounted.

- --j.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)
Comment: Exmh CVS

iD8DBQFA8wYxQTcbUG5Y7woRAj9pAJ4/z4kABmCDFr4vZSjdMPDwCipsMQCfSt7o
lCC0wrb+zWuXkqlQpHn16Rw=
=8I4s
-----END PGP SIGNATURE-----

Comment 19 Mike Loiterman 2004-07-12 15:07:55 UTC
>  - FreeBSD, perl version 5.6.1: ok

Hrm...I'm using 5.6.1 and it isn't working for me.

[17:08:51 root@fat_man: /home/mike/mailsort]# perl5.6.1 -V
Summary of my perl5 (revision 5.0 version 6 subversion 1) configuration:
  Platform:
    osname=freebsd, osvers=4.4-stable, archname=i386-freebsd
    uname='freebsd fat_man.ascendency.net 4.4-stable freebsd 4.4-stable #1: tue
mar 26 16:40:22 cst 2002 root@fat_man.ascendency.net:usrobjusrsrcsysfat_man_scsi
_kernel i386 '
    config_args=''
    hint=recommended, useposix=true, d_sigaction=define
    usethreads=undef use5005threads=undef useithreads=undef usemultiplicity=unde
f
    useperlio=undef d_sfio=undef uselargefiles=define usesocks=undef
    use64bitint=undef use64bitall=undef uselongdouble=undef
  Compiler:
    cc='cc', ccflags ='-fno-strict-aliasing -I/usr/local/include',
    optimize='-O',
    cppflags='-fno-strict-aliasing -I/usr/local/include'
    ccversion='', gccversion='2.95.3 20010315 (release) [FreeBSD]', gccosandvers
=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
    ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize
=8
    alignbytes=4, usemymalloc=n, prototype=define
  Linker and Libraries:
    ld='cc', ldflags ='-Wl,-E  -L/usr/local/lib'
    libpth=/usr/lib /usr/local/lib
    libs=-lm -lc -lcrypt -lutil
    perllibs=-lm -lc -lcrypt -lutil
    libc=, so=so, useshrplib=false, libperl=libperl.a
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags=' '
    cccdlflags='-DPIC -fpic', lddlflags='-shared  -L/usr/local/lib'


Characteristics of this binary (from libperl):
  Compile-time options: USE_LARGE_FILES
  Built under freebsd
  Compiled at Apr 11 2002 04:43:42
  @INC:
    /usr/local/lib/perl5/5.6.1/i386-freebsd
    /usr/local/lib/perl5/5.6.1
    /usr/local/lib/perl5/site_perl/5.6.1/i386-freebsd
    /usr/local/lib/perl5/site_perl/5.6.1
    /usr/local/lib/perl5/site_perl/5.005/i386-freebsd
    /usr/local/lib/perl5/site_perl/5.005
    /usr/local/lib/perl5/site_perl

[17:09:00 root@fat_man: /home/mike/mailsort]# uname -a
FreeBSD fat_man.ascendency.net 4.4-STABLE FreeBSD 4.4-STABLE #1: Tue Mar 26
16:40:22 CST 2002    
root@fat_man.ascendency.net:/usr/obj/usr/src/sys/FAT_MAN_SCSI_KERNEL  i386

[17:04:52 root@fat_man: /home/mike/mailsort]# perl5.6.1 -e 'sub p {print "RUID:
$<, EUID: $>\n";} p; use POSIX qw(setreuid); POSIX::setreuid(1000,1000); p;'
"setreuid" is not exported by the POSIX module at
/usr/local/lib/perl5/5.6.1/i386-freebsd/POSIX.pm line 19
Can't continue after import errors at
/usr/local/lib/perl5/5.6.1/i386-freebsd/POSIX.pm line 19
BEGIN failed--compilation aborted at -e line 1.

[17:05:18 root@fat_man: /home/mike/mailsort]# perl5.6.1 -e 'sub p {print "RUID:
$<, EUID: $>\n";} p; use POSIX qw(setresuid);POSIX::setresuid(1000,1000,1000); p;'
"setresuid" is not exported by the POSIX module at
/usr/local/lib/perl5/5.6.1/i386-freebsd/POSIX.pm line 19
Can't continue after import errors at
/usr/local/lib/perl5/5.6.1/i386-freebsd/POSIX.pm line 19
BEGIN failed--compilation aborted at -e line 1.

Comment 20 Theo Van Dinter 2004-07-12 15:08:52 UTC
Subject: Re:  failed sanity check

On Mon, Jul 12, 2004 at 02:44:34PM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
>     - Solaris, perl version ???: ok

I tried both both 5.005 and 5.8.3

>     - MacOS X, perl version ???: fails

5.8.1 RC3

>   perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; use POSIX qw(setreuid);
>   POSIX::setreuid(1000,1000); p;'
> 
> or setresuid():
> 
>   perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; use POSIX qw(setresuid);
>   POSIX::setresuid(1000,1000,1000); p;'
> 
> however, if those aren't exported, we can't use them :(

That's the case on my Mac OS X box unfortunately:

# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; use POSIX qw(setreuid); POSIX::setreuid(1000,1000); p;'
"setreuid" is not exported by the POSIX module
Can't continue after import errors at /System/Library/Perl/5.8.1/darwin-thread-multi-2level/POSIX.pm line
19
BEGIN failed--compilation aborted at -e line 1.

# perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; use POSIX qw(setresuid);
POSIX::setresuid(1000,1000,1000); p;'
"setresuid" is not exported by the POSIX module
Can't continue after import errors at /System/Library/Perl/5.8.1/darwin-thread-multi-2level/POSIX.pm line
19
BEGIN failed--compilation aborted at -e line 1.


I'm thinking a kluge for this is to simply try it, and instead of
doing a die, throw a debug warning that this is a potential security issue,
and document it somewhere.

Comment 21 Theo Van Dinter 2004-07-12 15:13:39 UTC
Subject: Re:  failed sanity check

On Mon, Jul 12, 2004 at 03:07:56PM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
> >  - FreeBSD, perl version 5.6.1: ok
> 
> Hrm...I'm using 5.6.1 and it isn't working for me.

are you using the standard of "ports" version of perl?

it seems somehow related to how perl is built on these bsd-based
platforms.  they clearly can do setuid() from a C program, but not from
perl, so either there's some security thing enabled such that perl is
blocked or it was compiled in such a way so that it doesn't try.

I'm attempting to get truss/strace (whatever the fbsd equivilent is) on
"perl -e '$<=1234;'".  I don't seem to have that ability on the Mac box. :(

Comment 22 Mike Loiterman 2004-07-12 15:20:46 UTC
> are you using the standard of "ports" version of perl?

No.

I can paste my Makefile if you want.
Comment 23 Theo Van Dinter 2004-07-12 15:20:48 UTC
Subject: Re:  failed sanity check

On Mon, Jul 12, 2004 at 06:13:37PM -0400, Theo Van Dinter wrote:
> I'm attempting to get truss/strace (whatever the fbsd equivilent is) on
> "perl -e '$<=1234;'".  I don't seem to have that ability on the Mac box. :(

ok, the truss output from the default fbsd 4.9 perl shows that there
was no attempt for setuid() or anything similar.

so it's not like it's trying and the OS throws an error.

Comment 24 Theo Van Dinter 2004-07-12 15:23:05 UTC
Subject: Re:  failed sanity check

On Mon, Jul 12, 2004 at 03:20:47PM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
> > are you using the standard of "ports" version of perl?

Sorry, I meant "standard" _or_ "ports".  I'm assuming "standard"
(although I was under the impression the standard version was 5.005 on
pretty much all the fbsd versions).

Comment 25 Mike Loiterman 2004-07-12 15:26:18 UTC
I'm not using the ports version.
Comment 26 Mike Loiterman 2004-07-12 15:28:39 UTC
Actually, I have both installed...
/usr/bin/perl (5.005_03 )
/usr/local/bin/perl5.6.1

perl is from the system and perl5.6.1 was installed manually.  I kept both for
compatability reasons.
Comment 27 Sidney Markowitz 2004-07-12 17:55:52 UTC
Changed summary to better reflect problem and show up better in searches
Comment 28 Sidney Markowitz 2004-07-12 18:07:51 UTC
Is this an answer? (I'm not near my MacOS X box to try it myself)

Quote from http://archives.listbox.com/spf-discuss@v2.listbox.com/200312/0446.html
 (see link for attributions):

> However I was assuming the script was to be run as root via
> /etc/init.d/something for example, and doesn't require
> suidperl. I can't think of why a perl intepreter can't use
> the setuid or setgid system call (achived by using
> '$( = 111' or '$< = 111' to set(gu)id to (gu)id 11).

It was solved, the particular syntax of the setuid was not liked by
freebsd I guess. That's not to say I wasn't a bit confused ;)
So $< = $> = $user;
became
$< = $user;
$> = $user;
and so on. 

Comment 29 Sidney Markowitz 2004-07-13 01:23:04 UTC
I tried that on MacOS X and it did not work.

Here is the entire thread which contains two full code examples that supposedly
work in FreeBSD. I'll look at it later if nobody else solves this first.

http://archives.listbox.com/spf-discuss@v2.listbox.com/200312/index.html#0296
Comment 30 Theo Van Dinter 2004-07-13 09:39:02 UTC
yeah, separating the commands doesn't matter.  it's a red herring anyway, we
already change stuff separately.

the boiled down issue here is:

$<=1000;
die if ($< != 1000);

This fails on the default FreeBSD and Mac OS X perl installs (I'd guess probably
also NetBSD and OpenBSD).  We're trying to drop privilege, but setuid() away
from uid 0 fails.

On FreeBSD 4.9, anyway, doing some truss checking shows the perl binary not even
trying the setuid() call, so the problem seems to be completely perl binary
related.  My guess is that either the default BSD-supplied build config, or the
perl configure script, for some reason disables all setuid functionality (though
they leave seteuid() functionality)).


I see two issue paths we need to work on here:
1) Instead of spamd doing die, it really ought to just throw a loud warning, and
return "fail".  This is, after all, just a rule.

2) Figure out a way to get around this issue.  It seems to be a perl binary
issue, and setuid (via $<, POSIX, etc,) doesn't work at all.  So IMO:
  - add in documentation about the issue
  - suggest people compile/get a different perl that does allow setuid
  - somehow autodetect the situation and disable "helper" rules automatically. 
users will probably be confused that DCC/etc doesn't work, we'll have to say
RTFM a lot...

I'm not really sure how to autodetect this though.  It's not an OS issue, since
you can get different perl binaries that work fine on FreeBSD, for instance.  So
we can't just disable if $^O is set appropriately.  It doesn't seem to be
obviously available via %Config, based on the results thus far.

FWIW: This issue just affects DCC, and Pyzor: setuid_to_euid() is only called by
helper_app_pipe_open_unix(), which is only called by helper_app_pipe_open(),
which is only called via Dns.pm and Reporter.pm for DCC and Pyzor.  Just so
we're clear this doesn't impact the majority of SA functionality. :)
Comment 31 Justin Mason 2004-07-13 11:44:52 UTC
BTW see also this thread on perl5-porters:

http://archive.develooper.com/perl5-porters@perl.org/msg95384.html
http://archive.develooper.com/perl5-porters@perl.org/msg95385.html
http://archive.develooper.com/perl5-porters@perl.org/msg95387.html

aha! the MacOS X behaviour is a confirmed perl bug -- perl#24122:

http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2003-10/msg00495.html


Anyway. Theo --

perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $< = 1000; $> = 1000; p;'

doesn't work on FreeBSD?  that's whta that spfd thread suggests... if that at
least works on FreeBSD, we can throw in a kludge for that, fixing half of the
problem OSes ;) It may work where "$>=1000; $<=1000;" doesn't, because of the
ordering.

'1) Instead of spamd doing die, it really ought to just throw a loud warning,
and return "fail".  This is, after all, just a rule.'

-1.  In this situation, we do not want the helper apps to run as root. We
should ensure that they do not get to run.  IMO, "die" and it's exception-like
semantics are the best way to do that, rather than risking that a "return 0"
may be lost/ignored along the call stack. Note that the die's are already
caught and do not effect other rules anyway.

However, if the 'throw a loud warning, and return "fail"' idea is implemented
in terms of catching that specific "die" using an eval { } block in the
helper-app running code, and turning it into a prettier error message,
then +1, that'd be fine ;)

However I agree it'll need doco -- probably a top FAQ item.

re: autodetecting -- agreed this doesn't really seem to be possible.  You
can't even detect at install time unless we get the user to run "make test"
as root, and I'm -1 on that idea. ;)

PS: for setuid reference docs:
http://www.cs.berkeley.edu/~hchen/paper/usenix02.html

also some stuff about saved userIDs (argh!):
http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2004-05/msg00699.html

Comment 32 Theo Van Dinter 2004-07-13 12:46:32 UTC
Subject: Re:  perl setuid fails, breaking spamd in FreeBSD, MacOS X

On Tue, Jul 13, 2004 at 11:44:53AM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
> '1) Instead of spamd doing die, it really ought to just throw a loud warning,
> and return "fail".  This is, after all, just a rule.'
> 
> -1.  In this situation, we do not want the helper apps to run as root. We
> should ensure that they do not get to run.  IMO, "die" and it's exception-like
> semantics are the best way to do that, rather than risking that a "return 0"
> may be lost/ignored along the call stack. Note that the die's are already
> caught and do not effect other rules anyway.

um.  right, that's why we would return "fail" and not run the helper
app. ;)  perhaps this wasn't fully clear in my initial statement.

the die() currently happens in a child process, pre running the
helper app.  there's a filehandle between the child and parent for
communication/output from helper app.  so instead of calling die(),
just close the filehandle and exit (we need to exit anyway since we're
a child).  do _not_ run the helper app.  the calling spamd process
will simply get an EOF when trying to read the output, and throw a
"read failed" or whatever, and the rule doesn't trigger.

btw: if I read the ticket correctly, the die() is apparently _NOT_
caught.  that's what causes the sanity check issue via spamc.

> re: autodetecting -- agreed this doesn't really seem to be possible.  You
> can't even detect at install time unless we get the user to run "make test"
> as root, and I'm -1 on that idea. ;)

ditto.

Comment 33 Theo Van Dinter 2004-07-13 13:03:24 UTC
Subject: Re:  perl setuid fails, breaking spamd in FreeBSD, MacOS X

On Tue, Jul 13, 2004 at 11:44:53AM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
> perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $< = 1000; $> = 1000; p;'
> 
> doesn't work on FreeBSD?  that's whta that spfd thread suggests... if that at
> least works on FreeBSD, we can throw in a kludge for that, fixing half of the
> problem OSes ;) It may work where "$>=1000; $<=1000;" doesn't, because of the
> ordering.

Well, "perl -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $<=1000; p;'"
fails on OS X (already in the ticket), but seems to work on FreeBSD:

 RUID: 0, EUID: 0
 RUID: 1000, EUID: 0

The issue here, of course, is that we seteuid() in spamd, then later on
the helper app stuff tries the setuid().  Wait a minute, is this something
stupid where perl checks EUID == 0 for setuid instead of using RUID?

A-ha!  It is!

$ perl5.00503 -e 'sub p {print "RUID: $<, EUID: $>\n";} p; $>=1000; p; $<=1000; p; $>=0; $<=1000; $>=1000;
p;'
RUID: 0, EUID: 0
RUID: 0, EUID: 1000
RUID: 0, EUID: 1000
RUID: 1000, EUID: 1000

ARG!   Ok, so the simple solution, it seems, is:

$>=$<;
$<=$newuid;
$>=$newuid;
if ($<!=$>) { ... }

Comment 34 Sidney Markowitz 2004-07-13 13:07:36 UTC
I emailed Mark Foster, one of the spfd developers from that thread to ask about
what they did. He confirmed that the fix they put in was changing $< = $> = $user to

 $< = $user;
 $> = $user;

"See http://archives.listbox.com/spf-discuss@v2.listbox.com/200312/0408.html
I believe Hans Dieter Pearcey was the one who solved this. The fix was committed
in v1.96."

Theo said it is a red herring since we do it separately anyway, but I see in
spamd.raw in the latest trunk that there is one place that is not done, line 858
has $> = $<;

Could that be the remaining place to fix to work in FreeBSD, and we are stuck in
the case of MacOS X? Can someone with FreeBSD check this out?

Comment 35 Theo Van Dinter 2004-07-16 14:41:03 UTC
Created attachment 2139 [details]
suggested patch

Ok, this patch does the kluge where we change euid back to ruid, then change
ruid, then change euid (again).  As posted in the ticket, this works in the
one-liner test, but I have not tried it via SpamAssassin at all.  I'd
appreciate it if the folks able to reproduce the problem with SA try this patch
out.

Thanks. :)
Comment 36 Sidney Markowitz 2004-07-16 15:13:01 UTC
The patch won't help in MacOS X. We should document somewhere what functionality
will not work in MacOS X with a reference to perl bug -- perl#24122 and the
discussion at
http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2003-10/msg00495.html

Should we create a spamd/README.MacOSX file?

Looking at patch... What about the following code in spamd.raw line 670

  # Change GID
  $) = "$ugid $ugid";    # effective gid
  $( = $ugid;            # real gid

  # Change UID
  $> = $uuid;            # effective uid
  $< = $uuid;            # real uid. we now cannot setuid anymore

and line 1018,1364, 1507, and 1551: Do we know that $< == 0 and $> == 0 at that
point, or do we need some checking? Those three places all say:

    $) = "$gid $gid";                   # eGID
    $> = $uid;                          # eUID

Also, do we need to do anything similar for $) and $( ?



Comment 37 Theo Van Dinter 2004-07-16 20:25:56 UTC
Subject: Re:  [review] perl setuid fails, breaking spamd in FreeBSD, MacOS X

On Fri, Jul 16, 2004 at 03:13:02PM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
> The patch won't help in MacOS X. We should document somewhere what functionality

Ah, good point.  I forgot about that whole "no setuid" thing. <sigh>

> will not work in MacOS X with a reference to perl bug -- perl#24122 and the
> discussion at
> http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2003-10/msg00495.html

Indeed.

> Should we create a spamd/README.MacOSX file?

I'm thinking a Mac OS X section of the spamd/READMe file. ;)

> Looking at patch... What about the following code in spamd.raw line 670
> 
>   # Change GID
>   $) = "$ugid $ugid";    # effective gid
>   $( = $ugid;            # real gid

Hrm.  $) works fine, $( doesn't. :(  stupid mac os x!

>   # Change UID
>   $> = $uuid;            # effective uid
>   $< = $uuid;            # real uid. we now cannot setuid anymore

yeah, we should reverse these.

> and line 1018,1364, 1507, and 1551: Do we know that $< == 0 and $> == 0 at that
> point, or do we need some checking? Those three places all say:
> 
>     $) = "$gid $gid";                   # eGID
>     $> = $uid;                          # eUID

I believe e[ug]id works fine everywhere.

> Also, do we need to do anything similar for $) and $( ?

good question.  anyone have a bsd box we can try this on?

Comment 38 Mike Loiterman 2004-07-16 20:33:34 UTC
Yes.  What do you want me to do...
Comment 39 Theo Van Dinter 2004-07-16 21:43:05 UTC
Subject: Re:  [review] perl setuid fails, breaking spamd in FreeBSD, MacOS X

On Fri, Jul 16, 2004 at 08:33:35PM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
> Yes.  What do you want me to do...

Ok, so first, we should do the gid first, then the uid.  I believe we
do it this way now.

Second, the question at the moment is: Do the platforms that have the
UID issue have the same issue with GID?  If we're still root, as long
as setgid is allowed, it shouldn't matter what the order is.

I think these commands, run as root, will tell us:
perl -e 'sub p {print "RGID: $(, EGID: $)\n";} p; $) = "1000 1000"; $( = 1000; p;'
perl -e 'sub p {print "RGID: $(, EGID: $)\n";} p; $( = 1000; $) = "1000 1000"; p;'
perl -e 'sub p {print "RGID: $(, EGID: $)\n";} p; $) = "1000 1000"; p; $) = $(; p; $( = 1000; $) = "1000 1000"; p;'

The first is the way we do it now.  The second is just reversing the
order of setting.  The third mimics the patch for uid.

The output of both $( and $) are multiple numbers, annoyingly.  The first
number is what really matters though.


Mac OS X: Will not setuid nor setgid.  seteuid and setegid work ok.

# perl -e 'sub p {print "RGID: $(, EGID: $)\n";} p; $) = "1000 1000"; $( = 1000; p;'
RGID: 0 80 31 20 5 4 3 2 1 0, EGID: 0 80 31 20 5 4 3 2 1 0
RGID: 0 1000, EGID: 1000 1000
# perl -e 'sub p {print "RGID: $(, EGID: $)\n";} p; $( = 1000; $) = "1000 1000"; p;'
RGID: 0 80 31 20 5 4 3 2 1 0, EGID: 0 80 31 20 5 4 3 2 1 0
RGID: 0 1000, EGID: 1000 1000
perl -e 'sub p {print "RGID: $(, EGID: $)\n";} p; $) = "1000 1000"; p; $) = $(; p; $( = 1000;
$) = "1000 1000"; p;'
RGID: 0 80 31 20 5 4 3 2 1 0, EGID: 0 80 31 20 5 4 3 2 1 0
RGID: 0 1000, EGID: 1000 1000
RGID: 0 0, EGID: 0 0
RGID: 0 1000, EGID: 1000 1000



Linux, just for comparison:

# perl -e 'sub p {print "RGID: $(, EGID: $)\n";} p; $) = "1000 1000"; $( = 1000; p;'
RGID: 0 100 10 6 4 3 2 1 0, EGID: 0 100 10 6 4 3 2 1 0
RGID: 1000 1000, EGID: 1000 1000
# perl -e 'sub p {print "RGID: $(, EGID: $)\n";} p; $( = 1000; $) = "1000 1000"; p;'
RGID: 0 100 10 6 4 3 2 1 0, EGID: 0 100 10 6 4 3 2 1 0
RGID: 1000 1000, EGID: 1000 1000
# perl -e 'sub p {print "RGID: $(, EGID: $)\n";} p; $) = "1000 1000"; p; $) = $(; p; $( = 1000;
$) = "1000 1000"; p;'
RGID: 0 100 10 6 4 3 2 1 0, EGID: 0 100 10 6 4 3 2 1 0
RGID: 0 1000, EGID: 1000 1000
RGID: 0 1000, EGID: 0 1000
RGID: 1000 1000, EGID: 1000 1000

Comment 40 Mike Loiterman 2004-07-16 22:12:55 UTC
[00:13:06 root@fat_man: /home/mike]# perl -e 'sub p {print "RGID: $(, EGID:
$)\n";} p; $) = "1000 1000"; $( = 1000; p;'
RGID: 0 31 20 5 4 3 2 0 0, EGID: 0 31 20 5 4 3 2 0 0
RGID: 1000 1000, EGID: 1000 1000




[00:13:37 root@fat_man: /home/mike]# perl -e 'sub p {print "RGID: $(, EGID:
$)\n";} p; $( = 1000; $) = "1000 1000"; p;'
RGID: 0 31 20 5 4 3 2 0 0, EGID: 0 31 20 5 4 3 2 0 0
RGID: 1000 1000, EGID: 1000 1000




[00:13:53 root@fat_man: /home/mike]# perl -e 'sub p {print "RGID: $(, EGID:
$)\n";} p; $) = "1000 1000"; p; $) = $(; p; $( = 1000; $) = "1000 1000"; p;'
RGID: 0 31 20 5 4 3 2 0 0, EGID: 0 31 20 5 4 3 2 0 0
RGID: 0 1000, EGID: 1000 1000
RGID: 0 0, EGID: 0 0
RGID: 1000 1000, EGID: 1000 1000


Comment 41 Justin Mason 2004-07-18 22:52:52 UTC
+0.5.  can you change

+    if ($< != $>) {

to be

+    if ($< != $touid) {

just in case $> doesn't change from 0 on the 2nd one for some reason?
Comment 42 Theo Van Dinter 2004-07-19 17:28:41 UTC
Subject: Re:  [review] perl setuid fails, breaking spamd in FreeBSD, MacOS X

On Sun, Jul 18, 2004 at 10:52:53PM -0700, bugzilla-daemon@bugzilla.spamassassin.org wrote:
> +0.5.  can you change
> 
> +    if ($< != $>) {
> 
> to be
> 
> +    if ($< != $touid) {
> 
> just in case $> doesn't change from 0 on the 2nd one for some reason?

sure.  although, the euid will change (at least in every test we tried),
so $< won't == $> in that situation.

actually, I'll add a little more logic to it, since this stupidity
doesn't have to be done for most platforms.

Comment 43 Theo Van Dinter 2004-07-19 18:37:30 UTC
Created attachment 2147 [details]
new version
Comment 44 Justin Mason 2004-07-19 18:44:05 UTC
+1 -- I like the extra sanity checking. ;)
Comment 45 Michael Parker 2004-07-22 19:50:44 UTC
+1
Comment 46 Theo Van Dinter 2004-07-22 20:08:43 UTC
committed.  r23168