Merge from vendor branch BIND:
[dragonfly.git] / contrib / sendmail-8.13.4 / libsm / mpeix.c
1 /*
2  * Copyright (c) 2001-2002 Sendmail, 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.6 2002/05/24 20:50:14 gshapiro 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
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
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
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
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
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 *
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 uninitalized 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 *
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
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
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
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 uid_t sendmail_mpe_egid = -1;
510
511 #undef getegid
512 gid_t
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
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
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
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 */