xref: /csrg-svn/usr.sbin/sendmail/src/util.c (revision 68703)
122717Sdist /*
242833Sbostic  * Copyright (c) 1983 Eric P. Allman
363589Sbostic  * Copyright (c) 1988, 1993
463589Sbostic  *	The Regents of the University of California.  All rights reserved.
533731Sbostic  *
642833Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822717Sdist 
922717Sdist #ifndef lint
10*68703Seric static char sccsid[] = "@(#)util.c	8.61 (Berkeley) 03/31/95";
1133731Sbostic #endif /* not lint */
1222717Sdist 
1358332Seric # include "sendmail.h"
14298Seric # include <sysexits.h>
1557135Seric /*
16298Seric **  STRIPQUOTES -- Strip quotes & quote bits from a string.
17298Seric **
18298Seric **	Runs through a string and strips off unquoted quote
19298Seric **	characters and quote bits.  This is done in place.
20298Seric **
21298Seric **	Parameters:
22298Seric **		s -- the string to strip.
23298Seric **
24298Seric **	Returns:
25298Seric **		none.
26298Seric **
27298Seric **	Side Effects:
28298Seric **		none.
29298Seric **
30298Seric **	Called By:
31298Seric **		deliver
32298Seric */
33298Seric 
3454983Seric stripquotes(s)
35298Seric 	char *s;
36298Seric {
37298Seric 	register char *p;
38298Seric 	register char *q;
39298Seric 	register char c;
40298Seric 
414101Seric 	if (s == NULL)
424101Seric 		return;
434101Seric 
4454983Seric 	p = q = s;
4554983Seric 	do
46298Seric 	{
4754983Seric 		c = *p++;
4854983Seric 		if (c == '\\')
4954983Seric 			c = *p++;
5054983Seric 		else if (c == '"')
5154983Seric 			continue;
5254983Seric 		*q++ = c;
5354983Seric 	} while (c != '\0');
54298Seric }
55298Seric /*
56298Seric **  XALLOC -- Allocate memory and bitch wildly on failure.
57298Seric **
58298Seric **	THIS IS A CLUDGE.  This should be made to give a proper
59298Seric **	error -- but after all, what can we do?
60298Seric **
61298Seric **	Parameters:
62298Seric **		sz -- size of area to allocate.
63298Seric **
64298Seric **	Returns:
65298Seric **		pointer to data region.
66298Seric **
67298Seric **	Side Effects:
68298Seric **		Memory is allocated.
69298Seric */
70298Seric 
71298Seric char *
72298Seric xalloc(sz)
737007Seric 	register int sz;
74298Seric {
75298Seric 	register char *p;
76298Seric 
7766747Seric 	/* some systems can't handle size zero mallocs */
7866747Seric 	if (sz <= 0)
7966747Seric 		sz = 1;
8066747Seric 
8123121Seric 	p = malloc((unsigned) sz);
82298Seric 	if (p == NULL)
83298Seric 	{
84298Seric 		syserr("Out of memory!!");
8510685Seric 		abort();
8610685Seric 		/* exit(EX_UNAVAILABLE); */
87298Seric 	}
88298Seric 	return (p);
89298Seric }
90298Seric /*
913151Seric **  COPYPLIST -- copy list of pointers.
923151Seric **
933151Seric **	This routine is the equivalent of newstr for lists of
943151Seric **	pointers.
953151Seric **
963151Seric **	Parameters:
973151Seric **		list -- list of pointers to copy.
983151Seric **			Must be NULL terminated.
993151Seric **		copycont -- if TRUE, copy the contents of the vector
1003151Seric **			(which must be a string) also.
1013151Seric **
1023151Seric **	Returns:
1033151Seric **		a copy of 'list'.
1043151Seric **
1053151Seric **	Side Effects:
1063151Seric **		none.
1073151Seric */
1083151Seric 
1093151Seric char **
1103151Seric copyplist(list, copycont)
1113151Seric 	char **list;
1123151Seric 	bool copycont;
1133151Seric {
1143151Seric 	register char **vp;
1153151Seric 	register char **newvp;
1163151Seric 
1173151Seric 	for (vp = list; *vp != NULL; vp++)
1183151Seric 		continue;
1193151Seric 
1203151Seric 	vp++;
1213151Seric 
12216897Seric 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
12316897Seric 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
1243151Seric 
1253151Seric 	if (copycont)
1263151Seric 	{
1273151Seric 		for (vp = newvp; *vp != NULL; vp++)
1283151Seric 			*vp = newstr(*vp);
1293151Seric 	}
1303151Seric 
1313151Seric 	return (newvp);
1323151Seric }
1333151Seric /*
13458170Seric **  COPYQUEUE -- copy address queue.
13558170Seric **
13658170Seric **	This routine is the equivalent of newstr for address queues
13758170Seric **	addresses marked with QDONTSEND aren't copied
13858170Seric **
13958170Seric **	Parameters:
14058170Seric **		addr -- list of address structures to copy.
14158170Seric **
14258170Seric **	Returns:
14358170Seric **		a copy of 'addr'.
14458170Seric **
14558170Seric **	Side Effects:
14658170Seric **		none.
14758170Seric */
14858170Seric 
14958170Seric ADDRESS *
15058170Seric copyqueue(addr)
15158170Seric 	ADDRESS *addr;
15258170Seric {
15358170Seric 	register ADDRESS *newaddr;
15458170Seric 	ADDRESS *ret;
15558170Seric 	register ADDRESS **tail = &ret;
15658170Seric 
15758170Seric 	while (addr != NULL)
15858170Seric 	{
15958170Seric 		if (!bitset(QDONTSEND, addr->q_flags))
16058170Seric 		{
16158170Seric 			newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
16258170Seric 			STRUCTCOPY(*addr, *newaddr);
16358170Seric 			*tail = newaddr;
16458170Seric 			tail = &newaddr->q_next;
16558170Seric 		}
16658170Seric 		addr = addr->q_next;
16758170Seric 	}
16858170Seric 	*tail = NULL;
16958170Seric 
17058170Seric 	return ret;
17158170Seric }
17258170Seric /*
1733151Seric **  PRINTAV -- print argument vector.
1743151Seric **
1753151Seric **	Parameters:
1763151Seric **		av -- argument vector.
1773151Seric **
1783151Seric **	Returns:
1793151Seric **		none.
1803151Seric **
1813151Seric **	Side Effects:
1823151Seric **		prints av.
1833151Seric */
1843151Seric 
1853151Seric printav(av)
1863151Seric 	register char **av;
1873151Seric {
1883151Seric 	while (*av != NULL)
1893151Seric 	{
1908063Seric 		if (tTd(0, 44))
1918063Seric 			printf("\n\t%08x=", *av);
1928063Seric 		else
19323105Seric 			(void) putchar(' ');
1943151Seric 		xputs(*av++);
1953151Seric 	}
19623105Seric 	(void) putchar('\n');
1973151Seric }
1983151Seric /*
1993151Seric **  LOWER -- turn letter into lower case.
2003151Seric **
2013151Seric **	Parameters:
2023151Seric **		c -- character to turn into lower case.
2033151Seric **
2043151Seric **	Returns:
2053151Seric **		c, in lower case.
2063151Seric **
2073151Seric **	Side Effects:
2083151Seric **		none.
2093151Seric */
2103151Seric 
2113151Seric char
2123151Seric lower(c)
2133151Seric 	register char c;
2143151Seric {
21558050Seric 	return((isascii(c) && isupper(c)) ? tolower(c) : c);
2163151Seric }
2173151Seric /*
2183151Seric **  XPUTS -- put string doing control escapes.
2193151Seric **
2203151Seric **	Parameters:
2213151Seric **		s -- string to put.
2223151Seric **
2233151Seric **	Returns:
2243151Seric **		none.
2253151Seric **
2263151Seric **	Side Effects:
2273151Seric **		output to stdout
2283151Seric */
2293151Seric 
2303151Seric xputs(s)
2313151Seric 	register char *s;
2323151Seric {
23358050Seric 	register int c;
23451781Seric 	register struct metamac *mp;
23551781Seric 	extern struct metamac MetaMacros[];
2363151Seric 
2378055Seric 	if (s == NULL)
2388055Seric 	{
2398055Seric 		printf("<null>");
2408055Seric 		return;
2418055Seric 	}
24258050Seric 	while ((c = (*s++ & 0377)) != '\0')
2433151Seric 	{
2443151Seric 		if (!isascii(c))
2453151Seric 		{
24668481Seric 			if (c == MATCHREPL)
24758050Seric 			{
24858050Seric 				putchar('$');
24958050Seric 				continue;
25058050Seric 			}
25168481Seric 			if (c == MACROEXPAND)
25268481Seric 			{
25368481Seric 				putchar('$');
25468481Seric 				if (bitset(0200, *s))
25568481Seric 					printf("{%s}", macname(*s++ & 0377));
25668481Seric 				continue;
25768481Seric 			}
25858050Seric 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
25958050Seric 			{
26058050Seric 				if ((mp->metaval & 0377) == c)
26158050Seric 				{
26258050Seric 					printf("$%c", mp->metaname);
26358050Seric 					break;
26458050Seric 				}
26558050Seric 			}
26658050Seric 			if (mp->metaname != '\0')
26758050Seric 				continue;
26823105Seric 			(void) putchar('\\');
2693151Seric 			c &= 0177;
2703151Seric 		}
27157589Seric 		if (isprint(c))
2723151Seric 		{
27357589Seric 			putchar(c);
27457589Seric 			continue;
27557589Seric 		}
27652050Seric 
27757589Seric 		/* wasn't a meta-macro -- find another way to print it */
27857589Seric 		switch (c)
27957589Seric 		{
28057589Seric 		  case '\n':
28157589Seric 			c = 'n';
28257589Seric 			break;
28352050Seric 
28457589Seric 		  case '\r':
28557589Seric 			c = 'r';
28657589Seric 			break;
28752637Seric 
28857589Seric 		  case '\t':
28957589Seric 			c = 't';
29057589Seric 			break;
29157589Seric 
29257589Seric 		  default:
29357589Seric 			(void) putchar('^');
29457589Seric 			(void) putchar(c ^ 0100);
29557589Seric 			continue;
2963151Seric 		}
29768500Seric 		(void) putchar('\\');
29868500Seric 		(void) putchar(c);
2993151Seric 	}
3004086Seric 	(void) fflush(stdout);
3013151Seric }
3023151Seric /*
3033151Seric **  MAKELOWER -- Translate a line into lower case
3043151Seric **
3053151Seric **	Parameters:
3063151Seric **		p -- the string to translate.  If NULL, return is
3073151Seric **			immediate.
3083151Seric **
3093151Seric **	Returns:
3103151Seric **		none.
3113151Seric **
3123151Seric **	Side Effects:
3133151Seric **		String pointed to by p is translated to lower case.
3143151Seric **
3153151Seric **	Called By:
3163151Seric **		parse
3173151Seric */
3183151Seric 
31968481Seric void
3203151Seric makelower(p)
3213151Seric 	register char *p;
3223151Seric {
3233151Seric 	register char c;
3243151Seric 
3253151Seric 	if (p == NULL)
3263151Seric 		return;
3273151Seric 	for (; (c = *p) != '\0'; p++)
3283151Seric 		if (isascii(c) && isupper(c))
32933724Sbostic 			*p = tolower(c);
3303151Seric }
3314059Seric /*
3325196Seric **  BUILDFNAME -- build full name from gecos style entry.
3334375Seric **
3345196Seric **	This routine interprets the strange entry that would appear
3355196Seric **	in the GECOS field of the password file.
3365196Seric **
3374375Seric **	Parameters:
3385196Seric **		p -- name to build.
3395196Seric **		login -- the login name of this user (for &).
3405196Seric **		buf -- place to put the result.
3414375Seric **
3424375Seric **	Returns:
34365006Seric **		none.
3444375Seric **
3454375Seric **	Side Effects:
3464375Seric **		none.
3474375Seric */
3484375Seric 
34954984Seric buildfname(gecos, login, buf)
35065006Seric 	register char *gecos;
35165006Seric 	char *login;
35265006Seric 	char *buf;
3534375Seric {
35465006Seric 	register char *p;
35565006Seric 	register char *bp = buf;
35665006Seric 	int l;
3574375Seric 
35865006Seric 	if (*gecos == '*')
35965006Seric 		gecos++;
36065006Seric 
36165006Seric 	/* find length of final string */
36265006Seric 	l = 0;
36365006Seric 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
36454984Seric 	{
36565006Seric 		if (*p == '&')
36665006Seric 			l += strlen(login);
36765006Seric 		else
36865006Seric 			l++;
36954984Seric 	}
37065006Seric 
37165006Seric 	/* now fill in buf */
37265006Seric 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
3734375Seric 	{
37465006Seric 		if (*p == '&')
3754375Seric 		{
37665006Seric 			(void) strcpy(bp, login);
37765006Seric 			*bp = toupper(*bp);
37865006Seric 			while (*bp != '\0')
37965006Seric 				bp++;
3804375Seric 		}
38165006Seric 		else
38265006Seric 			*bp++ = *p;
3834375Seric 	}
3844375Seric 	*bp = '\0';
3854375Seric }
3864375Seric /*
3874538Seric **  SAFEFILE -- return true if a file exists and is safe for a user.
3884538Seric **
3894538Seric **	Parameters:
3904538Seric **		fn -- filename to check.
39164083Seric **		uid -- user id to compare against.
39264083Seric **		gid -- group id to compare against.
39364083Seric **		uname -- user name to compare against (used for group
39464083Seric **			sets).
39564944Seric **		flags -- modifiers:
39665064Seric **			SFF_MUSTOWN -- "uid" must own this file.
39765064Seric **			SFF_NOSLINK -- file cannot be a symbolic link.
3984538Seric **		mode -- mode bits that must match.
39968494Seric **		st -- if set, points to a stat structure that will
40068494Seric **			get the stat info for the file.
4014538Seric **
4024538Seric **	Returns:
40358247Seric **		0 if fn exists, is owned by uid, and matches mode.
40458247Seric **		An errno otherwise.  The actual errno is cleared.
4054538Seric **
4064538Seric **	Side Effects:
4074538Seric **		none.
4084538Seric */
4094538Seric 
41064083Seric #include <grp.h>
41164083Seric 
41263581Seric #ifndef S_IXOTH
41363581Seric # define S_IXOTH	(S_IEXEC >> 6)
41463581Seric #endif
41563581Seric 
41664083Seric #ifndef S_IXGRP
41764083Seric # define S_IXGRP	(S_IEXEC >> 3)
41864083Seric #endif
41964083Seric 
42063753Seric #ifndef S_IXUSR
42163753Seric # define S_IXUSR	(S_IEXEC)
42263753Seric #endif
42363753Seric 
42468494Seric #define ST_MODE_NOFILE	0171147		/* unlikely to occur */
42568494Seric 
42658247Seric int
42768494Seric safefile(fn, uid, gid, uname, flags, mode, st)
4284538Seric 	char *fn;
42955372Seric 	uid_t uid;
43064083Seric 	gid_t gid;
43164083Seric 	char *uname;
43264944Seric 	int flags;
4334538Seric 	int mode;
43468494Seric 	struct stat *st;
4354538Seric {
43663581Seric 	register char *p;
43764083Seric 	register struct group *gr = NULL;
4384538Seric 	struct stat stbuf;
4394538Seric 
44063581Seric 	if (tTd(54, 4))
44164944Seric 		printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
44264944Seric 			fn, uid, gid, flags, mode);
44363581Seric 	errno = 0;
44468494Seric 	if (st == NULL)
44568494Seric 		st = &stbuf;
44663581Seric 
44768481Seric 	if (!bitset(SFF_NOPATHCHECK, flags) ||
44868481Seric 	    (uid == 0 && !bitset(SFF_ROOTOK, flags)))
44963581Seric 	{
45068481Seric 		/* check the path to the file for acceptability */
45168481Seric 		for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
45265225Seric 		{
45368481Seric 			*p = '\0';
45468481Seric 			if (stat(fn, &stbuf) < 0)
45568481Seric 				break;
45668513Seric 			if (uid == 0 && bitset(S_IWGRP|S_IWOTH, stbuf.st_mode))
45768513Seric 				message("051 WARNING: writable directory %s",
45868513Seric 					fn);
45968481Seric 			if (uid == 0 && !bitset(SFF_ROOTOK, flags))
46068481Seric 			{
46168481Seric 				if (bitset(S_IXOTH, stbuf.st_mode))
46268481Seric 					continue;
46368481Seric 				break;
46468481Seric 			}
46568481Seric 			if (stbuf.st_uid == uid &&
46668481Seric 			    bitset(S_IXUSR, stbuf.st_mode))
46765225Seric 				continue;
46868481Seric 			if (stbuf.st_gid == gid &&
46968481Seric 			    bitset(S_IXGRP, stbuf.st_mode))
47068481Seric 				continue;
47168481Seric #ifndef NO_GROUP_SET
47268481Seric 			if (uname != NULL &&
47368481Seric 			    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
47468481Seric 			     (gr = getgrgid(stbuf.st_gid)) != NULL))
47568481Seric 			{
47668481Seric 				register char **gp;
47768481Seric 
47868481Seric 				for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
47968481Seric 					if (strcmp(*gp, uname) == 0)
48068481Seric 						break;
48168481Seric 				if (gp != NULL && *gp != NULL &&
48268481Seric 				    bitset(S_IXGRP, stbuf.st_mode))
48368481Seric 					continue;
48468481Seric 			}
48568481Seric #endif
48668481Seric 			if (!bitset(S_IXOTH, stbuf.st_mode))
48768481Seric 				break;
48868478Seric 		}
48968481Seric 		if (p != NULL)
49063581Seric 		{
49168481Seric 			int ret = errno;
49263581Seric 
49368481Seric 			if (ret == 0)
49468481Seric 				ret = EACCES;
49568481Seric 			if (tTd(54, 4))
49668481Seric 				printf("\t[dir %s] %s\n", fn, errstring(ret));
49768481Seric 			*p = '/';
49868481Seric 			return ret;
49963581Seric 		}
50063581Seric 	}
50163581Seric 
50264944Seric #ifdef HASLSTAT
50368494Seric 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
50468494Seric 					: stat(fn, st)) < 0)
50564944Seric #else
50668494Seric 	if (stat(fn, st) < 0)
50764944Seric #endif
50858247Seric 	{
50958247Seric 		int ret = errno;
51058247Seric 
51163581Seric 		if (tTd(54, 4))
51264083Seric 			printf("\t%s\n", errstring(ret));
51363581Seric 
51458247Seric 		errno = 0;
51568494Seric 		if (!bitset(SFF_CREAT, flags))
51668494Seric 			return ret;
51768494Seric 
51868494Seric 		/* check to see if legal to create the file */
51968494Seric 		p = strrchr(fn, '/');
52068494Seric 		if (p == NULL)
52168494Seric 			return ENOTDIR;
52268494Seric 		*p = '\0';
52368494Seric 		if (stat(fn, &stbuf) >= 0)
52468494Seric 		{
52568494Seric 			int md = S_IWRITE|S_IEXEC;
52668494Seric 			if (stbuf.st_uid != uid)
52768494Seric 				md >>= 6;
52868494Seric 			if ((stbuf.st_mode & md) != md)
52968494Seric 				errno = EACCES;
53068494Seric 		}
53168494Seric 		ret = errno;
53268494Seric 		if (tTd(54, 4))
53368494Seric 			printf("\t[final dir %s uid %d mode %o] %s\n",
53468494Seric 				fn, stbuf.st_uid, stbuf.st_mode,
53568494Seric 				errstring(ret));
53668494Seric 		*p = '/';
53768494Seric 		st->st_mode = ST_MODE_NOFILE;
53858247Seric 		return ret;
53958247Seric 	}
54064944Seric 
54164944Seric #ifdef S_ISLNK
54268494Seric 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
54364944Seric 	{
54464944Seric 		if (tTd(54, 4))
54568494Seric 			printf("\t[slink mode %o]\tEPERM\n", st->st_mode);
54664944Seric 		return EPERM;
54764944Seric 	}
54864944Seric #endif
54968513Seric 	if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
55068513Seric 	{
55168513Seric 		if (tTd(54, 4))
55268513Seric 			printf("\t[non-reg mode %o]\tEPERM\n", st->st_mode);
55368513Seric 		return EPERM;
55468513Seric 	}
55568494Seric 	if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && bitset(0111, st->st_mode))
55668494Seric 	{
55768494Seric 		if (tTd(29, 5))
55868494Seric 			printf("failed (mode %o: x bits)\n", st->st_mode);
55968513Seric 		return EPERM;
56068494Seric 	}
56168494Seric 
56268494Seric 	if (bitset(SFF_SETUIDOK, flags))
56368494Seric 	{
56468494Seric 		if (bitset(S_ISUID, st->st_mode) &&
56568494Seric 		    (st->st_uid != 0 || bitset(SFF_ROOTOK, flags)))
56668494Seric 		{
56768494Seric 			uid = st->st_uid;
56868494Seric 			uname = NULL;
56968494Seric 		}
57068494Seric 		if (bitset(S_ISGID, st->st_mode) &&
57168494Seric 		    (st->st_gid != 0 || bitset(SFF_ROOTOK, flags)))
57268494Seric 			gid = st->st_gid;
57368494Seric 	}
57468494Seric 
57565139Seric 	if (uid == 0 && !bitset(SFF_ROOTOK, flags))
57663581Seric 		mode >>= 6;
57768494Seric 	else if (st->st_uid != uid)
57864084Seric 	{
57964084Seric 		mode >>= 3;
58068494Seric 		if (st->st_gid == gid)
58164084Seric 			;
58264084Seric #ifndef NO_GROUP_SET
58364084Seric 		else if (uname != NULL &&
58468494Seric 			 ((gr != NULL && gr->gr_gid == st->st_gid) ||
58568494Seric 			  (gr = getgrgid(st->st_gid)) != NULL))
58664084Seric 		{
58764084Seric 			register char **gp;
58864084Seric 
58964084Seric 			for (gp = gr->gr_mem; *gp != NULL; gp++)
59064084Seric 				if (strcmp(*gp, uname) == 0)
59164084Seric 					break;
59264084Seric 			if (*gp == NULL)
59364084Seric 				mode >>= 3;
59464084Seric 		}
59564084Seric #endif
59664084Seric 		else
59764084Seric 			mode >>= 3;
59864084Seric 	}
59963581Seric 	if (tTd(54, 4))
60064084Seric 		printf("\t[uid %d, stat %o, mode %o] ",
60168494Seric 			st->st_uid, st->st_mode, mode);
60268494Seric 	if ((st->st_uid == uid || st->st_uid == 0 ||
60365064Seric 	     !bitset(SFF_MUSTOWN, flags)) &&
60468494Seric 	    (st->st_mode & mode) == mode)
60563581Seric 	{
60663581Seric 		if (tTd(54, 4))
60764083Seric 			printf("\tOK\n");
60858247Seric 		return 0;
60963581Seric 	}
61063581Seric 	if (tTd(54, 4))
61164083Seric 		printf("\tEACCES\n");
61263581Seric 	return EACCES;
6134538Seric }
6144538Seric /*
61568494Seric **  SAFEFOPEN -- do a file open with extra checking
61668494Seric **
61768494Seric **	Parameters:
61868494Seric **		fn -- the file name to open.
61968494Seric **		omode -- the open-style mode flags.
62068494Seric **		cmode -- the create-style mode flags.
62168494Seric **		sff -- safefile flags.
62268494Seric **
62368494Seric **	Returns:
62468494Seric **		Same as fopen.
62568494Seric */
62668494Seric 
627*68703Seric #ifndef O_ACCMODE
628*68703Seric # define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
629*68703Seric #endif
630*68703Seric 
63168494Seric FILE *
63268494Seric safefopen(fn, omode, cmode, sff)
63368494Seric 	char *fn;
63468494Seric 	int omode;
63568494Seric 	int cmode;
63668494Seric 	int sff;
63768494Seric {
63868494Seric 	int rval;
63968494Seric 	FILE *fp;
64068494Seric 	int smode;
64168494Seric 	struct stat stb, sta;
64268494Seric 	extern char RealUserName[];
64368494Seric 
64468494Seric 	if (bitset(O_CREAT, omode))
64568494Seric 		sff |= SFF_CREAT;
64668494Seric 	smode = 0;
64768494Seric 	switch (omode & O_ACCMODE)
64868494Seric 	{
64968494Seric 	  case O_RDONLY:
65068494Seric 		smode = S_IREAD;
65168494Seric 		break;
65268494Seric 
65368494Seric 	  case O_WRONLY:
65468494Seric 		smode = S_IWRITE;
65568494Seric 		break;
65668494Seric 
65768494Seric 	  case O_RDWR:
65868494Seric 		smode = S_IREAD|S_IWRITE;
65968494Seric 		break;
66068494Seric 
66168494Seric 	  default:
66268494Seric 		smode = 0;
66368494Seric 		break;
66468494Seric 	}
66568513Seric 	if (bitset(SFF_OPENASROOT, sff))
66668513Seric 		rval = safefile(fn, 0, 0, NULL, sff, smode, &stb);
66768513Seric 	else
66868513Seric 		rval = safefile(fn, RealUid, RealGid, RealUserName,
66968513Seric 				sff, smode, &stb);
67068494Seric 	if (rval != 0)
67168494Seric 	{
67268494Seric 		errno = rval;
67368494Seric 		return NULL;
67468494Seric 	}
67568494Seric 	if (stb.st_mode == ST_MODE_NOFILE)
67668494Seric 		omode |= O_EXCL;
67768494Seric 
67868494Seric 	fp = dfopen(fn, omode, cmode);
67968494Seric 	if (fp == NULL)
68068494Seric 		return NULL;
68168494Seric 	if (bitset(O_EXCL, omode))
68268494Seric 		return fp;
68368494Seric 	if (fstat(fileno(fp), &sta) < 0 ||
68468494Seric 	    sta.st_nlink != stb.st_nlink ||
68568494Seric 	    sta.st_dev != stb.st_dev ||
68668494Seric 	    sta.st_ino != stb.st_ino ||
68768494Seric 	    sta.st_uid != stb.st_uid ||
68868494Seric 	    sta.st_gid != stb.st_gid)
68968494Seric 	{
69068494Seric 		syserr("554 cannot open: file %s changed after open", fn);
69168513Seric 		fclose(fp);
69268494Seric 		errno = EPERM;
69368494Seric 		return NULL;
69468494Seric 	}
69568494Seric 	return fp;
69668494Seric }
69768494Seric /*
6984557Seric **  FIXCRLF -- fix <CR><LF> in line.
6994557Seric **
7004557Seric **	Looks for the <CR><LF> combination and turns it into the
7014557Seric **	UNIX canonical <NL> character.  It only takes one line,
7024557Seric **	i.e., it is assumed that the first <NL> found is the end
7034557Seric **	of the line.
7044557Seric **
7054557Seric **	Parameters:
7064557Seric **		line -- the line to fix.
7074557Seric **		stripnl -- if true, strip the newline also.
7084557Seric **
7094557Seric **	Returns:
7104557Seric **		none.
7114557Seric **
7124557Seric **	Side Effects:
7134557Seric **		line is changed in place.
7144557Seric */
7154557Seric 
7164557Seric fixcrlf(line, stripnl)
7174557Seric 	char *line;
7184557Seric 	bool stripnl;
7194557Seric {
7204557Seric 	register char *p;
7214557Seric 
72256795Seric 	p = strchr(line, '\n');
7234557Seric 	if (p == NULL)
7244557Seric 		return;
72536291Sbostic 	if (p > line && p[-1] == '\r')
7264557Seric 		p--;
7274557Seric 	if (!stripnl)
7284557Seric 		*p++ = '\n';
7294557Seric 	*p = '\0';
7304557Seric }
7314557Seric /*
7326890Seric **  DFOPEN -- determined file open
7336890Seric **
7346890Seric **	This routine has the semantics of fopen, except that it will
7356890Seric **	keep trying a few times to make this happen.  The idea is that
7366890Seric **	on very loaded systems, we may run out of resources (inodes,
7376890Seric **	whatever), so this tries to get around it.
7386890Seric */
7396890Seric 
74059745Seric struct omodes
74159745Seric {
74259745Seric 	int	mask;
74359745Seric 	int	mode;
74459745Seric 	char	*farg;
74559745Seric } OpenModes[] =
74659745Seric {
74759745Seric 	O_ACCMODE,		O_RDONLY,		"r",
74859745Seric 	O_ACCMODE|O_APPEND,	O_WRONLY,		"w",
74959745Seric 	O_ACCMODE|O_APPEND,	O_WRONLY|O_APPEND,	"a",
75059745Seric 	O_TRUNC,		0,			"w+",
75159745Seric 	O_APPEND,		O_APPEND,		"a+",
75259745Seric 	0,			0,			"r+",
75359745Seric };
75459745Seric 
7556890Seric FILE *
75659745Seric dfopen(filename, omode, cmode)
7576890Seric 	char *filename;
75859745Seric 	int omode;
75959745Seric 	int cmode;
7606890Seric {
7616890Seric 	register int tries;
76259745Seric 	int fd;
76359745Seric 	register struct omodes *om;
76459431Seric 	struct stat st;
7656890Seric 
76659745Seric 	for (om = OpenModes; om->mask != 0; om++)
76759745Seric 		if ((omode & om->mask) == om->mode)
76859745Seric 			break;
76959745Seric 
7706890Seric 	for (tries = 0; tries < 10; tries++)
7716890Seric 	{
77225618Seric 		sleep((unsigned) (10 * tries));
7736890Seric 		errno = 0;
77459745Seric 		fd = open(filename, omode, cmode);
77559745Seric 		if (fd >= 0)
7766890Seric 			break;
77766017Seric 		switch (errno)
77866017Seric 		{
77966017Seric 		  case ENFILE:		/* system file table full */
78066017Seric 		  case EINTR:		/* interrupted syscall */
78166017Seric #ifdef ETXTBSY
78266017Seric 		  case ETXTBSY:		/* Apollo: net file locked */
78366017Seric #endif
78466017Seric 			continue;
78566017Seric 		}
78666017Seric 		break;
7876890Seric 	}
78859745Seric 	if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
78956328Seric 	{
79056328Seric 		int locktype;
79156328Seric 
79256328Seric 		/* lock the file to avoid accidental conflicts */
79359745Seric 		if ((omode & O_ACCMODE) != O_RDONLY)
79456328Seric 			locktype = LOCK_EX;
79556328Seric 		else
79656328Seric 			locktype = LOCK_SH;
79764335Seric 		(void) lockfile(fd, filename, NULL, locktype);
79856328Seric 		errno = 0;
79956328Seric 	}
80063787Seric 	if (fd < 0)
80163787Seric 		return NULL;
80263787Seric 	else
80363787Seric 		return fdopen(fd, om->farg);
8046890Seric }
8057124Seric /*
8067124Seric **  PUTLINE -- put a line like fputs obeying SMTP conventions
8077124Seric **
8087753Seric **	This routine always guarantees outputing a newline (or CRLF,
8097753Seric **	as appropriate) at the end of the string.
8107753Seric **
8117124Seric **	Parameters:
8127124Seric **		l -- line to put.
81365870Seric **		mci -- the mailer connection information.
8147124Seric **
8157124Seric **	Returns:
8167124Seric **		none
8177124Seric **
8187124Seric **	Side Effects:
8197124Seric **		output of l to fp.
8207124Seric */
8217124Seric 
82265870Seric putline(l, mci)
8237753Seric 	register char *l;
82465870Seric 	register MCI *mci;
8257124Seric {
82668515Seric 	extern void putxline();
82768515Seric 
82868515Seric 	putxline(l, mci, TRUE);
82968515Seric }
83068515Seric 
83168515Seric void
83268515Seric putxline(l, mci, mapfrom)
83368515Seric 	register char *l;
83468515Seric 	register MCI *mci;
83568515Seric 	bool mapfrom;
83668515Seric {
8377124Seric 	register char *p;
83847157Sbostic 	register char svchar;
83966004Seric 	int slop = 0;
8407124Seric 
84111275Seric 	/* strip out 0200 bits -- these can look like TELNET protocol */
84265870Seric 	if (bitset(MCIF_7BIT, mci->mci_flags))
84311275Seric 	{
84461707Seric 		for (p = l; (svchar = *p) != '\0'; ++p)
84561707Seric 			if (bitset(0200, svchar))
84647157Sbostic 				*p = svchar &~ 0200;
84711275Seric 	}
84811275Seric 
8497753Seric 	do
8507124Seric 	{
8517753Seric 		/* find the end of the line */
85256795Seric 		p = strchr(l, '\n');
8537753Seric 		if (p == NULL)
8547753Seric 			p = &l[strlen(l)];
8557124Seric 
85663753Seric 		if (TrafficLogFile != NULL)
85763753Seric 			fprintf(TrafficLogFile, "%05d >>> ", getpid());
85863753Seric 
8597753Seric 		/* check for line overflow */
86065870Seric 		while (mci->mci_mailer->m_linelimit > 0 &&
86166004Seric 		       (p - l + slop) > mci->mci_mailer->m_linelimit)
8627753Seric 		{
86366004Seric 			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
8647124Seric 
8657753Seric 			svchar = *q;
8667753Seric 			*q = '\0';
86766004Seric 			if (l[0] == '.' && slop == 0 &&
86866004Seric 			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
86963753Seric 			{
87065870Seric 				(void) putc('.', mci->mci_out);
87163753Seric 				if (TrafficLogFile != NULL)
87263753Seric 					(void) putc('.', TrafficLogFile);
87363753Seric 			}
87468515Seric 			else if (l[0] == 'F' && slop == 0 && mapfrom &&
87568515Seric 				 strncmp(l, "From ", 5) == 0 &
87668515Seric 				 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
87768515Seric 			{
87868515Seric 				(void) putc('>', mci->mci_out);
87968515Seric 				if (TrafficLogFile != NULL)
88068515Seric 					(void) putc('>', TrafficLogFile);
88168515Seric 			}
88265870Seric 			fputs(l, mci->mci_out);
88365870Seric 			(void) putc('!', mci->mci_out);
88465870Seric 			fputs(mci->mci_mailer->m_eol, mci->mci_out);
88566004Seric 			(void) putc(' ', mci->mci_out);
88663753Seric 			if (TrafficLogFile != NULL)
88766004Seric 				fprintf(TrafficLogFile, "%s!\n%05d >>>  ",
88863753Seric 					l, getpid());
8897753Seric 			*q = svchar;
8907753Seric 			l = q;
89166004Seric 			slop = 1;
8927753Seric 		}
8937124Seric 
8947753Seric 		/* output last part */
89566004Seric 		if (l[0] == '.' && slop == 0 &&
89666004Seric 		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
89763753Seric 		{
89865870Seric 			(void) putc('.', mci->mci_out);
89963753Seric 			if (TrafficLogFile != NULL)
90063753Seric 				(void) putc('.', TrafficLogFile);
90163753Seric 		}
90263753Seric 		if (TrafficLogFile != NULL)
90363753Seric 			fprintf(TrafficLogFile, "%.*s\n", p - l, l);
90447157Sbostic 		for ( ; l < p; ++l)
90565870Seric 			(void) putc(*l, mci->mci_out);
90665870Seric 		fputs(mci->mci_mailer->m_eol, mci->mci_out);
9077753Seric 		if (*l == '\n')
90847157Sbostic 			++l;
9097753Seric 	} while (l[0] != '\0');
9107124Seric }
9117676Seric /*
9127676Seric **  XUNLINK -- unlink a file, doing logging as appropriate.
9137676Seric **
9147676Seric **	Parameters:
9157676Seric **		f -- name of file to unlink.
9167676Seric **
9177676Seric **	Returns:
9187676Seric **		none.
9197676Seric **
9207676Seric **	Side Effects:
9217676Seric **		f is unlinked.
9227676Seric */
9237676Seric 
9247676Seric xunlink(f)
9257676Seric 	char *f;
9267676Seric {
9277676Seric 	register int i;
9287676Seric 
9297676Seric # ifdef LOG
93058020Seric 	if (LogLevel > 98)
93158020Seric 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
93256795Seric # endif /* LOG */
9337676Seric 
9347676Seric 	i = unlink(f);
9357676Seric # ifdef LOG
93658020Seric 	if (i < 0 && LogLevel > 97)
9377942Seric 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
93856795Seric # endif /* LOG */
9397676Seric }
9407685Seric /*
94158680Seric **  XFCLOSE -- close a file, doing logging as appropriate.
94258680Seric **
94358680Seric **	Parameters:
94458680Seric **		fp -- file pointer for the file to close
94558680Seric **		a, b -- miscellaneous crud to print for debugging
94658680Seric **
94758680Seric **	Returns:
94858680Seric **		none.
94958680Seric **
95058680Seric **	Side Effects:
95158680Seric **		fp is closed.
95258680Seric */
95358680Seric 
95458680Seric xfclose(fp, a, b)
95558680Seric 	FILE *fp;
95658680Seric 	char *a, *b;
95758680Seric {
95858796Seric 	if (tTd(53, 99))
95958680Seric 		printf("xfclose(%x) %s %s\n", fp, a, b);
96064401Seric #ifdef XDEBUG
96164401Seric 	if (fileno(fp) == 1)
96264401Seric 		syserr("xfclose(%s %s): fd = 1", a, b);
96364401Seric #endif
96458796Seric 	if (fclose(fp) < 0 && tTd(53, 99))
96558680Seric 		printf("xfclose FAILURE: %s\n", errstring(errno));
96658680Seric }
96758680Seric /*
96814885Seric **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
9697685Seric **
9707685Seric **	Parameters:
9717685Seric **		buf -- place to put the input line.
9727685Seric **		siz -- size of buf.
9737685Seric **		fp -- file to read from.
97457384Seric **		timeout -- the timeout before error occurs.
97561093Seric **		during -- what we are trying to read (for error messages).
9767685Seric **
9777685Seric **	Returns:
97815533Seric **		NULL on error (including timeout).  This will also leave
97915533Seric **			buf containing a null string.
9807685Seric **		buf otherwise.
9817685Seric **
9827685Seric **	Side Effects:
9837685Seric **		none.
9847685Seric */
9857685Seric 
98614885Seric static jmp_buf	CtxReadTimeout;
98768481Seric static void	readtimeout();
9887685Seric 
9897685Seric char *
99061093Seric sfgets(buf, siz, fp, timeout, during)
9917685Seric 	char *buf;
9927685Seric 	int siz;
9937685Seric 	FILE *fp;
99457384Seric 	time_t timeout;
99561093Seric 	char *during;
9967685Seric {
9977942Seric 	register EVENT *ev = NULL;
9987685Seric 	register char *p;
9997685Seric 
100066332Seric 	if (fp == NULL)
100166332Seric 	{
100266332Seric 		buf[0] = '\0';
100366332Seric 		return NULL;
100466332Seric 	}
100566332Seric 
100614885Seric 	/* set the timeout */
100757384Seric 	if (timeout != 0)
100814885Seric 	{
100914885Seric 		if (setjmp(CtxReadTimeout) != 0)
101014885Seric 		{
101136233Skarels # ifdef LOG
101236230Skarels 			syslog(LOG_NOTICE,
101361093Seric 			    "timeout waiting for input from %s during %s\n",
101461093Seric 			    CurHostName? CurHostName: "local", during);
101536233Skarels # endif
101636230Skarels 			errno = 0;
101761093Seric 			usrerr("451 timeout waiting for input during %s",
101861093Seric 				during);
101919037Seric 			buf[0] = '\0';
102063753Seric #ifdef XDEBUG
102163753Seric 			checkfd012(during);
102263753Seric #endif
102314885Seric 			return (NULL);
102414885Seric 		}
102568481Seric 		ev = setevent(timeout, readtimeout, 0);
102614885Seric 	}
102714885Seric 
102814885Seric 	/* try to read */
102915533Seric 	p = NULL;
103065190Seric 	while (!feof(fp) && !ferror(fp))
10317942Seric 	{
10327942Seric 		errno = 0;
10337942Seric 		p = fgets(buf, siz, fp);
103465190Seric 		if (p != NULL || errno != EINTR)
103565186Seric 			break;
103665190Seric 		clearerr(fp);
103715533Seric 	}
103814885Seric 
103914885Seric 	/* clear the event if it has not sprung */
104068481Seric 	clrevent(ev);
104114885Seric 
104214885Seric 	/* clean up the books and exit */
10438055Seric 	LineNumber++;
104415533Seric 	if (p == NULL)
104516880Seric 	{
104615533Seric 		buf[0] = '\0';
104763753Seric 		if (TrafficLogFile != NULL)
104863753Seric 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
104916880Seric 		return (NULL);
105016880Seric 	}
105163753Seric 	if (TrafficLogFile != NULL)
105263753Seric 		fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
105368481Seric 	if (SevenBitInput)
105468481Seric 	{
105552106Seric 		for (p = buf; *p != '\0'; p++)
105652106Seric 			*p &= ~0200;
105767546Seric 	}
105868481Seric 	else if (!HasEightBits)
105967546Seric 	{
106068481Seric 		for (p = buf; *p != '\0'; p++)
106168481Seric 		{
106268481Seric 			if (bitset(0200, *p))
106368481Seric 			{
106468481Seric 				HasEightBits = TRUE;
106568481Seric 				break;
106668481Seric 			}
106768481Seric 		}
106867546Seric 	}
106968481Seric 	return (buf);
10707685Seric }
10717685Seric 
107268481Seric static void
107366765Seric readtimeout(timeout)
107466765Seric 	time_t timeout;
10757685Seric {
107668481Seric 	longjmp(CtxReadTimeout, 1);
10777685Seric }
10787786Seric /*
10797786Seric **  FGETFOLDED -- like fgets, but know about folded lines.
10807786Seric **
10817786Seric **	Parameters:
10827786Seric **		buf -- place to put result.
10837786Seric **		n -- bytes available.
10847786Seric **		f -- file to read from.
10857786Seric **
10867786Seric **	Returns:
108757135Seric **		input line(s) on success, NULL on error or EOF.
108857135Seric **		This will normally be buf -- unless the line is too
108957135Seric **			long, when it will be xalloc()ed.
10907786Seric **
10917786Seric **	Side Effects:
10927786Seric **		buf gets lines from f, with continuation lines (lines
10937786Seric **		with leading white space) appended.  CRLF's are mapped
10947786Seric **		into single newlines.  Any trailing NL is stripped.
10957786Seric */
10967786Seric 
10977786Seric char *
10987786Seric fgetfolded(buf, n, f)
10997786Seric 	char *buf;
11007786Seric 	register int n;
11017786Seric 	FILE *f;
11027786Seric {
11037786Seric 	register char *p = buf;
110457135Seric 	char *bp = buf;
11057786Seric 	register int i;
11067786Seric 
11077786Seric 	n--;
110817350Seric 	while ((i = getc(f)) != EOF)
11097786Seric 	{
111017350Seric 		if (i == '\r')
111117350Seric 		{
111217350Seric 			i = getc(f);
111317350Seric 			if (i != '\n')
111417350Seric 			{
111517350Seric 				if (i != EOF)
111623105Seric 					(void) ungetc(i, f);
111717350Seric 				i = '\r';
111817350Seric 			}
111917350Seric 		}
112057135Seric 		if (--n <= 0)
112157135Seric 		{
112257135Seric 			/* allocate new space */
112357135Seric 			char *nbp;
112457135Seric 			int nn;
112557135Seric 
112657135Seric 			nn = (p - bp);
112757232Seric 			if (nn < MEMCHUNKSIZE)
112857135Seric 				nn *= 2;
112957135Seric 			else
113057232Seric 				nn += MEMCHUNKSIZE;
113157135Seric 			nbp = xalloc(nn);
113257135Seric 			bcopy(bp, nbp, p - bp);
113357135Seric 			p = &nbp[p - bp];
113457135Seric 			if (bp != buf)
113557135Seric 				free(bp);
113657135Seric 			bp = nbp;
113757135Seric 			n = nn - (p - bp);
113857135Seric 		}
113957135Seric 		*p++ = i;
114017350Seric 		if (i == '\n')
114117350Seric 		{
114217350Seric 			LineNumber++;
114317350Seric 			i = getc(f);
114417350Seric 			if (i != EOF)
114523105Seric 				(void) ungetc(i, f);
114617350Seric 			if (i != ' ' && i != '\t')
114752647Seric 				break;
114817350Seric 		}
11497786Seric 	}
115057135Seric 	if (p == bp)
115152647Seric 		return (NULL);
115252647Seric 	*--p = '\0';
115357135Seric 	return (bp);
11547786Seric }
11557860Seric /*
11567886Seric **  CURTIME -- return current time.
11577886Seric **
11587886Seric **	Parameters:
11597886Seric **		none.
11607886Seric **
11617886Seric **	Returns:
11627886Seric **		the current time.
11637886Seric **
11647886Seric **	Side Effects:
11657886Seric **		none.
11667886Seric */
11677886Seric 
11687886Seric time_t
11697886Seric curtime()
11707886Seric {
11717886Seric 	auto time_t t;
11727886Seric 
11737886Seric 	(void) time(&t);
11747886Seric 	return (t);
11757886Seric }
11768264Seric /*
11778264Seric **  ATOBOOL -- convert a string representation to boolean.
11788264Seric **
11798264Seric **	Defaults to "TRUE"
11808264Seric **
11818264Seric **	Parameters:
11828264Seric **		s -- string to convert.  Takes "tTyY" as true,
11838264Seric **			others as false.
11848264Seric **
11858264Seric **	Returns:
11868264Seric **		A boolean representation of the string.
11878264Seric **
11888264Seric **	Side Effects:
11898264Seric **		none.
11908264Seric */
11918264Seric 
11928264Seric bool
11938264Seric atobool(s)
11948264Seric 	register char *s;
11958264Seric {
119663833Seric 	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
11978264Seric 		return (TRUE);
11988264Seric 	return (FALSE);
11998264Seric }
12009048Seric /*
12019048Seric **  ATOOCT -- convert a string representation to octal.
12029048Seric **
12039048Seric **	Parameters:
12049048Seric **		s -- string to convert.
12059048Seric **
12069048Seric **	Returns:
12079048Seric **		An integer representing the string interpreted as an
12089048Seric **		octal number.
12099048Seric **
12109048Seric **	Side Effects:
12119048Seric **		none.
12129048Seric */
12139048Seric 
12149048Seric atooct(s)
12159048Seric 	register char *s;
12169048Seric {
12179048Seric 	register int i = 0;
12189048Seric 
12199048Seric 	while (*s >= '0' && *s <= '7')
12209048Seric 		i = (i << 3) | (*s++ - '0');
12219048Seric 	return (i);
12229048Seric }
12239376Seric /*
12249376Seric **  WAITFOR -- wait for a particular process id.
12259376Seric **
12269376Seric **	Parameters:
12279376Seric **		pid -- process id to wait for.
12289376Seric **
12299376Seric **	Returns:
12309376Seric **		status of pid.
12319376Seric **		-1 if pid never shows up.
12329376Seric **
12339376Seric **	Side Effects:
12349376Seric **		none.
12359376Seric */
12369376Seric 
123764562Seric int
12389376Seric waitfor(pid)
12399376Seric 	int pid;
12409376Seric {
124164562Seric #ifdef WAITUNION
124264562Seric 	union wait st;
124364562Seric #else
12449376Seric 	auto int st;
124564562Seric #endif
12469376Seric 	int i;
12479376Seric 
12489376Seric 	do
12499376Seric 	{
12509376Seric 		errno = 0;
12519376Seric 		i = wait(&st);
12529376Seric 	} while ((i >= 0 || errno == EINTR) && i != pid);
12539376Seric 	if (i < 0)
125464562Seric 		return -1;
125564562Seric #ifdef WAITUNION
125664562Seric 	return st.w_status;
125764562Seric #else
125864562Seric 	return st;
125964562Seric #endif
12609376Seric }
12619376Seric /*
126210685Seric **  BITINTERSECT -- tell if two bitmaps intersect
126310685Seric **
126410685Seric **	Parameters:
126510685Seric **		a, b -- the bitmaps in question
126610685Seric **
126710685Seric **	Returns:
126810685Seric **		TRUE if they have a non-null intersection
126910685Seric **		FALSE otherwise
127010685Seric **
127110685Seric **	Side Effects:
127210685Seric **		none.
127310685Seric */
127410685Seric 
127510685Seric bool
127610685Seric bitintersect(a, b)
127710685Seric 	BITMAP a;
127810685Seric 	BITMAP b;
127910685Seric {
128010685Seric 	int i;
128110685Seric 
128210685Seric 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
128310685Seric 		if ((a[i] & b[i]) != 0)
128410685Seric 			return (TRUE);
128510685Seric 	return (FALSE);
128610685Seric }
128710685Seric /*
128810685Seric **  BITZEROP -- tell if a bitmap is all zero
128910685Seric **
129010685Seric **	Parameters:
129110685Seric **		map -- the bit map to check
129210685Seric **
129310685Seric **	Returns:
129410685Seric **		TRUE if map is all zero.
129510685Seric **		FALSE if there are any bits set in map.
129610685Seric **
129710685Seric **	Side Effects:
129810685Seric **		none.
129910685Seric */
130010685Seric 
130110685Seric bool
130210685Seric bitzerop(map)
130310685Seric 	BITMAP map;
130410685Seric {
130510685Seric 	int i;
130610685Seric 
130710685Seric 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
130810685Seric 		if (map[i] != 0)
130910685Seric 			return (FALSE);
131010685Seric 	return (TRUE);
131110685Seric }
131258247Seric /*
131358318Seric **  STRCONTAINEDIN -- tell if one string is contained in another
131458318Seric **
131558318Seric **	Parameters:
131658318Seric **		a -- possible substring.
131758318Seric **		b -- possible superstring.
131858318Seric **
131958318Seric **	Returns:
132058318Seric **		TRUE if a is contained in b.
132158318Seric **		FALSE otherwise.
132258318Seric */
132358318Seric 
132458318Seric bool
132558318Seric strcontainedin(a, b)
132658318Seric 	register char *a;
132758318Seric 	register char *b;
132858318Seric {
132965012Seric 	int la;
133065012Seric 	int lb;
133165012Seric 	int c;
133258318Seric 
133365012Seric 	la = strlen(a);
133465012Seric 	lb = strlen(b);
133565012Seric 	c = *a;
133665012Seric 	if (isascii(c) && isupper(c))
133765012Seric 		c = tolower(c);
133865012Seric 	for (; lb-- >= la; b++)
133958318Seric 	{
134065012Seric 		if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
134165012Seric 			continue;
134265012Seric 		if (strncasecmp(a, b, la) == 0)
134358318Seric 			return TRUE;
134458318Seric 	}
134565012Seric 	return FALSE;
134658318Seric }
134763753Seric /*
134863753Seric **  CHECKFD012 -- check low numbered file descriptors
134963753Seric **
135063753Seric **	File descriptors 0, 1, and 2 should be open at all times.
135163753Seric **	This routine verifies that, and fixes it if not true.
135263753Seric **
135363753Seric **	Parameters:
135463753Seric **		where -- a tag printed if the assertion failed
135563753Seric **
135663753Seric **	Returns:
135763753Seric **		none
135863753Seric */
135963753Seric 
136063753Seric checkfd012(where)
136163753Seric 	char *where;
136263753Seric {
136363753Seric #ifdef XDEBUG
136463753Seric 	register int i;
136563753Seric 	struct stat stbuf;
136663753Seric 
136763753Seric 	for (i = 0; i < 3; i++)
136863753Seric 	{
136964735Seric 		if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
137063753Seric 		{
137163753Seric 			/* oops.... */
137263753Seric 			int fd;
137363753Seric 
137463753Seric 			syserr("%s: fd %d not open", where, i);
137563753Seric 			fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
137663753Seric 			if (fd != i)
137763753Seric 			{
137863753Seric 				(void) dup2(fd, i);
137963753Seric 				(void) close(fd);
138063753Seric 			}
138163753Seric 		}
138263753Seric 	}
138363937Seric #endif /* XDEBUG */
138463753Seric }
138564725Seric /*
138664725Seric **  PRINTOPENFDS -- print the open file descriptors (for debugging)
138764725Seric **
138864725Seric **	Parameters:
138964725Seric **		logit -- if set, send output to syslog; otherwise
139064725Seric **			print for debugging.
139164725Seric **
139264725Seric **	Returns:
139364725Seric **		none.
139464725Seric */
139564725Seric 
139664725Seric #include <arpa/inet.h>
139764725Seric 
139864725Seric printopenfds(logit)
139964725Seric 	bool logit;
140064725Seric {
140164725Seric 	register int fd;
140264743Seric 	extern int DtableSize;
140364743Seric 
140464743Seric 	for (fd = 0; fd < DtableSize; fd++)
140564743Seric 		dumpfd(fd, FALSE, logit);
140664743Seric }
140764743Seric /*
140864743Seric **  DUMPFD -- dump a file descriptor
140964743Seric **
141064743Seric **	Parameters:
141164743Seric **		fd -- the file descriptor to dump.
141264743Seric **		printclosed -- if set, print a notification even if
141364743Seric **			it is closed; otherwise print nothing.
141464743Seric **		logit -- if set, send output to syslog instead of stdout.
141564743Seric */
141664743Seric 
141764743Seric dumpfd(fd, printclosed, logit)
141864743Seric 	int fd;
141964743Seric 	bool printclosed;
142064743Seric 	bool logit;
142164743Seric {
142264725Seric 	register struct hostent *hp;
142364725Seric 	register char *p;
142466747Seric 	char *fmtstr;
142564743Seric 	struct sockaddr_in sin;
142664743Seric 	auto int slen;
142764743Seric 	struct stat st;
142864725Seric 	char buf[200];
142964725Seric 
143064743Seric 	p = buf;
143164743Seric 	sprintf(p, "%3d: ", fd);
143264743Seric 	p += strlen(p);
143364743Seric 
143464743Seric 	if (fstat(fd, &st) < 0)
143564725Seric 	{
143664743Seric 		if (printclosed || errno != EBADF)
143764743Seric 		{
143864743Seric 			sprintf(p, "CANNOT STAT (%s)", errstring(errno));
143964743Seric 			goto printit;
144064743Seric 		}
144164743Seric 		return;
144264743Seric 	}
144364725Seric 
144464743Seric 	slen = fcntl(fd, F_GETFL, NULL);
144564743Seric 	if (slen != -1)
144664743Seric 	{
144764743Seric 		sprintf(p, "fl=0x%x, ", slen);
144864743Seric 		p += strlen(p);
144964743Seric 	}
145064725Seric 
145164743Seric 	sprintf(p, "mode=%o: ", st.st_mode);
145264743Seric 	p += strlen(p);
145364743Seric 	switch (st.st_mode & S_IFMT)
145464743Seric 	{
145564807Seric #ifdef S_IFSOCK
145664743Seric 	  case S_IFSOCK:
145764743Seric 		sprintf(p, "SOCK ");
145864725Seric 		p += strlen(p);
145964743Seric 		slen = sizeof sin;
146064743Seric 		if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0)
146164743Seric 			sprintf(p, "(badsock)");
146264743Seric 		else
146364725Seric 		{
146468693Seric 			hp = sm_gethostbyaddr((char *) &sin.sin_addr,
146568481Seric 					   INADDRSZ, AF_INET);
146664743Seric 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
146764743Seric 						   : hp->h_name, ntohs(sin.sin_port));
146864743Seric 		}
146964743Seric 		p += strlen(p);
147064743Seric 		sprintf(p, "->");
147164743Seric 		p += strlen(p);
147264743Seric 		slen = sizeof sin;
147364743Seric 		if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0)
147464743Seric 			sprintf(p, "(badsock)");
147564743Seric 		else
147664743Seric 		{
147768693Seric 			hp = sm_gethostbyaddr((char *) &sin.sin_addr,
147868481Seric 					   INADDRSZ, AF_INET);
147964743Seric 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
148064743Seric 						   : hp->h_name, ntohs(sin.sin_port));
148164743Seric 		}
148264743Seric 		break;
148364807Seric #endif
148464725Seric 
148564743Seric 	  case S_IFCHR:
148664743Seric 		sprintf(p, "CHR: ");
148764743Seric 		p += strlen(p);
148864743Seric 		goto defprint;
148964725Seric 
149064743Seric 	  case S_IFBLK:
149164743Seric 		sprintf(p, "BLK: ");
149264743Seric 		p += strlen(p);
149364743Seric 		goto defprint;
149464725Seric 
149566252Seric #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
149665378Seric 	  case S_IFIFO:
149765378Seric 		sprintf(p, "FIFO: ");
149865378Seric 		p += strlen(p);
149965378Seric 		goto defprint;
150065378Seric #endif
150165378Seric 
150265378Seric #ifdef S_IFDIR
150365378Seric 	  case S_IFDIR:
150465378Seric 		sprintf(p, "DIR: ");
150565378Seric 		p += strlen(p);
150665378Seric 		goto defprint;
150765378Seric #endif
150865378Seric 
150965378Seric #ifdef S_IFLNK
151065378Seric 	  case S_IFLNK:
151165378Seric 		sprintf(p, "LNK: ");
151265378Seric 		p += strlen(p);
151365378Seric 		goto defprint;
151465378Seric #endif
151565378Seric 
151664743Seric 	  default:
151764725Seric defprint:
151866785Seric 		if (sizeof st.st_size > sizeof (long))
151966751Seric 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd";
152066747Seric 		else
152166751Seric 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld";
152266747Seric 		sprintf(p, fmtstr,
152364743Seric 			major(st.st_dev), minor(st.st_dev), st.st_ino,
152464743Seric 			st.st_nlink, st.st_uid, st.st_gid, st.st_size);
152564743Seric 		break;
152664743Seric 	}
152764725Seric 
152864743Seric printit:
152966748Seric #ifdef LOG
153064743Seric 	if (logit)
153165006Seric 		syslog(LOG_DEBUG, "%s", buf);
153264743Seric 	else
153366748Seric #endif
153464743Seric 		printf("%s\n", buf);
153564725Seric }
153665015Seric /*
153765015Seric **  SHORTENSTRING -- return short version of a string
153865015Seric **
153965015Seric **	If the string is already short, just return it.  If it is too
154065015Seric **	long, return the head and tail of the string.
154165015Seric **
154265015Seric **	Parameters:
154365015Seric **		s -- the string to shorten.
154465015Seric **		m -- the max length of the string.
154565015Seric **
154665015Seric **	Returns:
154765015Seric **		Either s or a short version of s.
154865015Seric */
154965015Seric 
155065015Seric #ifndef MAXSHORTSTR
155165055Seric # define MAXSHORTSTR	203
155265015Seric #endif
155365015Seric 
155465015Seric char *
155565015Seric shortenstring(s, m)
155665015Seric 	register char *s;
155765015Seric 	int m;
155865015Seric {
155965015Seric 	int l;
156065015Seric 	static char buf[MAXSHORTSTR + 1];
156165015Seric 
156265015Seric 	l = strlen(s);
156365015Seric 	if (l < m)
156465015Seric 		return s;
156565015Seric 	if (m > MAXSHORTSTR)
156665015Seric 		m = MAXSHORTSTR;
156765015Seric 	else if (m < 10)
156865015Seric 	{
156965015Seric 		if (m < 5)
157065015Seric 		{
157165015Seric 			strncpy(buf, s, m);
157265015Seric 			buf[m] = '\0';
157365015Seric 			return buf;
157465015Seric 		}
157565015Seric 		strncpy(buf, s, m - 3);
157665015Seric 		strcpy(buf + m - 3, "...");
157765015Seric 		return buf;
157865015Seric 	}
157965015Seric 	m = (m - 3) / 2;
158065015Seric 	strncpy(buf, s, m);
158165015Seric 	strcpy(buf + m, "...");
158265015Seric 	strcpy(buf + m + 3, s + l - m);
158365015Seric 	return buf;
158465015Seric }
158567848Seric /*
158668481Seric **  GET_COLUMN  -- look up a Column in a line buffer
158768481Seric **
158868481Seric **	Parameters:
158968481Seric **		line -- the raw text line to search.
159068481Seric **		col -- the column number to fetch.
159168481Seric **		delim -- the delimiter between columns.  If null,
159268481Seric **			use white space.
159368481Seric **		buf -- the output buffer.
159468481Seric **
159568481Seric **	Returns:
159668481Seric **		buf if successful.
159768481Seric **		NULL otherwise.
159868481Seric */
159968481Seric 
160068481Seric char *
160168481Seric get_column(line, col, delim, buf)
160268481Seric 	char line[];
160368481Seric 	int col;
160468481Seric 	char delim;
160568481Seric 	char buf[];
160668481Seric {
160768481Seric 	char *p;
160868481Seric 	char *begin, *end;
160968481Seric 	int i;
161068481Seric 	char delimbuf[3];
161168481Seric 
161268481Seric 	if (delim == '\0')
161368481Seric 		strcpy(delimbuf, "\t ");
161468481Seric 	else
161568481Seric 	{
161668481Seric 		delimbuf[0] = delim;
161768481Seric 		delimbuf[1] = '\0';
161868481Seric 	}
161968481Seric 
162068481Seric 	p = line;
162168481Seric 	if (*p == '\0')
162268481Seric 		return NULL;			/* line empty */
162368481Seric 	if (*p == delim && col == 0)
162468481Seric 		return NULL;			/* first column empty */
162568481Seric 
162668481Seric 	begin = line;
162768481Seric 
162868481Seric 	if (col == 0 && delim == '\0')
162968481Seric 	{
163068481Seric 		while (*begin && isspace(*begin))
163168481Seric 			begin++;
163268481Seric 	}
163368481Seric 
163468481Seric 	for (i = 0; i < col; i++)
163568481Seric 	{
163668481Seric 		if ((begin = strpbrk(begin, delimbuf)) == NULL)
163768481Seric 			return NULL;		/* no such column */
163868481Seric 		begin++;
163968481Seric 		if (delim == '\0')
164068481Seric 		{
164168481Seric 			while (*begin && isspace(*begin))
164268481Seric 				begin++;
164368481Seric 		}
164468481Seric 	}
164568481Seric 
164668481Seric 	end = strpbrk(begin, delimbuf);
164768481Seric 	if (end == NULL)
164868481Seric 	{
164968481Seric 		strcpy(buf, begin);
165068481Seric 	}
165168481Seric 	else
165268481Seric 	{
165368481Seric 		strncpy(buf, begin, end - begin);
165468481Seric 		buf[end - begin] = '\0';
165568481Seric 	}
165668481Seric 	return buf;
165768481Seric }
165868481Seric /*
165968267Seric **  CLEANSTRCPY -- copy string keeping out bogus characters
166068267Seric **
166168267Seric **	Parameters:
166268267Seric **		t -- "to" string.
166368267Seric **		f -- "from" string.
166468267Seric **		l -- length of space available in "to" string.
166568267Seric **
166668267Seric **	Returns:
166768267Seric **		none.
166868267Seric */
166968267Seric 
167068267Seric void
167168267Seric cleanstrcpy(t, f, l)
167268267Seric 	register char *t;
167368267Seric 	register char *f;
167468267Seric 	int l;
167568267Seric {
167668281Seric #ifdef LOG
167768281Seric 	/* check for newlines and log if necessary */
167868478Seric 	(void) denlstring(f, TRUE, TRUE);
167968281Seric #endif
168068281Seric 
168168267Seric 	l--;
168268267Seric 	while (l > 0 && *f != '\0')
168368267Seric 	{
168468267Seric 		if (isascii(*f) &&
168568267Seric 		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
168668267Seric 		{
168768267Seric 			l--;
168868267Seric 			*t++ = *f;
168968267Seric 		}
169068267Seric 		f++;
169168267Seric 	}
169268267Seric 	*t = '\0';
169368267Seric }
169468267Seric /*
169568267Seric **  DENLSTRING -- convert newlines in a string to spaces
169668267Seric **
169768267Seric **	Parameters:
169868267Seric **		s -- the input string
169968478Seric **		strict -- if set, don't permit continuation lines.
170068457Seric **		logattacks -- if set, log attempted attacks.
170168267Seric **
170268267Seric **	Returns:
170368267Seric **		A pointer to a version of the string with newlines
170468267Seric **		mapped to spaces.  This should be copied.
170568267Seric */
170668267Seric 
170768267Seric char *
170868478Seric denlstring(s, strict, logattacks)
170968267Seric 	char *s;
171068702Seric 	bool strict;
171168702Seric 	bool logattacks;
171268267Seric {
171368267Seric 	register char *p;
171468267Seric 	int l;
171568267Seric 	static char *bp = NULL;
171668267Seric 	static int bl = 0;
171768267Seric 
171868478Seric 	p = s;
171968478Seric 	while ((p = strchr(p, '\n')) != NULL)
172068478Seric 		if (strict || (*++p != ' ' && *p != '\t'))
172168478Seric 			break;
172268478Seric 	if (p == NULL)
172368267Seric 		return s;
172468267Seric 
172568267Seric 	l = strlen(s) + 1;
172668267Seric 	if (bl < l)
172768267Seric 	{
172868267Seric 		/* allocate more space */
172968267Seric 		if (bp != NULL)
173068267Seric 			free(bp);
173168267Seric 		bp = xalloc(l);
173268267Seric 		bl = l;
173368267Seric 	}
173468267Seric 	strcpy(bp, s);
173568267Seric 	for (p = bp; (p = strchr(p, '\n')) != NULL; )
173668267Seric 		*p++ = ' ';
173768281Seric 
173868481Seric /*
173968281Seric #ifdef LOG
174068457Seric 	if (logattacks)
174168457Seric 	{
174268457Seric 		syslog(LOG_NOTICE, "POSSIBLE ATTACK from %s: newline in string \"%s\"",
174368457Seric 			RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
174468457Seric 			shortenstring(bp, 80));
174568457Seric 	}
174668281Seric #endif
174768481Seric */
174868281Seric 
174968267Seric 	return bp;
175068267Seric }
1751