summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/libsm/mpeix.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sendmail/libsm/mpeix.c')
-rw-r--r--contrib/sendmail/libsm/mpeix.c646
1 files changed, 646 insertions, 0 deletions
diff --git a/contrib/sendmail/libsm/mpeix.c b/contrib/sendmail/libsm/mpeix.c
new file mode 100644
index 0000000..d0767b5
--- /dev/null
+++ b/contrib/sendmail/libsm/mpeix.c
@@ -0,0 +1,646 @@
+/*
+ * Copyright (c) 2001 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.4 2001/12/14 23:54:18 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 uid_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 */
OpenPOWER on IntegriCloud