xref: /freebsd/contrib/sendmail/libsm/mpeix.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
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
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 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 *
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 gid_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 */
647