/* * 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_RCSID("@(#)$Id: mpeix.c,v 1.6 2002/05/24 20:50:14 gshapiro Exp $") #ifdef MPE /* ** MPE lacks many common functions required across all sendmail programs ** so we define implementations for these functions here. */ # include # include # include # include # include # include # include # include # include # include /* ** 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 */