Logo Search packages:      
Sourcecode: batv-milter version File versions  Download package

mpeix.c

/*
 * Copyright (c) 2001-2002 Sendmail, Inc. and its suppliers.
 *    All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 */

#include <sm/gen.h>
SM_RCSID("@(#)$Id: mpeix.c,v 1.7 2007/05/11 18:51:22 gshapiro Exp $")

#ifdef MPE
/*
**    MPE lacks many common functions required across all sendmail programs
**    so we define implementations for these functions here.
*/

# include <errno.h>
# include <fcntl.h>
# include <limits.h>
# include <mpe.h>
# include <netinet/in.h>
# include <pwd.h>
# include <sys/socket.h>
# include <sys/stat.h>
# include <unistd.h>
# include <sm/conf.h>

/*
**  CHROOT -- dummy chroot() function
**
**    The MPE documentation for sendmail says that chroot-based
**    functionality is not implemented because MPE lacks chroot.  But
**    rather than mucking around with all the sendmail calls to chroot,
**    we define this dummy function to return an ENOSYS failure just in
**    case a sendmail user attempts to enable chroot-based functionality.
**
**    Parameters:
**          path -- pathname of new root (ignored).
**
**    Returns:
**          -1 and errno == ENOSYS (function not implemented)
*/

int
chroot(path)
      char *path;
{
      errno = ENOSYS;
      return -1;
}

/*
**  ENDPWENT -- dummy endpwent() function
**
**    Parameters:
**          none
**
**    Returns:
**          none
*/

void
endpwent()
{
      return;
}

/*
**  In addition to missing functions, certain existing MPE functions have
**  slightly different semantics (or bugs) compared to normal Unix OSes.
**
**  Here we define wrappers for these functions to make them behave in the
**  manner expected by sendmail.
*/

/*
**  SENDMAIL_MPE_BIND -- shadow function for the standard socket bind()
**
**    MPE requires GETPRIVMODE() for AF_INET sockets less than port 1024.
**
**    Parameters:
**          sd -- socket descriptor.
**          addr -- socket address.
**          addrlen -- length of socket address.
**
**    Results:
**          0 -- success
**          != 0 -- failure
*/

#undef bind
int
sendmail_mpe_bind(sd, addr, addrlen)
      int sd;
      void *addr;
      int addrlen;
{
      bool priv = false;
      int result;
      extern void GETPRIVMODE __P((void));
      extern void GETUSERMODE __P((void));

      if (addrlen == sizeof(struct sockaddr_in) &&
          ((struct sockaddr_in *)addr)->sin_family == AF_INET)
      {
            /* AF_INET */
            if (((struct sockaddr_in *)addr)->sin_port > 0 &&
                ((struct sockaddr_in *)addr)->sin_port < 1024)
            {
                  priv = true;
                  GETPRIVMODE();
            }
            ((struct sockaddr_in *)addr)->sin_addr.s_addr = 0;
            result = bind(sd, addr, addrlen);
            if (priv)
                  GETUSERMODE();
            return result;
      }

      /* AF_UNIX */
      return bind(sd, addr, addrlen);
}

/*
**  SENDMAIL_MPE__EXIT -- wait for children to terminate, then _exit()
**
**    Child processes cannot survive the death of their parent on MPE, so
**    we must call wait() before _exit() in order to prevent this
**    infanticide.
**
**    Parameters:
**          status -- _exit status value.
**
**    Returns:
**          none.
*/

#undef _exit
void
sendmail_mpe__exit(status)
      int status;
{
      int result;

      /* Wait for all children to terminate. */
      do
      {
            result = wait(NULL);
      } while (result > 0 || errno == EINTR);
      _exit(status);
}

/*
**  SENDMAIL_MPE_EXIT -- wait for children to terminate, then exit()
**
**    Child processes cannot survive the death of their parent on MPE, so
**    we must call wait() before exit() in order to prevent this
**    infanticide.
**
**    Parameters:
**          status -- exit status value.
**
**    Returns:
**          none.
*/

#undef exit
void
sendmail_mpe_exit(status)
      int status;
{
      int result;

      /* Wait for all children to terminate. */
      do
      {
            result = wait(NULL);
      } while (result > 0 || errno == EINTR);
      exit(status);
}

