diff options
Diffstat (limited to 'sendmail/libsm/mpeix.c')
-rw-r--r-- | sendmail/libsm/mpeix.c | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/sendmail/libsm/mpeix.c b/sendmail/libsm/mpeix.c new file mode 100644 index 0000000..163dfba --- /dev/null +++ b/sendmail/libsm/mpeix.c @@ -0,0 +1,646 @@ +/* + * 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 */ |