1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-26 10:04:02 +03:00

AIX patch from Michael Wojcik <mww@microfocus.com> adding detail

to the AIX specific (and undocumented) setpriv and setuidx calls.
Jeremy.
This commit is contained in:
Jeremy Allison -
parent 4d26757776
commit 7a8d0a4ed4

View File

@ -74,15 +74,124 @@ static BOOL become_uid(int uid)
#ifdef AIX
{
/* AIX 3 stuff - inspired by a code fragment in wu-ftpd */
/* MWW: This is all undocumented, of course. There's a patch to WU-ftpd
in the AIX FAQ which does the setpriv, then sets the gid stuff, then
sets uid. Since Samba separates setting the gid and setting the uid,
I've duplicated the setpriv code in become_gid. I've also made the
requisite changes to become_gid to match the WU-ftpd patch.
I believe we'll still get errors in the Samba logs. This setpriv
call is supposed to disable "trapdooring" on AIX - ie. normally
once a seteuid / setegid is done, the effective ID can't be set back
to what it was before. See the comments in become_root / unbecome_root.
I *think* that we may have to do something additional here to prevent
the "Can't set uid (AIX3)" messages, though - possibly change the
values of priv.pv_priv to keep the SET_PROC_DAC privilege, and
possibly SET_OBJ_DAC and SET_OBJ_STAT as well.
The pv_priv array is two longwords, and the constants in sys/priv.h
have values between 1 and 64, according to the comments in priv.h.
This strongly suggests a bit vector - but does BYPASS_DAC_WRITE
(#define'd to 1) mean 1<<0 or 1<<1? Unfortunately, nothing's
defined to 0 or 64, which would be a dead giveaway. Also, what's the
fullword-boundary endianness? That is, is pv_priv[0] the high or
the low 32 bits? Fortunately, the values used by "su" (see below)
don't make much sense if pv_priv[0] is the high bits. Also, based
on analysis of the values used by su, I concluded that, for example,
BYPASS_DAC_READ (defined to 2) is bit "2" counting from 1 - ie.
if (pv_priv[0] & (1 << (BYPASS_DAC_READ - 1))) then BYPASS_DAC_READ
is on. That's a bit odd, but it makes more sense than if the
privilege constants are meant to be taken as exponents of 2.
For analysis, I ran "su" as root under dbx, and stopped in setpriv.
The first argument to setpriv can be examined using
print $r3 (eg. "0x30009" = PRIV_SET|PRIV_MAXIMUM|PRIV_EFFECTIV)
the contents of the pv_priv array can be examined using
($r4)/2X
Here's what su does:
setpriv(PRIV_SET | PRIV_INHERITED | PRIV_BEQUEATH, {0,0})
setpriv(PRIV_SET | PRIV_EFFECTIVE | PRIV_MAXIMUM,
{0x02800006, 0x00040000})
0x02800006 = SET_PROC_AUDIT | SET_PROC_ENV |
BYPASS_DAC_EXEC | BYPASS_DAC_READ
0x00040000 = TPATH_CONFIG
setpriv(PRIV_SET | PRIV_EFFECTIVE, {0, 0})
Analysis:
Reduce inherited privileges to none, so the child doesn't inherit
anything special.
Change su's privileges so it can execute the shell even if someone
has goofed up the permissions to it or to directories on the
search path; so it can set the process auditing characteristics
and privileged environment (stuff in /etc/security/environ with
the sysenv attribute); and so that it can set the trusted path
characteristics for this login.
Zap those privileges back off when we don't need them any more.
I'm guessing we want to keep SET_PROC_DAC in the current priv set,
but not in the inherited one. That is, set PRIV_INHERITED and
PRIV_BEQUEATH to 0. We also probably want to set PRIV_MAXIMUM and
PRIV_EFFECTIVE to only the privs we need, which at this point would
appear to be just SET_PROC_DAC. *Note*: setting PRIV_MAXIMUM
with any of the privilege sets higher than what you're trying to
set the maximum to will produce an EINVAL. For example, if we
try to set PRIV_MAXIMUM to SET_PROC_DAC *before* we reduce
PRIV_INHERITED and PRIV_BEQUEATH, it won't work. Zero out the
inherited privileges first.
Some experimentation with simple programs confirms that if we're
running with an EUID of 0 we can switch our UID/EUID back and
forth with setuidx - *unless* we call setpriv({0,0}, ...) first.
In other words, by default root has SET_PROC_DAT set, but we can
remove it from our privilege set. This is what we want to do for
child processes, I believe.
Also, calling setpriv(PRIV_SUB|PRIV_EFFECTIVE,...) with pv_priv[0]
set to SET_PROC_DAC (1 << (SET_PROC_DAC - 1)) will prevent an
EUID-root process from switching its EUID back with setuidx.
In other words, setuidx on AIX is *not* trapdoor. setuid is
trapdoor. We need a non-trapdoor setuid function, but we don't
want processes we fork to have access to it. Thus we use setuidx
but first we disable it for our children.
Note, however, that we can only increase our privileges (as we
do in the first call to setpriv) if we're EUID-root. If we
started out as root, and then switched to a non-root user ID,
that's OK; we've already set them. Just don't try to set them
again.
Also, I suspect that after using setpriv / setuidx / etc. here in
the AIX-specific code we DON'T want to fall through to the code that
calls setuid, etc. However, I haven't noticed any more problems with
the code the way it is here.
*/
priv_t priv;
priv.pv_priv[0] = 0;
priv.pv_priv[1] = 0;
if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
&priv, sizeof(priv_t)) < 0 ||
setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)uid) < 0 ||
seteuid((uid_t)uid) < 0)
if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_BEQUEATH,
&priv, sizeof(priv_t)) < 0) {
DEBUG(1, ("Can't set child privileges (AIX3): %s\n", strerror(errno)));
}
priv.pv_priv[0] = (1 << (SET_PROC_DAC - 1));
if (setpriv(PRIV_SET|PRIV_EFFECTIVE|PRIV_MAXIMUM,
&priv, sizeof(priv_t)) < 0) {
DEBUG(1, ("Can't set own privileges (AIX3): %s\n", strerror(errno)));
}
if (setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)uid) < 0 ||
seteuid((uid_t)uid) < 0) {
DEBUG(1,("Can't set uid (AIX3)\n"));
}
}
#endif
@ -125,6 +234,24 @@ static BOOL become_gid(int gid)
DEBUG(1,("WARNING: using gid %d is a security risk\n",gid));
}
#ifdef AIX
{
/* MWW: See comment above in become_uid. */
priv_t priv;
priv.pv_priv[0] = 0;
priv.pv_priv[1] = 0;
if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
&priv, sizeof(priv_t)) < 0) {
DEBUG(1, ("Can't set privilege (AIX3)\n"));
}
if (setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)gid) < 0 ||
setegid((gid_t)gid) < 0) {
DEBUG(1,("Can't set gid (AIX3)\n"));
}
}
#endif
#ifdef USE_SETRES
if (setresgid(-1,gid,-1) != 0)
#elif defined(USE_SETFS)
@ -169,6 +296,10 @@ BOOL become_guest(void)
pass = Get_Pwnam(lp_guestaccount(-1),True);
if (!pass) return(False);
#ifdef AIX
/* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before setting IDs */
initgroups(pass->pw_name, (gid_t)pass->pw_gid);
#endif
ret = become_id(pass->pw_uid,pass->pw_gid);
if (!ret)