/*
**  SENDMAIL_MPE_FCNTL -- shadow function for fcntl()
**
**    MPE requires sfcntl() for sockets, and fcntl() for everything
**    else.  This shadow routine determines the descriptor type and
**    makes the appropriate call.
**
**    Parameters:
**          same as fcntl().
**
**    Returns:
**          same as fcntl().
*/

#undef fcntl
int
sendmail_mpe_fcntl(int fildes, int cmd, ...)
{
      int len, result;
      struct sockaddr sa;

      void *arg;
      va_list ap;

      va_start(ap, cmd);
      arg = va_arg(ap, void *);
      va_end(ap);

      len = sizeof sa;
      if (getsockname(fildes, &sa, &len) == -1)
      {
            if (errno == EAFNOSUPPORT)
            {
                  /* AF_UNIX socket */
                  return sfcntl(fildes, cmd, arg);
            }
            else if (errno == ENOTSOCK)
            {
                  /* file or pipe */
                  return fcntl(fildes, cmd, arg);
            }

            /* unknown getsockname() failure */
            return (-1);
      }
      else
      {
            /* AF_INET socket */
            if ((result = sfcntl(fildes, cmd, arg)) != -1 &&
                cmd == F_GETFL)
                  result |= O_RDWR;  /* fill in some missing flags */
            return result;
      }
}

/*
**  SENDMAIL_MPE_GETPWNAM - shadow function for getpwnam()
**
**    Several issues apply here:
**
**    - MPE user names MUST have one '.' separator character
**    - MPE user names MUST be in upper case
**    - MPE does not initialize all fields in the passwd struct
**
**    Parameters:
**          name -- username string.
**
**    Returns:
**          pointer to struct passwd if found else NULL
*/

static char *sendmail_mpe_nullstr = "";

#undef getpwnam
extern struct passwd *getpwnam(const char *);

struct passwd *
sendmail_mpe_getpwnam(name)
      const char *name;
{
      int dots = 0;
      int err;
      int i = strlen(name);
      char *upper;
      struct passwd *result = NULL;

      if (i <= 0)
      {
            errno = EINVAL;
            return result;
      }

      if ((upper = (char *)malloc(i + 1)) != NULL)
      {
            /* upshift the username parameter and count the dots */
            while (i >= 0)
            {
                  if (name[i] == '.')
                  {
                        dots++;
                        upper[i] = '.';
                  }
                  else
                        upper[i] = toupper(name[i]);
                  i--;
            }

            if (dots != 1)
            {
                  /* prevent bug when dots == 0 */
                  err = EINVAL;
            }
            else if ((result = getpwnam(upper)) != NULL)
            {
                  /* init the uninitialized fields */
                  result->pw_gecos = sendmail_mpe_nullstr;
                  result->pw_passwd = sendmail_mpe_nullstr;
                  result->pw_age = sendmail_mpe_nullstr;
                  result->pw_comment = sendmail_mpe_nullstr;
                  result->pw_audid = 0;
                  result->pw_audflg = 0;
            }
            err = errno;
            free(upper);
      }
      errno = err;
      return result;
}

/*
**  SENDMAIL_MPE_GETPWUID -- shadow function for getpwuid()
**
**    Initializes the uninitalized fields in the passwd struct.
**
**    Parameters:
**          uid -- uid to obtain passwd data for
**
**    Returns:
**          pointer to struct passwd or NULL if not found
*/

#undef getpwuid
extern struct passwd *getpwuid __P((uid_t));

struct passwd *
sendmail_mpe_getpwuid(uid)
      uid_t uid;
{
      struct passwd *result;

      if ((result = getpwuid(uid)) != NULL)
      {
            /* initialize the uninitialized fields */
            result->pw_gecos = sendmail_mpe_nullstr;
            result->pw_passwd = sendmail_mpe_nullstr;
            result->pw_age = sendmail_mpe_nullstr;
            result->pw_comment = sendmail_mpe_nullstr;
            result->pw_audid = 0;
            result->pw_audflg = 0;
      }
      return result;
}

