122717Sdist /*
268839Seric * Copyright (c) 1983, 1995 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*69963Seric static char sccsid[] = "@(#)util.c 8.78 (Berkeley) 06/21/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
3469748Seric void
stripquotes(s)3554983Seric stripquotes(s)
36298Seric char *s;
37298Seric {
38298Seric register char *p;
39298Seric register char *q;
40298Seric register char c;
41298Seric
424101Seric if (s == NULL)
434101Seric return;
444101Seric
4554983Seric p = q = s;
4654983Seric do
47298Seric {
4854983Seric c = *p++;
4954983Seric if (c == '\\')
5054983Seric c = *p++;
5154983Seric else if (c == '"')
5254983Seric continue;
5354983Seric *q++ = c;
5454983Seric } while (c != '\0');
55298Seric }
56298Seric /*
57298Seric ** XALLOC -- Allocate memory and bitch wildly on failure.
58298Seric **
59298Seric ** THIS IS A CLUDGE. This should be made to give a proper
60298Seric ** error -- but after all, what can we do?
61298Seric **
62298Seric ** Parameters:
63298Seric ** sz -- size of area to allocate.
64298Seric **
65298Seric ** Returns:
66298Seric ** pointer to data region.
67298Seric **
68298Seric ** Side Effects:
69298Seric ** Memory is allocated.
70298Seric */
71298Seric
72298Seric char *
xalloc(sz)73298Seric xalloc(sz)
747007Seric register int sz;
75298Seric {
76298Seric register char *p;
77298Seric
7866747Seric /* some systems can't handle size zero mallocs */
7966747Seric if (sz <= 0)
8066747Seric sz = 1;
8166747Seric
8223121Seric p = malloc((unsigned) sz);
83298Seric if (p == NULL)
84298Seric {
8569800Seric syserr("!Out of memory!!");
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 **
copyplist(list,copycont)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 *
copyqueue(addr)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
18569748Seric void
printav(av)1863151Seric printav(av)
1873151Seric register char **av;
1883151Seric {
1893151Seric while (*av != NULL)
1903151Seric {
1918063Seric if (tTd(0, 44))
1928063Seric printf("\n\t%08x=", *av);
1938063Seric else
19423105Seric (void) putchar(' ');
1953151Seric xputs(*av++);
1963151Seric }
19723105Seric (void) putchar('\n');
1983151Seric }
1993151Seric /*
2003151Seric ** LOWER -- turn letter into lower case.
2013151Seric **
2023151Seric ** Parameters:
2033151Seric ** c -- character to turn into lower case.
2043151Seric **
2053151Seric ** Returns:
2063151Seric ** c, in lower case.
2073151Seric **
2083151Seric ** Side Effects:
2093151Seric ** none.
2103151Seric */
2113151Seric
2123151Seric char
lower(c)2133151Seric lower(c)
2143151Seric register char c;
2153151Seric {
21658050Seric return((isascii(c) && isupper(c)) ? tolower(c) : c);
2173151Seric }
2183151Seric /*
2193151Seric ** XPUTS -- put string doing control escapes.
2203151Seric **
2213151Seric ** Parameters:
2223151Seric ** s -- string to put.
2233151Seric **
2243151Seric ** Returns:
2253151Seric ** none.
2263151Seric **
2273151Seric ** Side Effects:
2283151Seric ** output to stdout
2293151Seric */
2303151Seric
23169748Seric void
xputs(s)2323151Seric xputs(s)
23369748Seric register const char *s;
2343151Seric {
23558050Seric register int c;
23651781Seric register struct metamac *mp;
23751781Seric extern struct metamac MetaMacros[];
2383151Seric
2398055Seric if (s == NULL)
2408055Seric {
2418055Seric printf("<null>");
2428055Seric return;
2438055Seric }
24458050Seric while ((c = (*s++ & 0377)) != '\0')
2453151Seric {
2463151Seric if (!isascii(c))
2473151Seric {
24868481Seric if (c == MATCHREPL)
24958050Seric {
25058050Seric putchar('$');
25158050Seric continue;
25258050Seric }
25368481Seric if (c == MACROEXPAND)
25468481Seric {
25568481Seric putchar('$');
25668481Seric if (bitset(0200, *s))
25768481Seric printf("{%s}", macname(*s++ & 0377));
25868481Seric continue;
25968481Seric }
26058050Seric for (mp = MetaMacros; mp->metaname != '\0'; mp++)
26158050Seric {
26258050Seric if ((mp->metaval & 0377) == c)
26358050Seric {
26458050Seric printf("$%c", mp->metaname);
26558050Seric break;
26658050Seric }
26758050Seric }
26858050Seric if (mp->metaname != '\0')
26958050Seric continue;
27023105Seric (void) putchar('\\');
2713151Seric c &= 0177;
2723151Seric }
27357589Seric if (isprint(c))
2743151Seric {
27557589Seric putchar(c);
27657589Seric continue;
27757589Seric }
27852050Seric
27957589Seric /* wasn't a meta-macro -- find another way to print it */
28057589Seric switch (c)
28157589Seric {
28257589Seric case '\n':
28357589Seric c = 'n';
28457589Seric break;
28552050Seric
28657589Seric case '\r':
28757589Seric c = 'r';
28857589Seric break;
28952637Seric
29057589Seric case '\t':
29157589Seric c = 't';
29257589Seric break;
29357589Seric
29457589Seric default:
29557589Seric (void) putchar('^');
29657589Seric (void) putchar(c ^ 0100);
29757589Seric continue;
2983151Seric }
29968500Seric (void) putchar('\\');
30068500Seric (void) putchar(c);
3013151Seric }
3024086Seric (void) fflush(stdout);
3033151Seric }
3043151Seric /*
3053151Seric ** MAKELOWER -- Translate a line into lower case
3063151Seric **
3073151Seric ** Parameters:
3083151Seric ** p -- the string to translate. If NULL, return is
3093151Seric ** immediate.
3103151Seric **
3113151Seric ** Returns:
3123151Seric ** none.
3133151Seric **
3143151Seric ** Side Effects:
3153151Seric ** String pointed to by p is translated to lower case.
3163151Seric **
3173151Seric ** Called By:
3183151Seric ** parse
3193151Seric */
3203151Seric
32168481Seric void
makelower(p)3223151Seric makelower(p)
3233151Seric register char *p;
3243151Seric {
3253151Seric register char c;
3263151Seric
3273151Seric if (p == NULL)
3283151Seric return;
3293151Seric for (; (c = *p) != '\0'; p++)
3303151Seric if (isascii(c) && isupper(c))
33133724Sbostic *p = tolower(c);
3323151Seric }
3334059Seric /*
3345196Seric ** BUILDFNAME -- build full name from gecos style entry.
3354375Seric **
3365196Seric ** This routine interprets the strange entry that would appear
3375196Seric ** in the GECOS field of the password file.
3385196Seric **
3394375Seric ** Parameters:
3405196Seric ** p -- name to build.
3415196Seric ** login -- the login name of this user (for &).
3425196Seric ** buf -- place to put the result.
3434375Seric **
3444375Seric ** Returns:
34565006Seric ** none.
3464375Seric **
3474375Seric ** Side Effects:
3484375Seric ** none.
3494375Seric */
3504375Seric
35169748Seric void
buildfname(gecos,login,buf)35254984Seric buildfname(gecos, login, buf)
35365006Seric register char *gecos;
35465006Seric char *login;
35565006Seric char *buf;
3564375Seric {
35765006Seric register char *p;
35865006Seric register char *bp = buf;
35965006Seric int l;
3604375Seric
36165006Seric if (*gecos == '*')
36265006Seric gecos++;
36365006Seric
36465006Seric /* find length of final string */
36565006Seric l = 0;
36665006Seric for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
36754984Seric {
36865006Seric if (*p == '&')
36965006Seric l += strlen(login);
37065006Seric else
37165006Seric l++;
37254984Seric }
37365006Seric
37465006Seric /* now fill in buf */
37565006Seric for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
3764375Seric {
37765006Seric if (*p == '&')
3784375Seric {
37965006Seric (void) strcpy(bp, login);
38065006Seric *bp = toupper(*bp);
38165006Seric while (*bp != '\0')
38265006Seric bp++;
3834375Seric }
38465006Seric else
38565006Seric *bp++ = *p;
3864375Seric }
3874375Seric *bp = '\0';
3884375Seric }
3894375Seric /*
3904538Seric ** SAFEFILE -- return true if a file exists and is safe for a user.
3914538Seric **
3924538Seric ** Parameters:
3934538Seric ** fn -- filename to check.
39464083Seric ** uid -- user id to compare against.
39564083Seric ** gid -- group id to compare against.
39664083Seric ** uname -- user name to compare against (used for group
39764083Seric ** sets).
39864944Seric ** flags -- modifiers:
39965064Seric ** SFF_MUSTOWN -- "uid" must own this file.
40065064Seric ** SFF_NOSLINK -- file cannot be a symbolic link.
4014538Seric ** mode -- mode bits that must match.
40268494Seric ** st -- if set, points to a stat structure that will
40368494Seric ** get the stat info for the file.
4044538Seric **
4054538Seric ** Returns:
40658247Seric ** 0 if fn exists, is owned by uid, and matches mode.
40758247Seric ** An errno otherwise. The actual errno is cleared.
4084538Seric **
4094538Seric ** Side Effects:
4104538Seric ** none.
4114538Seric */
4124538Seric
41364083Seric #include <grp.h>
41464083Seric
41563581Seric #ifndef S_IXOTH
41663581Seric # define S_IXOTH (S_IEXEC >> 6)
41763581Seric #endif
41863581Seric
41964083Seric #ifndef S_IXGRP
42064083Seric # define S_IXGRP (S_IEXEC >> 3)
42164083Seric #endif
42264083Seric
42363753Seric #ifndef S_IXUSR
42463753Seric # define S_IXUSR (S_IEXEC)
42563753Seric #endif
42663753Seric
42768494Seric #define ST_MODE_NOFILE 0171147 /* unlikely to occur */
42868494Seric
42958247Seric int
safefile(fn,uid,gid,uname,flags,mode,st)43068494Seric safefile(fn, uid, gid, uname, flags, mode, st)
4314538Seric char *fn;
43255372Seric uid_t uid;
43364083Seric gid_t gid;
43464083Seric char *uname;
43564944Seric int flags;
4364538Seric int mode;
43768494Seric struct stat *st;
4384538Seric {
43963581Seric register char *p;
44064083Seric register struct group *gr = NULL;
4414538Seric struct stat stbuf;
4424538Seric
44363581Seric if (tTd(54, 4))
44464944Seric printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
44564944Seric fn, uid, gid, flags, mode);
44663581Seric errno = 0;
44768494Seric if (st == NULL)
44868494Seric st = &stbuf;
44963581Seric
45068481Seric if (!bitset(SFF_NOPATHCHECK, flags) ||
45168481Seric (uid == 0 && !bitset(SFF_ROOTOK, flags)))
45263581Seric {
45368481Seric /* check the path to the file for acceptability */
45468481Seric for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
45565225Seric {
45668481Seric *p = '\0';
45768481Seric if (stat(fn, &stbuf) < 0)
45868481Seric break;
45968513Seric if (uid == 0 && bitset(S_IWGRP|S_IWOTH, stbuf.st_mode))
46068513Seric message("051 WARNING: writable directory %s",
46168513Seric fn);
46268481Seric if (uid == 0 && !bitset(SFF_ROOTOK, flags))
46368481Seric {
46468481Seric if (bitset(S_IXOTH, stbuf.st_mode))
46568481Seric continue;
46668481Seric break;
46768481Seric }
46868481Seric if (stbuf.st_uid == uid &&
46968481Seric bitset(S_IXUSR, stbuf.st_mode))
47065225Seric continue;
47168481Seric if (stbuf.st_gid == gid &&
47268481Seric bitset(S_IXGRP, stbuf.st_mode))
47368481Seric continue;
47468481Seric #ifndef NO_GROUP_SET
47568481Seric if (uname != NULL &&
47668481Seric ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
47768481Seric (gr = getgrgid(stbuf.st_gid)) != NULL))
47868481Seric {
47968481Seric register char **gp;
48068481Seric
48168481Seric for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
48268481Seric if (strcmp(*gp, uname) == 0)
48368481Seric break;
48468481Seric if (gp != NULL && *gp != NULL &&
48568481Seric bitset(S_IXGRP, stbuf.st_mode))
48668481Seric continue;
48768481Seric }
48868481Seric #endif
48968481Seric if (!bitset(S_IXOTH, stbuf.st_mode))
49068481Seric break;
49168478Seric }
49268481Seric if (p != NULL)
49363581Seric {
49468481Seric int ret = errno;
49563581Seric
49668481Seric if (ret == 0)
49768481Seric ret = EACCES;
49868481Seric if (tTd(54, 4))
49968481Seric printf("\t[dir %s] %s\n", fn, errstring(ret));
50068481Seric *p = '/';
50168481Seric return ret;
50263581Seric }
50363581Seric }
50463581Seric
50564944Seric #ifdef HASLSTAT
50668494Seric if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
50768494Seric : stat(fn, st)) < 0)
50864944Seric #else
50968494Seric if (stat(fn, st) < 0)
51064944Seric #endif
51158247Seric {
51258247Seric int ret = errno;
51358247Seric
51463581Seric if (tTd(54, 4))
51564083Seric printf("\t%s\n", errstring(ret));
51663581Seric
51758247Seric errno = 0;
51868494Seric if (!bitset(SFF_CREAT, flags))
51968494Seric return ret;
52068494Seric
52168494Seric /* check to see if legal to create the file */
52268494Seric p = strrchr(fn, '/');
52368494Seric if (p == NULL)
52468494Seric return ENOTDIR;
52568494Seric *p = '\0';
52668494Seric if (stat(fn, &stbuf) >= 0)
52768494Seric {
52868494Seric int md = S_IWRITE|S_IEXEC;
52968494Seric if (stbuf.st_uid != uid)
53068494Seric md >>= 6;
53168494Seric if ((stbuf.st_mode & md) != md)
53268494Seric errno = EACCES;
53368494Seric }
53468494Seric ret = errno;
53568494Seric if (tTd(54, 4))
53668494Seric printf("\t[final dir %s uid %d mode %o] %s\n",
53768494Seric fn, stbuf.st_uid, stbuf.st_mode,
53868494Seric errstring(ret));
53968494Seric *p = '/';
54068494Seric st->st_mode = ST_MODE_NOFILE;
54158247Seric return ret;
54258247Seric }
54364944Seric
54464944Seric #ifdef S_ISLNK
54568494Seric if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
54664944Seric {
54764944Seric if (tTd(54, 4))
54868494Seric printf("\t[slink mode %o]\tEPERM\n", st->st_mode);
54964944Seric return EPERM;
55064944Seric }
55164944Seric #endif
55268513Seric if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
55368513Seric {
55468513Seric if (tTd(54, 4))
55568513Seric printf("\t[non-reg mode %o]\tEPERM\n", st->st_mode);
55668513Seric return EPERM;
55768513Seric }
55868494Seric if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && bitset(0111, st->st_mode))
55968494Seric {
56068494Seric if (tTd(29, 5))
56168494Seric printf("failed (mode %o: x bits)\n", st->st_mode);
56268513Seric return EPERM;
56368494Seric }
56468494Seric
56568494Seric if (bitset(SFF_SETUIDOK, flags))
56668494Seric {
56769952Seric #ifdef SUID_ROOT_FILES_OK
56869952Seric if (bitset(S_ISUID, st->st_mode))
56969952Seric #else
57069952Seric if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0)
57169952Seric #endif
57268494Seric {
57368494Seric uid = st->st_uid;
57468494Seric uname = NULL;
57568494Seric }
57669952Seric #ifdef SUID_ROOT_FILES_OK
57769952Seric if (bitset(S_ISGID, st->st_mode))
57869952Seric #else
57969952Seric if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
58069952Seric #endif
58168494Seric gid = st->st_gid;
58268494Seric }
58368494Seric
58465139Seric if (uid == 0 && !bitset(SFF_ROOTOK, flags))
58563581Seric mode >>= 6;
58668494Seric else if (st->st_uid != uid)
58764084Seric {
58864084Seric mode >>= 3;
58968494Seric if (st->st_gid == gid)
59064084Seric ;
59164084Seric #ifndef NO_GROUP_SET
59264084Seric else if (uname != NULL &&
59368494Seric ((gr != NULL && gr->gr_gid == st->st_gid) ||
59468494Seric (gr = getgrgid(st->st_gid)) != NULL))
59564084Seric {
59664084Seric register char **gp;
59764084Seric
59864084Seric for (gp = gr->gr_mem; *gp != NULL; gp++)
59964084Seric if (strcmp(*gp, uname) == 0)
60064084Seric break;
60164084Seric if (*gp == NULL)
60264084Seric mode >>= 3;
60364084Seric }
60464084Seric #endif
60564084Seric else
60664084Seric mode >>= 3;
60764084Seric }
60863581Seric if (tTd(54, 4))
60964084Seric printf("\t[uid %d, stat %o, mode %o] ",
61068494Seric st->st_uid, st->st_mode, mode);
61168494Seric if ((st->st_uid == uid || st->st_uid == 0 ||
61265064Seric !bitset(SFF_MUSTOWN, flags)) &&
61368494Seric (st->st_mode & mode) == mode)
61463581Seric {
61563581Seric if (tTd(54, 4))
61664083Seric printf("\tOK\n");
61758247Seric return 0;
61863581Seric }
61963581Seric if (tTd(54, 4))
62064083Seric printf("\tEACCES\n");
62163581Seric return EACCES;
6224538Seric }
6234538Seric /*
62468494Seric ** SAFEFOPEN -- do a file open with extra checking
62568494Seric **
62668494Seric ** Parameters:
62768494Seric ** fn -- the file name to open.
62868494Seric ** omode -- the open-style mode flags.
62968494Seric ** cmode -- the create-style mode flags.
63068494Seric ** sff -- safefile flags.
63168494Seric **
63268494Seric ** Returns:
63368494Seric ** Same as fopen.
63468494Seric */
63568494Seric
63668703Seric #ifndef O_ACCMODE
63768703Seric # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
63868703Seric #endif
63968703Seric
64068494Seric FILE *
safefopen(fn,omode,cmode,sff)64168494Seric safefopen(fn, omode, cmode, sff)
64268494Seric char *fn;
64368494Seric int omode;
64468494Seric int cmode;
64568494Seric int sff;
64668494Seric {
64768494Seric int rval;
64868494Seric FILE *fp;
64968494Seric int smode;
65068494Seric struct stat stb, sta;
65168494Seric
65268494Seric if (bitset(O_CREAT, omode))
65368494Seric sff |= SFF_CREAT;
65468494Seric smode = 0;
65568494Seric switch (omode & O_ACCMODE)
65668494Seric {
65768494Seric case O_RDONLY:
65868494Seric smode = S_IREAD;
65968494Seric break;
66068494Seric
66168494Seric case O_WRONLY:
66268494Seric smode = S_IWRITE;
66368494Seric break;
66468494Seric
66568494Seric case O_RDWR:
66668494Seric smode = S_IREAD|S_IWRITE;
66768494Seric break;
66868494Seric
66968494Seric default:
67068494Seric smode = 0;
67168494Seric break;
67268494Seric }
67368513Seric if (bitset(SFF_OPENASROOT, sff))
67468513Seric rval = safefile(fn, 0, 0, NULL, sff, smode, &stb);
67568513Seric else
67668513Seric rval = safefile(fn, RealUid, RealGid, RealUserName,
67768513Seric sff, smode, &stb);
67868494Seric if (rval != 0)
67968494Seric {
68068494Seric errno = rval;
68168494Seric return NULL;
68268494Seric }
68368494Seric if (stb.st_mode == ST_MODE_NOFILE)
68468494Seric omode |= O_EXCL;
68568494Seric
68668494Seric fp = dfopen(fn, omode, cmode);
68768494Seric if (fp == NULL)
68868494Seric return NULL;
68968494Seric if (bitset(O_EXCL, omode))
69068494Seric return fp;
69168494Seric if (fstat(fileno(fp), &sta) < 0 ||
69268494Seric sta.st_nlink != stb.st_nlink ||
69368494Seric sta.st_dev != stb.st_dev ||
69468494Seric sta.st_ino != stb.st_ino ||
69568494Seric sta.st_uid != stb.st_uid ||
69668494Seric sta.st_gid != stb.st_gid)
69768494Seric {
69868494Seric syserr("554 cannot open: file %s changed after open", fn);
69968513Seric fclose(fp);
70068494Seric errno = EPERM;
70168494Seric return NULL;
70268494Seric }
70368494Seric return fp;
70468494Seric }
70568494Seric /*
7064557Seric ** FIXCRLF -- fix <CR><LF> in line.
7074557Seric **
7084557Seric ** Looks for the <CR><LF> combination and turns it into the
7094557Seric ** UNIX canonical <NL> character. It only takes one line,
7104557Seric ** i.e., it is assumed that the first <NL> found is the end
7114557Seric ** of the line.
7124557Seric **
7134557Seric ** Parameters:
7144557Seric ** line -- the line to fix.
7154557Seric ** stripnl -- if true, strip the newline also.
7164557Seric **
7174557Seric ** Returns:
7184557Seric ** none.
7194557Seric **
7204557Seric ** Side Effects:
7214557Seric ** line is changed in place.
7224557Seric */
7234557Seric
72469748Seric void
fixcrlf(line,stripnl)7254557Seric fixcrlf(line, stripnl)
7264557Seric char *line;
7274557Seric bool stripnl;
7284557Seric {
7294557Seric register char *p;
7304557Seric
73156795Seric p = strchr(line, '\n');
7324557Seric if (p == NULL)
7334557Seric return;
73436291Sbostic if (p > line && p[-1] == '\r')
7354557Seric p--;
7364557Seric if (!stripnl)
7374557Seric *p++ = '\n';
7384557Seric *p = '\0';
7394557Seric }
7404557Seric /*
7416890Seric ** DFOPEN -- determined file open
7426890Seric **
7436890Seric ** This routine has the semantics of fopen, except that it will
7446890Seric ** keep trying a few times to make this happen. The idea is that
7456890Seric ** on very loaded systems, we may run out of resources (inodes,
7466890Seric ** whatever), so this tries to get around it.
7476890Seric */
7486890Seric
74959745Seric struct omodes
75059745Seric {
75159745Seric int mask;
75259745Seric int mode;
75359745Seric char *farg;
75459745Seric } OpenModes[] =
75559745Seric {
75659745Seric O_ACCMODE, O_RDONLY, "r",
75759745Seric O_ACCMODE|O_APPEND, O_WRONLY, "w",
75859745Seric O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a",
75959745Seric O_TRUNC, 0, "w+",
76059745Seric O_APPEND, O_APPEND, "a+",
76159745Seric 0, 0, "r+",
76259745Seric };
76359745Seric
7646890Seric FILE *
dfopen(filename,omode,cmode)76559745Seric dfopen(filename, omode, cmode)
7666890Seric char *filename;
76759745Seric int omode;
76859745Seric int cmode;
7696890Seric {
7706890Seric register int tries;
77159745Seric int fd;
77259745Seric register struct omodes *om;
77359431Seric struct stat st;
7746890Seric
77559745Seric for (om = OpenModes; om->mask != 0; om++)
77659745Seric if ((omode & om->mask) == om->mode)
77759745Seric break;
77859745Seric
7796890Seric for (tries = 0; tries < 10; tries++)
7806890Seric {
78125618Seric sleep((unsigned) (10 * tries));
7826890Seric errno = 0;
78359745Seric fd = open(filename, omode, cmode);
78459745Seric if (fd >= 0)
7856890Seric break;
78666017Seric switch (errno)
78766017Seric {
78866017Seric case ENFILE: /* system file table full */
78966017Seric case EINTR: /* interrupted syscall */
79066017Seric #ifdef ETXTBSY
79166017Seric case ETXTBSY: /* Apollo: net file locked */
79266017Seric #endif
79366017Seric continue;
79466017Seric }
79566017Seric break;
7966890Seric }
79759745Seric if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
79856328Seric {
79956328Seric int locktype;
80056328Seric
80156328Seric /* lock the file to avoid accidental conflicts */
80259745Seric if ((omode & O_ACCMODE) != O_RDONLY)
80356328Seric locktype = LOCK_EX;
80456328Seric else
80556328Seric locktype = LOCK_SH;
80664335Seric (void) lockfile(fd, filename, NULL, locktype);
80756328Seric errno = 0;
80856328Seric }
80963787Seric if (fd < 0)
81063787Seric return NULL;
81163787Seric else
81263787Seric return fdopen(fd, om->farg);
8136890Seric }
8147124Seric /*
8157124Seric ** PUTLINE -- put a line like fputs obeying SMTP conventions
8167124Seric **
8177753Seric ** This routine always guarantees outputing a newline (or CRLF,
8187753Seric ** as appropriate) at the end of the string.
8197753Seric **
8207124Seric ** Parameters:
8217124Seric ** l -- line to put.
82265870Seric ** mci -- the mailer connection information.
8237124Seric **
8247124Seric ** Returns:
8257124Seric ** none
8267124Seric **
8277124Seric ** Side Effects:
8287124Seric ** output of l to fp.
8297124Seric */
8307124Seric
83169748Seric void
putline(l,mci)83265870Seric putline(l, mci)
8337753Seric register char *l;
83465870Seric register MCI *mci;
8357124Seric {
83668847Seric putxline(l, mci, PXLF_MAPFROM);
83768515Seric }
83868847Seric /*
83968847Seric ** PUTXLINE -- putline with flags bits.
84068847Seric **
84168847Seric ** This routine always guarantees outputing a newline (or CRLF,
84268847Seric ** as appropriate) at the end of the string.
84368847Seric **
84468847Seric ** Parameters:
84568847Seric ** l -- line to put.
84668847Seric ** mci -- the mailer connection information.
84768847Seric ** pxflags -- flag bits:
84868847Seric ** PXLF_MAPFROM -- map From_ to >From_.
84968847Seric ** PXLF_STRIP8BIT -- strip 8th bit.
85068847Seric **
85168847Seric ** Returns:
85268847Seric ** none
85368847Seric **
85468847Seric ** Side Effects:
85568847Seric ** output of l to fp.
85668847Seric */
85768515Seric
85868515Seric void
putxline(l,mci,pxflags)85968847Seric putxline(l, mci, pxflags)
86068515Seric register char *l;
86168515Seric register MCI *mci;
86268847Seric int pxflags;
86368515Seric {
8647124Seric register char *p;
86547157Sbostic register char svchar;
86666004Seric int slop = 0;
8677124Seric
86811275Seric /* strip out 0200 bits -- these can look like TELNET protocol */
86968847Seric if (bitset(MCIF_7BIT, mci->mci_flags) ||
87068847Seric bitset(PXLF_STRIP8BIT, pxflags))
87111275Seric {
87261707Seric for (p = l; (svchar = *p) != '\0'; ++p)
87361707Seric if (bitset(0200, svchar))
87447157Sbostic *p = svchar &~ 0200;
87511275Seric }
87611275Seric
8777753Seric do
8787124Seric {
8797753Seric /* find the end of the line */
88056795Seric p = strchr(l, '\n');
8817753Seric if (p == NULL)
8827753Seric p = &l[strlen(l)];
8837124Seric
88463753Seric if (TrafficLogFile != NULL)
88563753Seric fprintf(TrafficLogFile, "%05d >>> ", getpid());
88663753Seric
8877753Seric /* check for line overflow */
88865870Seric while (mci->mci_mailer->m_linelimit > 0 &&
88966004Seric (p - l + slop) > mci->mci_mailer->m_linelimit)
8907753Seric {
89166004Seric register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
8927124Seric
8937753Seric svchar = *q;
8947753Seric *q = '\0';
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 }
90268847Seric else if (l[0] == 'F' && slop == 0 &&
90368847Seric bitset(PXLF_MAPFROM, pxflags) &&
90468847Seric strncmp(l, "From ", 5) == 0 &&
90568515Seric bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
90668515Seric {
90768515Seric (void) putc('>', mci->mci_out);
90868515Seric if (TrafficLogFile != NULL)
90968515Seric (void) putc('>', TrafficLogFile);
91068515Seric }
91165870Seric fputs(l, mci->mci_out);
91265870Seric (void) putc('!', mci->mci_out);
91365870Seric fputs(mci->mci_mailer->m_eol, mci->mci_out);
91466004Seric (void) putc(' ', mci->mci_out);
91563753Seric if (TrafficLogFile != NULL)
91666004Seric fprintf(TrafficLogFile, "%s!\n%05d >>> ",
91763753Seric l, getpid());
9187753Seric *q = svchar;
9197753Seric l = q;
92066004Seric slop = 1;
9217753Seric }
9227124Seric
9237753Seric /* output last part */
92466004Seric if (l[0] == '.' && slop == 0 &&
92566004Seric bitnset(M_XDOT, mci->mci_mailer->m_flags))
92663753Seric {
92765870Seric (void) putc('.', mci->mci_out);
92863753Seric if (TrafficLogFile != NULL)
92963753Seric (void) putc('.', TrafficLogFile);
93063753Seric }
93163753Seric if (TrafficLogFile != NULL)
93263753Seric fprintf(TrafficLogFile, "%.*s\n", p - l, l);
93347157Sbostic for ( ; l < p; ++l)
93465870Seric (void) putc(*l, mci->mci_out);
93565870Seric fputs(mci->mci_mailer->m_eol, mci->mci_out);
9367753Seric if (*l == '\n')
93747157Sbostic ++l;
9387753Seric } while (l[0] != '\0');
9397124Seric }
9407676Seric /*
9417676Seric ** XUNLINK -- unlink a file, doing logging as appropriate.
9427676Seric **
9437676Seric ** Parameters:
9447676Seric ** f -- name of file to unlink.
9457676Seric **
9467676Seric ** Returns:
9477676Seric ** none.
9487676Seric **
9497676Seric ** Side Effects:
9507676Seric ** f is unlinked.
9517676Seric */
9527676Seric
95369748Seric void
xunlink(f)9547676Seric xunlink(f)
9557676Seric char *f;
9567676Seric {
9577676Seric register int i;
9587676Seric
9597676Seric # ifdef LOG
96058020Seric if (LogLevel > 98)
96158020Seric syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
96256795Seric # endif /* LOG */
9637676Seric
9647676Seric i = unlink(f);
9657676Seric # ifdef LOG
96658020Seric if (i < 0 && LogLevel > 97)
9677942Seric syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
96856795Seric # endif /* LOG */
9697676Seric }
9707685Seric /*
97158680Seric ** XFCLOSE -- close a file, doing logging as appropriate.
97258680Seric **
97358680Seric ** Parameters:
97458680Seric ** fp -- file pointer for the file to close
97558680Seric ** a, b -- miscellaneous crud to print for debugging
97658680Seric **
97758680Seric ** Returns:
97858680Seric ** none.
97958680Seric **
98058680Seric ** Side Effects:
98158680Seric ** fp is closed.
98258680Seric */
98358680Seric
98469748Seric void
xfclose(fp,a,b)98558680Seric xfclose(fp, a, b)
98658680Seric FILE *fp;
98758680Seric char *a, *b;
98858680Seric {
98958796Seric if (tTd(53, 99))
99058680Seric printf("xfclose(%x) %s %s\n", fp, a, b);
99169881Seric #if XDEBUG
99264401Seric if (fileno(fp) == 1)
99364401Seric syserr("xfclose(%s %s): fd = 1", a, b);
99464401Seric #endif
99558796Seric if (fclose(fp) < 0 && tTd(53, 99))
99658680Seric printf("xfclose FAILURE: %s\n", errstring(errno));
99758680Seric }
99858680Seric /*
99914885Seric ** SFGETS -- "safe" fgets -- times out and ignores random interrupts.
10007685Seric **
10017685Seric ** Parameters:
10027685Seric ** buf -- place to put the input line.
10037685Seric ** siz -- size of buf.
10047685Seric ** fp -- file to read from.
100557384Seric ** timeout -- the timeout before error occurs.
100661093Seric ** during -- what we are trying to read (for error messages).
10077685Seric **
10087685Seric ** Returns:
100915533Seric ** NULL on error (including timeout). This will also leave
101015533Seric ** buf containing a null string.
10117685Seric ** buf otherwise.
10127685Seric **
10137685Seric ** Side Effects:
10147685Seric ** none.
10157685Seric */
10167685Seric
101714885Seric static jmp_buf CtxReadTimeout;
101868481Seric static void readtimeout();
10197685Seric
10207685Seric char *
sfgets(buf,siz,fp,timeout,during)102161093Seric sfgets(buf, siz, fp, timeout, during)
10227685Seric char *buf;
10237685Seric int siz;
10247685Seric FILE *fp;
102557384Seric time_t timeout;
102661093Seric char *during;
10277685Seric {
10287942Seric register EVENT *ev = NULL;
10297685Seric register char *p;
10307685Seric
103166332Seric if (fp == NULL)
103266332Seric {
103366332Seric buf[0] = '\0';
103466332Seric return NULL;
103566332Seric }
103666332Seric
103714885Seric /* set the timeout */
103857384Seric if (timeout != 0)
103914885Seric {
104014885Seric if (setjmp(CtxReadTimeout) != 0)
104114885Seric {
104236233Skarels # ifdef LOG
104336230Skarels syslog(LOG_NOTICE,
104461093Seric "timeout waiting for input from %s during %s\n",
104561093Seric CurHostName? CurHostName: "local", during);
104636233Skarels # endif
104736230Skarels errno = 0;
104861093Seric usrerr("451 timeout waiting for input during %s",
104961093Seric during);
105019037Seric buf[0] = '\0';
105169881Seric #if XDEBUG
105263753Seric checkfd012(during);
105363753Seric #endif
105414885Seric return (NULL);
105514885Seric }
105668481Seric ev = setevent(timeout, readtimeout, 0);
105714885Seric }
105814885Seric
105914885Seric /* try to read */
106015533Seric p = NULL;
106165190Seric while (!feof(fp) && !ferror(fp))
10627942Seric {
10637942Seric errno = 0;
10647942Seric p = fgets(buf, siz, fp);
106565190Seric if (p != NULL || errno != EINTR)
106665186Seric break;
106765190Seric clearerr(fp);
106815533Seric }
106914885Seric
107014885Seric /* clear the event if it has not sprung */
107168481Seric clrevent(ev);
107214885Seric
107314885Seric /* clean up the books and exit */
10748055Seric LineNumber++;
107515533Seric if (p == NULL)
107616880Seric {
107715533Seric buf[0] = '\0';
107863753Seric if (TrafficLogFile != NULL)
107963753Seric fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
108016880Seric return (NULL);
108116880Seric }
108263753Seric if (TrafficLogFile != NULL)
108363753Seric fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
108468481Seric if (SevenBitInput)
108568481Seric {
108652106Seric for (p = buf; *p != '\0'; p++)
108752106Seric *p &= ~0200;
108867546Seric }
108968481Seric else if (!HasEightBits)
109067546Seric {
109168481Seric for (p = buf; *p != '\0'; p++)
109268481Seric {
109368481Seric if (bitset(0200, *p))
109468481Seric {
109568481Seric HasEightBits = TRUE;
109668481Seric break;
109768481Seric }
109868481Seric }
109967546Seric }
110068481Seric return (buf);
11017685Seric }
11027685Seric
110368481Seric static void
readtimeout(timeout)110466765Seric readtimeout(timeout)
110566765Seric time_t timeout;
11067685Seric {
110768481Seric longjmp(CtxReadTimeout, 1);
11087685Seric }
11097786Seric /*
11107786Seric ** FGETFOLDED -- like fgets, but know about folded lines.
11117786Seric **
11127786Seric ** Parameters:
11137786Seric ** buf -- place to put result.
11147786Seric ** n -- bytes available.
11157786Seric ** f -- file to read from.
11167786Seric **
11177786Seric ** Returns:
111857135Seric ** input line(s) on success, NULL on error or EOF.
111957135Seric ** This will normally be buf -- unless the line is too
112057135Seric ** long, when it will be xalloc()ed.
11217786Seric **
11227786Seric ** Side Effects:
11237786Seric ** buf gets lines from f, with continuation lines (lines
11247786Seric ** with leading white space) appended. CRLF's are mapped
11257786Seric ** into single newlines. Any trailing NL is stripped.
11267786Seric */
11277786Seric
11287786Seric char *
fgetfolded(buf,n,f)11297786Seric fgetfolded(buf, n, f)
11307786Seric char *buf;
11317786Seric register int n;
11327786Seric FILE *f;
11337786Seric {
11347786Seric register char *p = buf;
113557135Seric char *bp = buf;
11367786Seric register int i;
11377786Seric
11387786Seric n--;
113917350Seric while ((i = getc(f)) != EOF)
11407786Seric {
114117350Seric if (i == '\r')
114217350Seric {
114317350Seric i = getc(f);
114417350Seric if (i != '\n')
114517350Seric {
114617350Seric if (i != EOF)
114723105Seric (void) ungetc(i, f);
114817350Seric i = '\r';
114917350Seric }
115017350Seric }
115157135Seric if (--n <= 0)
115257135Seric {
115357135Seric /* allocate new space */
115457135Seric char *nbp;
115557135Seric int nn;
115657135Seric
115757135Seric nn = (p - bp);
115857232Seric if (nn < MEMCHUNKSIZE)
115957135Seric nn *= 2;
116057135Seric else
116157232Seric nn += MEMCHUNKSIZE;
116257135Seric nbp = xalloc(nn);
116357135Seric bcopy(bp, nbp, p - bp);
116457135Seric p = &nbp[p - bp];
116557135Seric if (bp != buf)
116657135Seric free(bp);
116757135Seric bp = nbp;
116857135Seric n = nn - (p - bp);
116957135Seric }
117057135Seric *p++ = i;
117117350Seric if (i == '\n')
117217350Seric {
117317350Seric LineNumber++;
117417350Seric i = getc(f);
117517350Seric if (i != EOF)
117623105Seric (void) ungetc(i, f);
117717350Seric if (i != ' ' && i != '\t')
117852647Seric break;
117917350Seric }
11807786Seric }
118157135Seric if (p == bp)
118252647Seric return (NULL);
118369697Seric if (p[-1] == '\n')
118469697Seric p--;
118569697Seric *p = '\0';
118657135Seric return (bp);
11877786Seric }
11887860Seric /*
11897886Seric ** CURTIME -- return current time.
11907886Seric **
11917886Seric ** Parameters:
11927886Seric ** none.
11937886Seric **
11947886Seric ** Returns:
11957886Seric ** the current time.
11967886Seric **
11977886Seric ** Side Effects:
11987886Seric ** none.
11997886Seric */
12007886Seric
12017886Seric time_t
curtime()12027886Seric curtime()
12037886Seric {
12047886Seric auto time_t t;
12057886Seric
12067886Seric (void) time(&t);
12077886Seric return (t);
12087886Seric }
12098264Seric /*
12108264Seric ** ATOBOOL -- convert a string representation to boolean.
12118264Seric **
12128264Seric ** Defaults to "TRUE"
12138264Seric **
12148264Seric ** Parameters:
12158264Seric ** s -- string to convert. Takes "tTyY" as true,
12168264Seric ** others as false.
12178264Seric **
12188264Seric ** Returns:
12198264Seric ** A boolean representation of the string.
12208264Seric **
12218264Seric ** Side Effects:
12228264Seric ** none.
12238264Seric */
12248264Seric
12258264Seric bool
atobool(s)12268264Seric atobool(s)
12278264Seric register char *s;
12288264Seric {
122963833Seric if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
12308264Seric return (TRUE);
12318264Seric return (FALSE);
12328264Seric }
12339048Seric /*
12349048Seric ** ATOOCT -- convert a string representation to octal.
12359048Seric **
12369048Seric ** Parameters:
12379048Seric ** s -- string to convert.
12389048Seric **
12399048Seric ** Returns:
12409048Seric ** An integer representing the string interpreted as an
12419048Seric ** octal number.
12429048Seric **
12439048Seric ** Side Effects:
12449048Seric ** none.
12459048Seric */
12469048Seric
124769748Seric int
atooct(s)12489048Seric atooct(s)
12499048Seric register char *s;
12509048Seric {
12519048Seric register int i = 0;
12529048Seric
12539048Seric while (*s >= '0' && *s <= '7')
12549048Seric i = (i << 3) | (*s++ - '0');
12559048Seric return (i);
12569048Seric }
12579376Seric /*
12589376Seric ** WAITFOR -- wait for a particular process id.
12599376Seric **
12609376Seric ** Parameters:
12619376Seric ** pid -- process id to wait for.
12629376Seric **
12639376Seric ** Returns:
12649376Seric ** status of pid.
12659376Seric ** -1 if pid never shows up.
12669376Seric **
12679376Seric ** Side Effects:
12689376Seric ** none.
12699376Seric */
12709376Seric
127164562Seric int
waitfor(pid)12729376Seric waitfor(pid)
12739376Seric int pid;
12749376Seric {
127564562Seric #ifdef WAITUNION
127664562Seric union wait st;
127764562Seric #else
12789376Seric auto int st;
127964562Seric #endif
12809376Seric int i;
12819376Seric
12829376Seric do
12839376Seric {
12849376Seric errno = 0;
12859376Seric i = wait(&st);
12869376Seric } while ((i >= 0 || errno == EINTR) && i != pid);
12879376Seric if (i < 0)
128864562Seric return -1;
128964562Seric #ifdef WAITUNION
129064562Seric return st.w_status;
129164562Seric #else
129264562Seric return st;
129364562Seric #endif
12949376Seric }
12959376Seric /*
129610685Seric ** BITINTERSECT -- tell if two bitmaps intersect
129710685Seric **
129810685Seric ** Parameters:
129910685Seric ** a, b -- the bitmaps in question
130010685Seric **
130110685Seric ** Returns:
130210685Seric ** TRUE if they have a non-null intersection
130310685Seric ** FALSE otherwise
130410685Seric **
130510685Seric ** Side Effects:
130610685Seric ** none.
130710685Seric */
130810685Seric
130910685Seric bool
bitintersect(a,b)131010685Seric bitintersect(a, b)
131110685Seric BITMAP a;
131210685Seric BITMAP b;
131310685Seric {
131410685Seric int i;
131510685Seric
131610685Seric for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
131710685Seric if ((a[i] & b[i]) != 0)
131810685Seric return (TRUE);
131910685Seric return (FALSE);
132010685Seric }
132110685Seric /*
132210685Seric ** BITZEROP -- tell if a bitmap is all zero
132310685Seric **
132410685Seric ** Parameters:
132510685Seric ** map -- the bit map to check
132610685Seric **
132710685Seric ** Returns:
132810685Seric ** TRUE if map is all zero.
132910685Seric ** FALSE if there are any bits set in map.
133010685Seric **
133110685Seric ** Side Effects:
133210685Seric ** none.
133310685Seric */
133410685Seric
133510685Seric bool
bitzerop(map)133610685Seric bitzerop(map)
133710685Seric BITMAP map;
133810685Seric {
133910685Seric int i;
134010685Seric
134110685Seric for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
134210685Seric if (map[i] != 0)
134310685Seric return (FALSE);
134410685Seric return (TRUE);
134510685Seric }
134658247Seric /*
134758318Seric ** STRCONTAINEDIN -- tell if one string is contained in another
134858318Seric **
134958318Seric ** Parameters:
135058318Seric ** a -- possible substring.
135158318Seric ** b -- possible superstring.
135258318Seric **
135358318Seric ** Returns:
135458318Seric ** TRUE if a is contained in b.
135558318Seric ** FALSE otherwise.
135658318Seric */
135758318Seric
135858318Seric bool
strcontainedin(a,b)135958318Seric strcontainedin(a, b)
136058318Seric register char *a;
136158318Seric register char *b;
136258318Seric {
136365012Seric int la;
136465012Seric int lb;
136565012Seric int c;
136658318Seric
136765012Seric la = strlen(a);
136865012Seric lb = strlen(b);
136965012Seric c = *a;
137065012Seric if (isascii(c) && isupper(c))
137165012Seric c = tolower(c);
137265012Seric for (; lb-- >= la; b++)
137358318Seric {
137465012Seric if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
137565012Seric continue;
137665012Seric if (strncasecmp(a, b, la) == 0)
137758318Seric return TRUE;
137858318Seric }
137965012Seric return FALSE;
138058318Seric }
138163753Seric /*
138263753Seric ** CHECKFD012 -- check low numbered file descriptors
138363753Seric **
138463753Seric ** File descriptors 0, 1, and 2 should be open at all times.
138563753Seric ** This routine verifies that, and fixes it if not true.
138663753Seric **
138763753Seric ** Parameters:
138863753Seric ** where -- a tag printed if the assertion failed
138963753Seric **
139063753Seric ** Returns:
139163753Seric ** none
139263753Seric */
139363753Seric
139469748Seric void
checkfd012(where)139563753Seric checkfd012(where)
139663753Seric char *where;
139763753Seric {
139869881Seric #if XDEBUG
139963753Seric register int i;
140063753Seric struct stat stbuf;
140163753Seric
140263753Seric for (i = 0; i < 3; i++)
140363753Seric {
140464735Seric if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
140563753Seric {
140663753Seric /* oops.... */
140763753Seric int fd;
140863753Seric
140963753Seric syserr("%s: fd %d not open", where, i);
141063753Seric fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
141163753Seric if (fd != i)
141263753Seric {
141363753Seric (void) dup2(fd, i);
141463753Seric (void) close(fd);
141563753Seric }
141663753Seric }
141763753Seric }
141863937Seric #endif /* XDEBUG */
141963753Seric }
142064725Seric /*
142164725Seric ** PRINTOPENFDS -- print the open file descriptors (for debugging)
142264725Seric **
142364725Seric ** Parameters:
142464725Seric ** logit -- if set, send output to syslog; otherwise
142564725Seric ** print for debugging.
142664725Seric **
142764725Seric ** Returns:
142864725Seric ** none.
142964725Seric */
143064725Seric
143164725Seric #include <arpa/inet.h>
143264725Seric
143369748Seric void
printopenfds(logit)143464725Seric printopenfds(logit)
143564725Seric bool logit;
143664725Seric {
143764725Seric register int fd;
143864743Seric extern int DtableSize;
143964743Seric
144064743Seric for (fd = 0; fd < DtableSize; fd++)
144164743Seric dumpfd(fd, FALSE, logit);
144264743Seric }
144364743Seric /*
144464743Seric ** DUMPFD -- dump a file descriptor
144564743Seric **
144664743Seric ** Parameters:
144764743Seric ** fd -- the file descriptor to dump.
144864743Seric ** printclosed -- if set, print a notification even if
144964743Seric ** it is closed; otherwise print nothing.
145064743Seric ** logit -- if set, send output to syslog instead of stdout.
145164743Seric */
145264743Seric
145369748Seric void
dumpfd(fd,printclosed,logit)145464743Seric dumpfd(fd, printclosed, logit)
145564743Seric int fd;
145664743Seric bool printclosed;
145764743Seric bool logit;
145864743Seric {
145964725Seric register char *p;
146068777Seric char *hp;
146166747Seric char *fmtstr;
146268777Seric SOCKADDR sa;
146364743Seric auto int slen;
146464743Seric struct stat st;
146564725Seric char buf[200];
146668777Seric extern char *hostnamebyanyaddr();
146764725Seric
146864743Seric p = buf;
146964743Seric sprintf(p, "%3d: ", fd);
147064743Seric p += strlen(p);
147164743Seric
147264743Seric if (fstat(fd, &st) < 0)
147364725Seric {
147464743Seric if (printclosed || errno != EBADF)
147564743Seric {
147664743Seric sprintf(p, "CANNOT STAT (%s)", errstring(errno));
147764743Seric goto printit;
147864743Seric }
147964743Seric return;
148064743Seric }
148164725Seric
148264743Seric slen = fcntl(fd, F_GETFL, NULL);
148364743Seric if (slen != -1)
148464743Seric {
148564743Seric sprintf(p, "fl=0x%x, ", slen);
148664743Seric p += strlen(p);
148764743Seric }
148864725Seric
148964743Seric sprintf(p, "mode=%o: ", st.st_mode);
149064743Seric p += strlen(p);
149164743Seric switch (st.st_mode & S_IFMT)
149264743Seric {
149364807Seric #ifdef S_IFSOCK
149464743Seric case S_IFSOCK:
149564743Seric sprintf(p, "SOCK ");
149664725Seric p += strlen(p);
149768777Seric slen = sizeof sa;
149868777Seric if (getsockname(fd, &sa.sa, &slen) < 0)
149968810Seric sprintf(p, "(%s)", errstring(errno));
150064743Seric else
150164725Seric {
150268777Seric hp = hostnamebyanyaddr(&sa);
150368777Seric if (sa.sa.sa_family == AF_INET)
150468777Seric sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port));
150568777Seric else
150668777Seric sprintf(p, "%s", hp);
150764743Seric }
150864743Seric p += strlen(p);
150964743Seric sprintf(p, "->");
151064743Seric p += strlen(p);
151168777Seric slen = sizeof sa;
151268777Seric if (getpeername(fd, &sa.sa, &slen) < 0)
151368810Seric sprintf(p, "(%s)", errstring(errno));
151464743Seric else
151564743Seric {
151668777Seric hp = hostnamebyanyaddr(&sa);
151768777Seric if (sa.sa.sa_family == AF_INET)
151868777Seric sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port));
151968777Seric else
152068777Seric sprintf(p, "%s", hp);
152164743Seric }
152264743Seric break;
152364807Seric #endif
152464725Seric
152564743Seric case S_IFCHR:
152664743Seric sprintf(p, "CHR: ");
152764743Seric p += strlen(p);
152864743Seric goto defprint;
152964725Seric
153064743Seric case S_IFBLK:
153164743Seric sprintf(p, "BLK: ");
153264743Seric p += strlen(p);
153364743Seric goto defprint;
153464725Seric
153566252Seric #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
153665378Seric case S_IFIFO:
153765378Seric sprintf(p, "FIFO: ");
153865378Seric p += strlen(p);
153965378Seric goto defprint;
154065378Seric #endif
154165378Seric
154265378Seric #ifdef S_IFDIR
154365378Seric case S_IFDIR:
154465378Seric sprintf(p, "DIR: ");
154565378Seric p += strlen(p);
154665378Seric goto defprint;
154765378Seric #endif
154865378Seric
154965378Seric #ifdef S_IFLNK
155065378Seric case S_IFLNK:
155165378Seric sprintf(p, "LNK: ");
155265378Seric p += strlen(p);
155365378Seric goto defprint;
155465378Seric #endif
155565378Seric
155664743Seric default:
155764725Seric defprint:
155866785Seric if (sizeof st.st_size > sizeof (long))
155966751Seric fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd";
156066747Seric else
156166751Seric fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld";
156266747Seric sprintf(p, fmtstr,
156364743Seric major(st.st_dev), minor(st.st_dev), st.st_ino,
156464743Seric st.st_nlink, st.st_uid, st.st_gid, st.st_size);
156564743Seric break;
156664743Seric }
156764725Seric
156864743Seric printit:
156966748Seric #ifdef LOG
157064743Seric if (logit)
157165006Seric syslog(LOG_DEBUG, "%s", buf);
157264743Seric else
157366748Seric #endif
157464743Seric printf("%s\n", buf);
157564725Seric }
157665015Seric /*
157765015Seric ** SHORTENSTRING -- return short version of a string
157865015Seric **
157965015Seric ** If the string is already short, just return it. If it is too
158065015Seric ** long, return the head and tail of the string.
158165015Seric **
158265015Seric ** Parameters:
158365015Seric ** s -- the string to shorten.
158465015Seric ** m -- the max length of the string.
158565015Seric **
158665015Seric ** Returns:
158765015Seric ** Either s or a short version of s.
158865015Seric */
158965015Seric
159065015Seric #ifndef MAXSHORTSTR
159165055Seric # define MAXSHORTSTR 203
159265015Seric #endif
159365015Seric
159465015Seric char *
shortenstring(s,m)159565015Seric shortenstring(s, m)
159669748Seric register const char *s;
159765015Seric int m;
159865015Seric {
159965015Seric int l;
160065015Seric static char buf[MAXSHORTSTR + 1];
160165015Seric
160265015Seric l = strlen(s);
160365015Seric if (l < m)
160469748Seric return (char *) s;
160565015Seric if (m > MAXSHORTSTR)
160665015Seric m = MAXSHORTSTR;
160765015Seric else if (m < 10)
160865015Seric {
160965015Seric if (m < 5)
161065015Seric {
161165015Seric strncpy(buf, s, m);
161265015Seric buf[m] = '\0';
161365015Seric return buf;
161465015Seric }
161565015Seric strncpy(buf, s, m - 3);
161665015Seric strcpy(buf + m - 3, "...");
161765015Seric return buf;
161865015Seric }
161965015Seric m = (m - 3) / 2;
162065015Seric strncpy(buf, s, m);
162165015Seric strcpy(buf + m, "...");
162265015Seric strcpy(buf + m + 3, s + l - m);
162365015Seric return buf;
162465015Seric }
162567848Seric /*
162669401Seric ** SHORTEN_HOSTNAME -- strip local domain information off of hostname.
162769401Seric **
162869401Seric ** Parameters:
162969401Seric ** host -- the host to shorten (stripped in place).
163069401Seric **
163169401Seric ** Returns:
163269401Seric ** none.
163369401Seric */
163469401Seric
163569401Seric void
shorten_hostname(host)163669401Seric shorten_hostname(host)
163769401Seric char host[];
163869401Seric {
163969401Seric register char *p;
164069401Seric char *mydom;
164169401Seric int i;
164269773Seric bool canon = FALSE;
164369401Seric
164469401Seric /* strip off final dot */
164569401Seric p = &host[strlen(host) - 1];
164669401Seric if (*p == '.')
164769773Seric {
164869401Seric *p = '\0';
164969773Seric canon = TRUE;
165069773Seric }
165169401Seric
165269401Seric /* see if there is any domain at all -- if not, we are done */
165369401Seric p = strchr(host, '.');
165469401Seric if (p == NULL)
165569401Seric return;
165669401Seric
165769401Seric /* yes, we have a domain -- see if it looks like us */
165869401Seric mydom = macvalue('m', CurEnv);
165969401Seric if (mydom == NULL)
166069401Seric mydom = "";
166169401Seric i = strlen(++p);
166269773Seric if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
166369401Seric (mydom[i] == '.' || mydom[i] == '\0'))
166469401Seric *--p = '\0';
166569401Seric }
166669401Seric /*
166769453Seric ** PROG_OPEN -- open a program for reading
166869453Seric **
166969453Seric ** Parameters:
167069453Seric ** argv -- the argument list.
167169453Seric ** pfd -- pointer to a place to store the file descriptor.
167269453Seric ** e -- the current envelope.
167369453Seric **
167469453Seric ** Returns:
167569453Seric ** pid of the process -- -1 if it failed.
167669453Seric */
167769453Seric
167869453Seric int
prog_open(argv,pfd,e)167969453Seric prog_open(argv, pfd, e)
168069453Seric char **argv;
168169453Seric int *pfd;
168269453Seric ENVELOPE *e;
168369453Seric {
168469453Seric int pid;
168569453Seric int i;
168669453Seric int saveerrno;
168769453Seric int fdv[2];
168869453Seric char *p, *q;
168969453Seric char buf[MAXLINE + 1];
169069453Seric extern int DtableSize;
169169453Seric
169269453Seric if (pipe(fdv) < 0)
169369453Seric {
169469453Seric syserr("%s: cannot create pipe for stdout", argv[0]);
169569453Seric return -1;
169669453Seric }
169769453Seric pid = fork();
169869453Seric if (pid < 0)
169969453Seric {
170069453Seric syserr("%s: cannot fork", argv[0]);
170169453Seric close(fdv[0]);
170269453Seric close(fdv[1]);
170369453Seric return -1;
170469453Seric }
170569453Seric if (pid > 0)
170669453Seric {
170769453Seric /* parent */
170869453Seric close(fdv[1]);
170969453Seric *pfd = fdv[0];
171069453Seric return pid;
171169453Seric }
171269453Seric
171369453Seric /* child -- close stdin */
171469453Seric close(0);
171569453Seric
171669453Seric /* stdout goes back to parent */
171769453Seric close(fdv[0]);
171869453Seric if (dup2(fdv[1], 1) < 0)
171969453Seric {
172069453Seric syserr("%s: cannot dup2 for stdout", argv[0]);
172169453Seric _exit(EX_OSERR);
172269453Seric }
172369453Seric close(fdv[1]);
172469453Seric
172569453Seric /* stderr goes to transcript if available */
172669453Seric if (e->e_xfp != NULL)
172769453Seric {
172869453Seric if (dup2(fileno(e->e_xfp), 2) < 0)
172969453Seric {
173069453Seric syserr("%s: cannot dup2 for stderr", argv[0]);
173169453Seric _exit(EX_OSERR);
173269453Seric }
173369453Seric }
173469453Seric
173569453Seric /* this process has no right to the queue file */
173669453Seric if (e->e_lockfp != NULL)
173769453Seric close(fileno(e->e_lockfp));
173869453Seric
173969453Seric /* run as default user */
1740*69963Seric endpwent();
174169453Seric setgid(DefGid);
174269453Seric setuid(DefUid);
174369453Seric
174469453Seric /* run in some directory */
174569453Seric if (ProgMailer != NULL)
174669453Seric p = ProgMailer->m_execdir;
174769453Seric else
174869453Seric p = NULL;
174969453Seric for (; p != NULL; p = q)
175069453Seric {
175169453Seric q = strchr(p, ':');
175269453Seric if (q != NULL)
175369453Seric *q = '\0';
175469453Seric expand(p, buf, sizeof buf, e);
175569453Seric if (q != NULL)
175669453Seric *q++ = ':';
175769453Seric if (buf[0] != '\0' && chdir(buf) >= 0)
175869453Seric break;
175969453Seric }
176069453Seric if (p == NULL)
176169453Seric {
176269453Seric /* backup directories */
176369453Seric if (chdir("/tmp") < 0)
176469453Seric (void) chdir("/");
176569453Seric }
176669453Seric
176769453Seric /* arrange for all the files to be closed */
176869453Seric for (i = 3; i < DtableSize; i++)
176969453Seric {
177069453Seric register int j;
177169453Seric
177269453Seric if ((j = fcntl(i, F_GETFD, 0)) != -1)
177369453Seric (void) fcntl(i, F_SETFD, j | 1);
177469453Seric }
177569453Seric
177669453Seric /* now exec the process */
177769476Seric execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
177869453Seric
177969453Seric /* woops! failed */
178069453Seric saveerrno = errno;
178169453Seric syserr("%s: cannot exec", argv[0]);
178269453Seric if (transienterror(saveerrno))
178369453Seric _exit(EX_OSERR);
178469453Seric _exit(EX_CONFIG);
178569453Seric }
178669453Seric /*
178768481Seric ** GET_COLUMN -- look up a Column in a line buffer
178868481Seric **
178968481Seric ** Parameters:
179068481Seric ** line -- the raw text line to search.
179168481Seric ** col -- the column number to fetch.
179268481Seric ** delim -- the delimiter between columns. If null,
179368481Seric ** use white space.
179468481Seric ** buf -- the output buffer.
179568481Seric **
179668481Seric ** Returns:
179768481Seric ** buf if successful.
179868481Seric ** NULL otherwise.
179968481Seric */
180068481Seric
180168481Seric char *
get_column(line,col,delim,buf)180268481Seric get_column(line, col, delim, buf)
180368481Seric char line[];
180468481Seric int col;
180568481Seric char delim;
180668481Seric char buf[];
180768481Seric {
180868481Seric char *p;
180968481Seric char *begin, *end;
181068481Seric int i;
181168481Seric char delimbuf[3];
181268481Seric
181368481Seric if (delim == '\0')
181469662Seric strcpy(delimbuf, "\n\t ");
181568481Seric else
181668481Seric {
181768481Seric delimbuf[0] = delim;
181868481Seric delimbuf[1] = '\0';
181968481Seric }
182068481Seric
182168481Seric p = line;
182268481Seric if (*p == '\0')
182368481Seric return NULL; /* line empty */
182468481Seric if (*p == delim && col == 0)
182568481Seric return NULL; /* first column empty */
182668481Seric
182768481Seric begin = line;
182868481Seric
182968481Seric if (col == 0 && delim == '\0')
183068481Seric {
183168481Seric while (*begin && isspace(*begin))
183268481Seric begin++;
183368481Seric }
183468481Seric
183568481Seric for (i = 0; i < col; i++)
183668481Seric {
183768481Seric if ((begin = strpbrk(begin, delimbuf)) == NULL)
183868481Seric return NULL; /* no such column */
183968481Seric begin++;
184068481Seric if (delim == '\0')
184168481Seric {
184268481Seric while (*begin && isspace(*begin))
184368481Seric begin++;
184468481Seric }
184568481Seric }
184668481Seric
184768481Seric end = strpbrk(begin, delimbuf);
184868481Seric if (end == NULL)
184968481Seric {
185068481Seric strcpy(buf, begin);
185168481Seric }
185268481Seric else
185368481Seric {
185468481Seric strncpy(buf, begin, end - begin);
185568481Seric buf[end - begin] = '\0';
185668481Seric }
185768481Seric return buf;
185868481Seric }
185968481Seric /*
186068267Seric ** CLEANSTRCPY -- copy string keeping out bogus characters
186168267Seric **
186268267Seric ** Parameters:
186368267Seric ** t -- "to" string.
186468267Seric ** f -- "from" string.
186568267Seric ** l -- length of space available in "to" string.
186668267Seric **
186768267Seric ** Returns:
186868267Seric ** none.
186968267Seric */
187068267Seric
187168267Seric void
cleanstrcpy(t,f,l)187268267Seric cleanstrcpy(t, f, l)
187368267Seric register char *t;
187468267Seric register char *f;
187568267Seric int l;
187668267Seric {
187768281Seric #ifdef LOG
187868281Seric /* check for newlines and log if necessary */
187968478Seric (void) denlstring(f, TRUE, TRUE);
188068281Seric #endif
188168281Seric
188268267Seric l--;
188368267Seric while (l > 0 && *f != '\0')
188468267Seric {
188568267Seric if (isascii(*f) &&
188668267Seric (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
188768267Seric {
188868267Seric l--;
188968267Seric *t++ = *f;
189068267Seric }
189168267Seric f++;
189268267Seric }
189368267Seric *t = '\0';
189468267Seric }
189568267Seric /*
189668267Seric ** DENLSTRING -- convert newlines in a string to spaces
189768267Seric **
189868267Seric ** Parameters:
189968267Seric ** s -- the input string
190068478Seric ** strict -- if set, don't permit continuation lines.
190168457Seric ** logattacks -- if set, log attempted attacks.
190268267Seric **
190368267Seric ** Returns:
190468267Seric ** A pointer to a version of the string with newlines
190568267Seric ** mapped to spaces. This should be copied.
190668267Seric */
190768267Seric
190868267Seric char *
denlstring(s,strict,logattacks)190968478Seric denlstring(s, strict, logattacks)
191068267Seric char *s;
191168702Seric bool strict;
191268702Seric bool logattacks;
191368267Seric {
191468267Seric register char *p;
191568267Seric int l;
191668267Seric static char *bp = NULL;
191768267Seric static int bl = 0;
191868267Seric
191968478Seric p = s;
192068478Seric while ((p = strchr(p, '\n')) != NULL)
192168478Seric if (strict || (*++p != ' ' && *p != '\t'))
192268478Seric break;
192368478Seric if (p == NULL)
192468267Seric return s;
192568267Seric
192668267Seric l = strlen(s) + 1;
192768267Seric if (bl < l)
192868267Seric {
192968267Seric /* allocate more space */
193068267Seric if (bp != NULL)
193168267Seric free(bp);
193268267Seric bp = xalloc(l);
193368267Seric bl = l;
193468267Seric }
193568267Seric strcpy(bp, s);
193668267Seric for (p = bp; (p = strchr(p, '\n')) != NULL; )
193768267Seric *p++ = ' ';
193868281Seric
193968481Seric /*
194068281Seric #ifdef LOG
194168457Seric if (logattacks)
194268457Seric {
194368457Seric syslog(LOG_NOTICE, "POSSIBLE ATTACK from %s: newline in string \"%s\"",
194468457Seric RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
194568457Seric shortenstring(bp, 80));
194668457Seric }
194768281Seric #endif
194868481Seric */
194968281Seric
195068267Seric return bp;
195168267Seric }
1952