2 * Copyright (c) 2001-2002 Sendmail, Inc. and its suppliers.
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
12 SM_RCSID("@(#)$Id: mpeix.c,v 1.7 2007/05/11 18:51:22 gshapiro Exp $")
16 ** MPE lacks many common functions required across all sendmail programs
17 ** so we define implementations for these functions here.
24 # include <netinet/in.h>
26 # include <sys/socket.h>
27 # include <sys/stat.h>
32 ** CHROOT -- dummy chroot() function
34 ** The MPE documentation for sendmail says that chroot-based
35 ** functionality is not implemented because MPE lacks chroot. But
36 ** rather than mucking around with all the sendmail calls to chroot,
37 ** we define this dummy function to return an ENOSYS failure just in
38 ** case a sendmail user attempts to enable chroot-based functionality.
41 ** path -- pathname of new root (ignored).
44 ** -1 and errno == ENOSYS (function not implemented)
56 ** ENDPWENT -- dummy endpwent() function
72 ** In addition to missing functions, certain existing MPE functions have
73 ** slightly different semantics (or bugs) compared to normal Unix OSes.
75 ** Here we define wrappers for these functions to make them behave in the
76 ** manner expected by sendmail.
80 ** SENDMAIL_MPE_BIND -- shadow function for the standard socket bind()
82 ** MPE requires GETPRIVMODE() for AF_INET sockets less than port 1024.
85 ** sd -- socket descriptor.
86 ** addr -- socket address.
87 ** addrlen -- length of socket address.
96 sendmail_mpe_bind(sd
, addr
, addrlen
)
103 extern void GETPRIVMODE
__P((void));
104 extern void GETUSERMODE
__P((void));
106 if (addrlen
== sizeof(struct sockaddr_in
) &&
107 ((struct sockaddr_in
*)addr
)->sin_family
== AF_INET
)
110 if (((struct sockaddr_in
*)addr
)->sin_port
> 0 &&
111 ((struct sockaddr_in
*)addr
)->sin_port
< 1024)
116 ((struct sockaddr_in
*)addr
)->sin_addr
.s_addr
= 0;
117 result
= bind(sd
, addr
, addrlen
);
124 return bind(sd
, addr
, addrlen
);
128 ** SENDMAIL_MPE__EXIT -- wait for children to terminate, then _exit()
130 ** Child processes cannot survive the death of their parent on MPE, so
131 ** we must call wait() before _exit() in order to prevent this
135 ** status -- _exit status value.
143 sendmail_mpe__exit(status
)
148 /* Wait for all children to terminate. */
152 } while (result
> 0 || errno
== EINTR
);
157 ** SENDMAIL_MPE_EXIT -- wait for children to terminate, then exit()
159 ** Child processes cannot survive the death of their parent on MPE, so
160 ** we must call wait() before exit() in order to prevent this
164 ** status -- exit status value.
172 sendmail_mpe_exit(status
)
177 /* Wait for all children to terminate. */
181 } while (result
> 0 || errno
== EINTR
);
186 ** SENDMAIL_MPE_FCNTL -- shadow function for fcntl()
188 ** MPE requires sfcntl() for sockets, and fcntl() for everything
189 ** else. This shadow routine determines the descriptor type and
190 ** makes the appropriate call.
201 sendmail_mpe_fcntl(int fildes
, int cmd
, ...)
210 arg
= va_arg(ap
, void *);
214 if (getsockname(fildes
, &sa
, &len
) == -1)
216 if (errno
== EAFNOSUPPORT
)
219 return sfcntl(fildes
, cmd
, arg
);
221 else if (errno
== ENOTSOCK
)
224 return fcntl(fildes
, cmd
, arg
);
227 /* unknown getsockname() failure */
233 if ((result
= sfcntl(fildes
, cmd
, arg
)) != -1 &&
235 result
|= O_RDWR
; /* fill in some missing flags */
241 ** SENDMAIL_MPE_GETPWNAM - shadow function for getpwnam()
243 ** Several issues apply here:
245 ** - MPE user names MUST have one '.' separator character
246 ** - MPE user names MUST be in upper case
247 ** - MPE does not initialize all fields in the passwd struct
250 ** name -- username string.
253 ** pointer to struct passwd if found else NULL
256 static char *sendmail_mpe_nullstr
= "";
259 extern struct passwd
*getpwnam(const char *);
262 sendmail_mpe_getpwnam(name
)
267 int i
= strlen(name
);
269 struct passwd
*result
= NULL
;
277 if ((upper
= (char *)malloc(i
+ 1)) != NULL
)
279 /* upshift the username parameter and count the dots */
288 upper
[i
] = toupper(name
[i
]);
294 /* prevent bug when dots == 0 */
297 else if ((result
= getpwnam(upper
)) != NULL
)
299 /* init the uninitialized fields */
300 result
->pw_gecos
= sendmail_mpe_nullstr
;
301 result
->pw_passwd
= sendmail_mpe_nullstr
;
302 result
->pw_age
= sendmail_mpe_nullstr
;
303 result
->pw_comment
= sendmail_mpe_nullstr
;
304 result
->pw_audid
= 0;
305 result
->pw_audflg
= 0;
315 ** SENDMAIL_MPE_GETPWUID -- shadow function for getpwuid()
317 ** Initializes the uninitalized fields in the passwd struct.
320 ** uid -- uid to obtain passwd data for
323 ** pointer to struct passwd or NULL if not found
327 extern struct passwd
*getpwuid
__P((uid_t
));
330 sendmail_mpe_getpwuid(uid
)
333 struct passwd
*result
;
335 if ((result
= getpwuid(uid
)) != NULL
)
337 /* initialize the uninitialized fields */
338 result
->pw_gecos
= sendmail_mpe_nullstr
;
339 result
->pw_passwd
= sendmail_mpe_nullstr
;
340 result
->pw_age
= sendmail_mpe_nullstr
;
341 result
->pw_comment
= sendmail_mpe_nullstr
;
342 result
->pw_audid
= 0;
343 result
->pw_audflg
= 0;
349 ** OK boys and girls, time for some serious voodoo!
351 ** MPE does not have a complete implementation of POSIX users and groups:
353 ** - there is no uid 0 superuser
354 ** - setuid/setgid file permission bits exist but have no-op functionality
355 ** - setgid() exists, but only supports new gid == current gid (boring!)
356 ** - setuid() forces a gid change to the new uid's primary (and only) gid
358 ** ...all of which thoroughly annoys sendmail.
360 ** So what to do? We can't go on an #ifdef MPE rampage throughout
361 ** sendmail, because there are only about a zillion references to uid 0
362 ** and so success (and security) would probably be rather dubious by the
365 ** Instead we take the approach of defining wrapper functions for the
366 ** gid/uid management functions getegid(), geteuid(), setgid(), and
367 ** setuid() in order to implement the following model:
369 ** - the sendmail program thinks it is a setuid-root (uid 0) program
370 ** - uid 0 is recognized as being valid, but does not grant extra powers
371 ** - MPE priv mode allows sendmail to call setuid(), not uid 0
372 ** - file access is still controlled by the real non-zero uid
373 ** - the other programs (vacation, etc) have standard MPE POSIX behavior
375 ** This emulation model is activated by use of the program file setgid and
376 ** setuid mode bits which exist but are unused by MPE. If the setgid mode
377 ** bit is on, then gid emulation will be enabled. If the setuid mode bit is
378 ** on, then uid emulation will be enabled. So for the mail daemon, we need
379 ** to do chmod u+s,g+s /SENDMAIL/CURRENT/SENDMAIL.
381 ** The following flags determine the current emulation state:
383 ** true == emulation enabled
384 ** false == emulation disabled, use unmodified MPE semantics
387 static bool sendmail_mpe_flaginit
= false;
388 static bool sendmail_mpe_gidflag
= false;
389 static bool sendmail_mpe_uidflag
= false;
392 ** SENDMAIL_MPE_GETMODE -- return the mode bits for the current process
398 ** file mode bits for the current process program file.
402 sendmail_mpe_getmode()
405 int myprogram_length
;
406 int myprogram_syntax
= 2;
407 char formaldesig
[28];
408 char myprogram
[PATH_MAX
+ 2];
409 char path
[PATH_MAX
+ 1];
411 extern HPMYPROGRAM
__P((int parms
, char *formaldesig
, int *status
,
412 int *length
, char *myprogram
,
413 int *myprogram_length
, int *myprogram_syntax
));
415 myprogram_length
= sizeof(myprogram
);
416 HPMYPROGRAM(6, formaldesig
, &status
, NULL
, myprogram
,
417 &myprogram_length
, &myprogram_syntax
);
419 /* should not occur, do not attempt emulation */
423 memcpy(&path
, &myprogram
[1], myprogram_length
- 2);
424 path
[myprogram_length
- 2] = '\0';
426 /* should not occur, do not attempt emulation */
427 if (stat(path
, &st
) < 0)
434 ** SENDMAIL_MPE_EMULGID -- should we perform gid emulation?
436 ** If !sendmail_mpe_flaginit then obtain the mode bits to determine
437 ** if the setgid bit is on, we want gid emulation and so set
438 ** sendmail_mpe_gidflag to true. Otherwise we do not want gid emulation
439 ** and so set sendmail_mpe_gidflag to false.
445 ** true -- perform gid emulation
446 ** false -- do not perform gid emulation
450 sendmail_mpe_emulgid()
452 if (!sendmail_mpe_flaginit
)
456 mode
= sendmail_mpe_getmode();
457 sendmail_mpe_gidflag
= ((mode
& S_ISGID
) == S_ISGID
);
458 sendmail_mpe_uidflag
= ((mode
& S_ISUID
) == S_ISUID
);
459 sendmail_mpe_flaginit
= true;
461 return sendmail_mpe_gidflag
;
465 ** SENDMAIL_MPE_EMULUID -- should we perform uid emulation?
467 ** If sendmail_mpe_uidflag == -1 then obtain the mode bits to determine
468 ** if the setuid bit is on, we want uid emulation and so set
469 ** sendmail_mpe_uidflag to true. Otherwise we do not want uid emulation
470 ** and so set sendmail_mpe_uidflag to false.
476 ** true -- perform uid emulation
477 ** false -- do not perform uid emulation
481 sendmail_mpe_emuluid()
483 if (!sendmail_mpe_flaginit
)
487 mode
= sendmail_mpe_getmode();
488 sendmail_mpe_gidflag
= ((mode
& S_ISGID
) == S_ISGID
);
489 sendmail_mpe_uidflag
= ((mode
& S_ISUID
) == S_ISUID
);
490 sendmail_mpe_flaginit
= true;
492 return sendmail_mpe_uidflag
;
496 ** SENDMAIL_MPE_GETEGID -- shadow function for getegid()
498 ** If emulation mode is in effect and the saved egid has been
499 ** initialized, return the saved egid; otherwise return the value of the
500 ** real getegid() function.
506 ** emulated egid if present, else true egid.
509 static gid_t sendmail_mpe_egid
= -1;
513 sendmail_mpe_getegid()
515 if (sendmail_mpe_emulgid() && sendmail_mpe_egid
!= -1)
516 return sendmail_mpe_egid
;
521 ** SENDMAIL_MPE_GETEUID -- shadow function for geteuid()
523 ** If emulation mode is in effect, return the saved euid; otherwise
524 ** return the value of the real geteuid() function.
526 ** Note that the initial value of the saved euid is zero, to simulate
527 ** a setuid-root program.
533 ** emulated euid if in emulation mode, else true euid.
536 static uid_t sendmail_mpe_euid
= 0;
540 sendmail_mpe_geteuid()
542 if (sendmail_mpe_emuluid())
543 return sendmail_mpe_euid
;
548 ** SENDMAIL_MPE_SETGID -- shadow function for setgid()
550 ** Simulate a call to setgid() without actually calling the real
551 ** function. Implement the expected uid 0 semantics.
553 ** Note that sendmail will also be calling setuid() which will force an
554 ** implicit real setgid() to the proper primary gid. So it doesn't matter
555 ** that we don't actually alter the real gid in this shadow function.
558 ** gid -- desired gid.
561 ** 0 -- emulated success
562 ** -1 -- emulated failure
567 sendmail_mpe_setgid(gid
)
570 if (sendmail_mpe_emulgid())
572 if (gid
== getgid() || sendmail_mpe_euid
== 0)
574 sendmail_mpe_egid
= gid
;
584 ** SENDMAIL_MPE_SETUID -- shadow function for setuid()
586 ** setuid() is broken as of MPE 7.0 in that it changes the current
587 ** working directory to be the home directory of the new uid. Thus
588 ** we must obtain the cwd and restore it after the setuid().
590 ** Note that expected uid 0 semantics have been added, as well as
591 ** remembering the new uid for later use by the other shadow functions.
594 ** uid -- desired uid.
606 sendmail_mpe_setuid(uid
)
610 char cwd_buf
[PATH_MAX
+ 1];
612 extern void GETPRIVMODE
__P((void));
613 extern void GETUSERMODE
__P((void));
615 if (sendmail_mpe_emuluid())
619 if (sendmail_mpe_euid
!= 0)
624 sendmail_mpe_euid
= 0;
628 /* Preserve the current working directory */
629 if ((cwd
= getcwd(cwd_buf
, PATH_MAX
+ 1)) == NULL
)
633 result
= setuid(uid
);
636 /* Restore the current working directory */
640 sendmail_mpe_euid
= uid
;