/*
**  OK boys and girls, time for some serious voodoo!
**
**  MPE does not have a complete implementation of POSIX users and groups:
**
**  - there is no uid 0 superuser
**  - setuid/setgid file permission bits exist but have no-op functionality
**  - setgid() exists, but only supports new gid == current gid (boring!)
**  - setuid() forces a gid change to the new uid's primary (and only) gid
**
**  ...all of which thoroughly annoys sendmail.
**
**  So what to do?  We can't go on an #ifdef MPE rampage throughout
**  sendmail, because there are only about a zillion references to uid 0
**  and so success (and security) would probably be rather dubious by the
**  time we finished.
**
**  Instead we take the approach of defining wrapper functions for the
**  gid/uid management functions getegid(), geteuid(), setgid(), and
**  setuid() in order to implement the following model:
**
**  - the sendmail program thinks it is a setuid-root (uid 0) program
**  - uid 0 is recognized as being valid, but does not grant extra powers
**    - MPE priv mode allows sendmail to call setuid(), not uid 0
**    - file access is still controlled by the real non-zero uid
**  - the other programs (vacation, etc) have standard MPE POSIX behavior
**
**  This emulation model is activated by use of the program file setgid and
**  setuid mode bits which exist but are unused by MPE.  If the setgid mode
**  bit is on, then gid emulation will be enabled.  If the setuid mode bit is
**  on, then uid emulation will be enabled.  So for the mail daemon, we need
**  to do chmod u+s,g+s /SENDMAIL/CURRENT/SENDMAIL.
**
**  The following flags determine the current emulation state:
**
**  true == emulation enabled
**  false == emulation disabled, use unmodified MPE semantics
*/

static bool sendmail_mpe_flaginit = false;
static bool sendmail_mpe_gidflag = false;
static bool sendmail_mpe_uidflag = false;

/*
**  SENDMAIL_MPE_GETMODE -- return the mode bits for the current process
**
**    Parameters:
**          none.
**
**    Returns:
**          file mode bits for the current process program file.
*/

mode_t
sendmail_mpe_getmode()
{
      int status = 666;
      int myprogram_length;
      int myprogram_syntax = 2;
      char formaldesig[28];
      char myprogram[PATH_MAX + 2];
      char path[PATH_MAX + 1];
      struct stat st;
      extern HPMYPROGRAM __P((int parms, char *formaldesig, int *status,
                        int *length, char *myprogram,
                        int *myprogram_length, int *myprogram_syntax));

      myprogram_length = sizeof(myprogram);
      HPMYPROGRAM(6, formaldesig, &status, NULL, myprogram,
                &myprogram_length, &myprogram_syntax);

      /* should not occur, do not attempt emulation */
      if (status != 0)
            return 0;

      memcpy(&path, &myprogram[1], myprogram_length - 2);
      path[myprogram_length - 2] = '\0';

      /* should not occur, do not attempt emulation */
      if (stat(path, &st) < 0)
            return 0;

      return st.st_mode;
}

/*
**  SENDMAIL_MPE_EMULGID -- should we perform gid emulation?
**
**    If !sendmail_mpe_flaginit then obtain the mode bits to determine
**    if the setgid bit is on, we want gid emulation and so set
**    sendmail_mpe_gidflag to true.  Otherwise we do not want gid emulation
**    and so set sendmail_mpe_gidflag to false.
**
**    Parameters:
**          none.
**
**    Returns:
**          true -- perform gid emulation
**          false -- do not perform gid emulation
*/

bool
sendmail_mpe_emulgid()
{
      if (!sendmail_mpe_flaginit)
      {
            mode_t mode;

            mode = sendmail_mpe_getmode();
            sendmail_mpe_gidflag = ((mode & S_ISGID) == S_ISGID);
            sendmail_mpe_uidflag = ((mode & S_ISUID) == S_ISUID);
            sendmail_mpe_flaginit = true;
      }
      return sendmail_mpe_gidflag;
}

/*
**  SENDMAIL_MPE_EMULUID -- should we perform uid emulation?
**
**    If sendmail_mpe_uidflag == -1 then obtain the mode bits to determine
**    if the setuid bit is on, we want uid emulation and so set
**    sendmail_mpe_uidflag to true.  Otherwise we do not want uid emulation
**    and so set sendmail_mpe_uidflag to false.
**
**    Parameters:
**          none.
**
**    Returns:
**          true -- perform uid emulation
**          false -- do not perform uid emulation
*/

