1 /*
2 * Copyright (c) 2001-2002 Proofpoint, Inc. and its suppliers.
3 * All rights reserved.
4 *
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.
8 *
9 */
10
11 #include <sm/gen.h>
12 SM_RCSID("@(#)$Id: mpeix.c,v 1.8 2013-11-22 20:51:43 ca Exp $")
13
14 #ifdef MPE
15 /*
16 ** MPE lacks many common functions required across all sendmail programs
17 ** so we define implementations for these functions here.
18 */
19
20 # include <errno.h>
21 # include <fcntl.h>
22 # include <limits.h>
23 # include <mpe.h>
24 # include <netinet/in.h>
25 # include <pwd.h>
26 # include <sys/socket.h>
27 # include <sys/stat.h>
28 # include <unistd.h>
29 # include <sm/conf.h>
30
31 /*
32 ** CHROOT -- dummy chroot() function
33 **
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.
39 **
40 ** Parameters:
41 ** path -- pathname of new root (ignored).
42 **
43 ** Returns:
44 ** -1 and errno == ENOSYS (function not implemented)
45 */
46
47 int
48 chroot(path)
49 char *path;
50 {
51 errno = ENOSYS;
52 return -1;
53 }
54
55 /*
56 ** ENDPWENT -- dummy endpwent() function
57 **
58 ** Parameters:
59 ** none
60 **
61 ** Returns:
62 ** none
63 */
64
65 void
endpwent()66 endpwent()
67 {
68 return;
69 }
70
71 /*
72 ** In addition to missing functions, certain existing MPE functions have
73 ** slightly different semantics (or bugs) compared to normal Unix OSes.
74 **
75 ** Here we define wrappers for these functions to make them behave in the
76 ** manner expected by sendmail.
77 */
78
79 /*
80 ** SENDMAIL_MPE_BIND -- shadow function for the standard socket bind()
81 **
82 ** MPE requires GETPRIVMODE() for AF_INET sockets less than port 1024.
83 **
84 ** Parameters:
85 ** sd -- socket descriptor.
86 ** addr -- socket address.
87 ** addrlen -- length of socket address.
88 **
89 ** Results:
90 ** 0 -- success
91 ** != 0 -- failure
92 */
93
94 #undef bind
95 int
sendmail_mpe_bind(sd,addr,addrlen)96 sendmail_mpe_bind(sd, addr, addrlen)
97 int sd;
98 void *addr;
99 int addrlen;
100 {
101 bool priv = false;
102 int result;
103 extern void GETPRIVMODE __P((void));
104 extern void GETUSERMODE __P((void));
105
106 if (addrlen == sizeof(struct sockaddr_in) &&
107 ((struct sockaddr_in *)addr)->sin_family == AF_INET)
108 {
109 /* AF_INET */
110 if (((struct sockaddr_in *)addr)->sin_port > 0 &&
111 ((struct sockaddr_in *)addr)->sin_port < 1024)
112 {
113 priv = true;
114 GETPRIVMODE();
115 }
116 ((struct sockaddr_in *)addr)->sin_addr.s_addr = 0;
117 result = bind(sd, addr, addrlen);
118 if (priv)
119 GETUSERMODE();
120 return result;
121 }
122
123 /* AF_UNIX */
124 return bind(sd, addr, addrlen);
125 }
126
127 /*
128 ** SENDMAIL_MPE__EXIT -- wait for children to terminate, then _exit()
129 **
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
132 ** infanticide.
133 **
134 ** Parameters:
135 ** status -- _exit status value.
136 **
137 ** Returns:
138 ** none.
139 */
140
141 #undef _exit
142 void
sendmail_mpe__exit(status)143 sendmail_mpe__exit(status)
144 int status;
145 {
146 int result;
147
148 /* Wait for all children to terminate. */
149 do
150 {
151 result = wait(NULL);
152 } while (result > 0 || errno == EINTR);
153 _exit(status);
154 }
155
156 /*
157 ** SENDMAIL_MPE_EXIT -- wait for children to terminate, then exit()
158 **
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
161 ** infanticide.
162 **
163 ** Parameters:
164 ** status -- exit status value.
165 **
166 ** Returns:
167 ** none.
168 */
169
170 #undef exit
171 void
sendmail_mpe_exit(status)172 sendmail_mpe_exit(status)
173 int status;
174 {
175 int result;
176
177 /* Wait for all children to terminate. */
178 do
179 {
180 result = wait(NULL);
181 } while (result > 0 || errno == EINTR);
182 exit(status);
183 }
184
185 /*
186 ** SENDMAIL_MPE_FCNTL -- shadow function for fcntl()
187 **
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.
191 **
192 ** Parameters:
193 ** same as fcntl().
194 **
195 ** Returns:
196 ** same as fcntl().
197 */
198
199 #undef fcntl
200 int
sendmail_mpe_fcntl(int fildes,int cmd,...)201 sendmail_mpe_fcntl(int fildes, int cmd, ...)
202 {
203 int len, result;
204 struct sockaddr sa;
205
206 void *arg;
207 va_list ap;
208
209 va_start(ap, cmd);
210 arg = va_arg(ap, void *);
211 va_end(ap);
212
213 len = sizeof sa;
214 if (getsockname(fildes, &sa, &len) == -1)
215 {
216 if (errno == EAFNOSUPPORT)
217 {
218 /* AF_UNIX socket */
219 return sfcntl(fildes, cmd, arg);
220 }
221 else if (errno == ENOTSOCK)
222 {
223 /* file or pipe */
224 return fcntl(fildes, cmd, arg);
225 }
226
227 /* unknown getsockname() failure */
228 return (-1);
229 }
230 else
231 {
232 /* AF_INET socket */
233 if ((result = sfcntl(fildes, cmd, arg)) != -1 &&
234 cmd == F_GETFL)
235 result |= O_RDWR; /* fill in some missing flags */
236 return result;
237 }
238 }
239
240 /*
241 ** SENDMAIL_MPE_GETPWNAM - shadow function for getpwnam()
242 **
243 ** Several issues apply here:
244 **
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
248 **
249 ** Parameters:
250 ** name -- username string.
251 **
252 ** Returns:
253 ** pointer to struct passwd if found else NULL
254 */
255
256 static char *sendmail_mpe_nullstr = "";
257
258 #undef getpwnam
259 extern struct passwd *getpwnam(const char *);
260
261 struct passwd *
sendmail_mpe_getpwnam(name)262 sendmail_mpe_getpwnam(name)
263 const char *name;
264 {
265 int dots = 0;
266 int err;
267 int i = strlen(name);
268 char *upper;
269 struct passwd *result = NULL;
270
271 if (i <= 0)
272 {
273 errno = EINVAL;
274 return result;
275 }
276
277 if ((upper = (char *)malloc(i + 1)) != NULL)
278 {
279 /* upshift the username parameter and count the dots */
280 while (i >= 0)
281 {
282 if (name[i] == '.')
283 {
284 dots++;
285 upper[i] = '.';
286 }
287 else
288 upper[i] = toupper(name[i]);
289 i--;
290 }
291
292 if (dots != 1)
293 {
294 /* prevent bug when dots == 0 */
295 err = EINVAL;
296 }
297 else if ((result = getpwnam(upper)) != NULL)
298 {
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;
306 }
307 err = errno;
308 free(upper);
309 }
310 errno = err;
311 return result;
312 }
313
314 /*
315 ** SENDMAIL_MPE_GETPWUID -- shadow function for getpwuid()
316 **
317 ** Initializes the uninitialized fields in the passwd struct.
318 **
319 ** Parameters:
320 ** uid -- uid to obtain passwd data for
321 **
322 ** Returns:
323 ** pointer to struct passwd or NULL if not found
324 */
325
326 #undef getpwuid
327 extern struct passwd *getpwuid __P((uid_t));
328
329 struct passwd *
sendmail_mpe_getpwuid(uid)330 sendmail_mpe_getpwuid(uid)
331 uid_t uid;
332 {
333 struct passwd *result;
334
335 if ((result = getpwuid(uid)) != NULL)
336 {
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;
344 }
345 return result;
346 }
347
348 /*
349 ** OK boys and girls, time for some serious voodoo!
350 **
351 ** MPE does not have a complete implementation of POSIX users and groups:
352 **
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
357 **
358 ** ...all of which thoroughly annoys sendmail.
359 **
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
363 ** time we finished.
364 **
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:
368 **
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
374 **
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.
380 **
381 ** The following flags determine the current emulation state:
382 **
383 ** true == emulation enabled
384 ** false == emulation disabled, use unmodified MPE semantics
385 */
386
387 static bool sendmail_mpe_flaginit = false;
388 static bool sendmail_mpe_gidflag = false;
389 static bool sendmail_mpe_uidflag = false;
390
391 /*
392 ** SENDMAIL_MPE_GETMODE -- return the mode bits for the current process
393 **
394 ** Parameters:
395 ** none.
396 **
397 ** Returns:
398 ** file mode bits for the current process program file.
399 */
400
401 mode_t
sendmail_mpe_getmode()402 sendmail_mpe_getmode()
403 {
404 int status = 666;
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];
410 struct stat st;
411 extern HPMYPROGRAM __P((int parms, char *formaldesig, int *status,
412 int *length, char *myprogram,
413 int *myprogram_length, int *myprogram_syntax));
414
415 myprogram_length = sizeof(myprogram);
416 HPMYPROGRAM(6, formaldesig, &status, NULL, myprogram,
417 &myprogram_length, &myprogram_syntax);
418
419 /* should not occur, do not attempt emulation */
420 if (status != 0)
421 return 0;
422
423 memcpy(&path, &myprogram[1], myprogram_length - 2);
424 path[myprogram_length - 2] = '\0';
425
426 /* should not occur, do not attempt emulation */
427 if (stat(path, &st) < 0)
428 return 0;
429
430 return st.st_mode;
431 }
432
433 /*
434 ** SENDMAIL_MPE_EMULGID -- should we perform gid emulation?
435 **
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.
440 **
441 ** Parameters:
442 ** none.
443 **
444 ** Returns:
445 ** true -- perform gid emulation
446 ** false -- do not perform gid emulation
447 */
448
449 bool
sendmail_mpe_emulgid()450 sendmail_mpe_emulgid()
451 {
452 if (!sendmail_mpe_flaginit)
453 {
454 mode_t mode;
455
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;
460 }
461 return sendmail_mpe_gidflag;
462 }
463
464 /*
465 ** SENDMAIL_MPE_EMULUID -- should we perform uid emulation?
466 **
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.
471 **
472 ** Parameters:
473 ** none.
474 **
475 ** Returns:
476 ** true -- perform uid emulation
477 ** false -- do not perform uid emulation
478 */
479
480 bool
sendmail_mpe_emuluid()481 sendmail_mpe_emuluid()
482 {
483 if (!sendmail_mpe_flaginit)
484 {
485 mode_t mode;
486
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;
491 }
492 return sendmail_mpe_uidflag;
493 }
494
495 /*
496 ** SENDMAIL_MPE_GETEGID -- shadow function for getegid()
497 **
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.
501 **
502 ** Parameters:
503 ** none.
504 **
505 ** Returns:
506 ** emulated egid if present, else true egid.
507 */
508
509 static gid_t sendmail_mpe_egid = -1;
510
511 #undef getegid
512 gid_t
sendmail_mpe_getegid()513 sendmail_mpe_getegid()
514 {
515 if (sendmail_mpe_emulgid() && sendmail_mpe_egid != -1)
516 return sendmail_mpe_egid;
517 return getegid();
518 }
519
520 /*
521 ** SENDMAIL_MPE_GETEUID -- shadow function for geteuid()
522 **
523 ** If emulation mode is in effect, return the saved euid; otherwise
524 ** return the value of the real geteuid() function.
525 **
526 ** Note that the initial value of the saved euid is zero, to simulate
527 ** a setuid-root program.
528 **
529 ** Parameters:
530 ** none
531 **
532 ** Returns:
533 ** emulated euid if in emulation mode, else true euid.
534 */
535
536 static uid_t sendmail_mpe_euid = 0;
537
538 #undef geteuid
539 uid_t
sendmail_mpe_geteuid()540 sendmail_mpe_geteuid()
541 {
542 if (sendmail_mpe_emuluid())
543 return sendmail_mpe_euid;
544 return geteuid();
545 }
546
547 /*
548 ** SENDMAIL_MPE_SETGID -- shadow function for setgid()
549 **
550 ** Simulate a call to setgid() without actually calling the real
551 ** function. Implement the expected uid 0 semantics.
552 **
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.
556 **
557 ** Parameters:
558 ** gid -- desired gid.
559 **
560 ** Returns:
561 ** 0 -- emulated success
562 ** -1 -- emulated failure
563 */
564
565 #undef setgid
566 int
sendmail_mpe_setgid(gid)567 sendmail_mpe_setgid(gid)
568 gid_t gid;
569 {
570 if (sendmail_mpe_emulgid())
571 {
572 if (gid == getgid() || sendmail_mpe_euid == 0)
573 {
574 sendmail_mpe_egid = gid;
575 return 0;
576 }
577 errno = EINVAL;
578 return -1;
579 }
580 return setgid(gid);
581 }
582
583 /*
584 ** SENDMAIL_MPE_SETUID -- shadow function for setuid()
585 **
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().
589 **
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.
592 **
593 ** Parameters:
594 ** uid -- desired uid.
595 **
596 ** Returns:
597 ** 0 -- success
598 ** -1 -- failure
599 **
600 ** Globals:
601 ** sendmail_mpe_euid
602 */
603
604 #undef setuid
605 int
sendmail_mpe_setuid(uid)606 sendmail_mpe_setuid(uid)
607 uid_t uid;
608 {
609 char *cwd;
610 char cwd_buf[PATH_MAX + 1];
611 int result;
612 extern void GETPRIVMODE __P((void));
613 extern void GETUSERMODE __P((void));
614
615 if (sendmail_mpe_emuluid())
616 {
617 if (uid == 0)
618 {
619 if (sendmail_mpe_euid != 0)
620 {
621 errno = EINVAL;
622 return -1;
623 }
624 sendmail_mpe_euid = 0;
625 return 0;
626 }
627
628 /* Preserve the current working directory */
629 if ((cwd = getcwd(cwd_buf, PATH_MAX + 1)) == NULL)
630 return -1;
631
632 GETPRIVMODE();
633 result = setuid(uid);
634 GETUSERMODE();
635
636 /* Restore the current working directory */
637 chdir(cwd_buf);
638
639 if (result == 0)
640 sendmail_mpe_euid = uid;
641
642 return result;
643 }
644 return setuid(uid);
645 }
646 #endif /* MPE */
647