source: trunk/third/sendmail/libsm/mpeix.c @ 19204

Revision 19204, 13.8 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19203, which included commits to RCS files with non-trunk default branches.
Line 
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>
12SM_RCSID("@(#)$Id: mpeix.c,v 1.1.1.1 2003-04-08 15:07:35 zacheiss 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
47int
48chroot(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
65void
66endpwent()
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
95int
96sendmail_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
142void
143sendmail_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
171void
172sendmail_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
200int
201sendmail_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
256static char *sendmail_mpe_nullstr = "";
257
258#undef getpwnam
259extern struct passwd *getpwnam(const char *);
260
261struct passwd *
262sendmail_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
327extern struct passwd *getpwuid __P((uid_t));
328
329struct passwd *
330sendmail_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
387static bool sendmail_mpe_flaginit = false;
388static bool sendmail_mpe_gidflag = false;
389static 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
401mode_t
402sendmail_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
449bool
450sendmail_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
480bool
481sendmail_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
509static uid_t sendmail_mpe_egid = -1;
510
511#undef getegid
512gid_t
513sendmail_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
536static uid_t sendmail_mpe_euid = 0;
537
538#undef geteuid
539uid_t
540sendmail_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
566int
567sendmail_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
605int
606sendmail_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 */
Note: See TracBrowser for help on using the repository browser.