bool
sendmail_mpe_emuluid()
{
      if (!sendmail_mpe_flaginit)
      {
            mode_t mode;

            mode = sendmail_mpe_getmode();
            sendmail_mpe_gidflag = ((mode & S_ISGID) == S_ISGID);
            sendmail_mpe_uidflag = ((mode & S_ISUID) == S_ISUID);
            sendmail_mpe_flaginit = true;
      }
      return sendmail_mpe_uidflag;
}

/*
**  SENDMAIL_MPE_GETEGID -- shadow function for getegid()
**
**    If emulation mode is in effect and the saved egid has been
**    initialized, return the saved egid; otherwise return the value of the
**    real getegid() function.
**
**    Parameters:
**          none.
**
**    Returns:
**          emulated egid if present, else true egid.
*/

static gid_t sendmail_mpe_egid = -1;

#undef getegid
gid_t
sendmail_mpe_getegid()
{
      if (sendmail_mpe_emulgid() && sendmail_mpe_egid != -1)
            return sendmail_mpe_egid;
      return getegid();
}

/*
**  SENDMAIL_MPE_GETEUID -- shadow function for geteuid()
**
**    If emulation mode is in effect, return the saved euid; otherwise
**    return the value of the real geteuid() function.
**
**    Note that the initial value of the saved euid is zero, to simulate
**    a setuid-root program.
**
**    Parameters:
**          none
**
**    Returns:
**          emulated euid if in emulation mode, else true euid.
*/

static uid_t sendmail_mpe_euid = 0;

#undef geteuid
uid_t
sendmail_mpe_geteuid()
{
      if (sendmail_mpe_emuluid())
            return sendmail_mpe_euid;
      return geteuid();
}

/*
**  SENDMAIL_MPE_SETGID -- shadow function for setgid()
**
**    Simulate a call to setgid() without actually calling the real
**    function.  Implement the expected uid 0 semantics.
**
**    Note that sendmail will also be calling setuid() which will force an
**    implicit real setgid() to the proper primary gid.  So it doesn't matter
**    that we don't actually alter the real gid in this shadow function.
**
**    Parameters:
**          gid -- desired gid.
**
**    Returns:
**          0 -- emulated success
**          -1 -- emulated failure
*/

#undef setgid
int
sendmail_mpe_setgid(gid)
      gid_t gid;
{
      if (sendmail_mpe_emulgid())
      {
            if (gid == getgid() || sendmail_mpe_euid == 0)
            {
                  sendmail_mpe_egid = gid;
                  return 0;
            }
            errno = EINVAL;
            return -1;
      }
      return setgid(gid);
}

/*
**  SENDMAIL_MPE_SETUID -- shadow function for setuid()
**
**    setuid() is broken as of MPE 7.0 in that it changes the current
**    working directory to be the home directory of the new uid.  Thus
**    we must obtain the cwd and restore it after the setuid().
**
**    Note that expected uid 0 semantics have been added, as well as
**    remembering the new uid for later use by the other shadow functions.
**
**    Parameters:
**          uid -- desired uid.
**
**    Returns:
**          0 -- success
**          -1 -- failure
**
**    Globals:
**          sendmail_mpe_euid
*/

#undef setuid
int
sendmail_mpe_setuid(uid)
      uid_t uid;
{
      char *cwd;
      char cwd_buf[PATH_MAX + 1];
      int result;
      extern void GETPRIVMODE __P((void));
      extern void GETUSERMODE __P((void));

      if (sendmail_mpe_emuluid())
      {
            if (uid == 0)
            {
                  if (sendmail_mpe_euid != 0)
                  {
                        errno = EINVAL;
                        return -1;
                  }
                  sendmail_mpe_euid = 0;
                  return 0;
            }

            /* Preserve the current working directory */
            if ((cwd = getcwd(cwd_buf, PATH_MAX + 1)) == NULL)
                  return -1;

            GETPRIVMODE();
            result = setuid(uid);
            GETUSERMODE();

            /* Restore the current working directory */
            chdir(cwd_buf);

            if (result == 0)
                  sendmail_mpe_euid = uid;

            return result;
      }
      return setuid(uid);
}
#endif /* MPE */

Generated by  Doxygen 1.6.0   Back to index