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>
12SM_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
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 gid_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 */
647