xref: /386bsd/usr/src/libexec/mail.local/mail.local.c (revision a2142627)
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1990 The Regents of the University of California.\n\
37  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 static char sccsid[] = "@(#)mail.local.c	5.6 (Berkeley) 6/19/91";
42 #endif /* not lint */
43 
44 #include <sys/param.h>
45 #include <sys/stat.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <syslog.h>
49 #include <fcntl.h>
50 #include <netdb.h>
51 #include <pwd.h>
52 #include <time.h>
53 #include <unistd.h>
54 #include <errno.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include "pathnames.h"
59 
60 #define	FATAL		1
61 #define	NOTFATAL	0
62 
63 int	deliver __P((int, char *));
64 void	err __P((int, const char *, ...));
65 void	notifybiff __P((char *));
66 int	store __P((char *));
67 void	usage __P((void));
68 
main(argc,argv)69 main(argc, argv)
70 	int argc;
71 	char **argv;
72 {
73 	extern int optind;
74 	extern char *optarg;
75 	struct passwd *pw;
76 	int ch, fd, eval;
77 	uid_t uid;
78 	char *from;
79 
80 	openlog("mail.local", LOG_PERROR, LOG_MAIL);
81 
82 	from = NULL;
83 	while ((ch = getopt(argc, argv, "df:r:")) != EOF)
84 		switch(ch) {
85 		case 'd':		/* backward compatible */
86 			break;
87 		case 'f':
88 		case 'r':		/* backward compatible */
89 			if (from)
90 			    err(FATAL, "multiple -f options");
91 			from = optarg;
92 			break;
93 		case '?':
94 		default:
95 			usage();
96 		}
97 	argc -= optind;
98 	argv += optind;
99 
100 	if (!*argv)
101 		usage();
102 
103 	/*
104 	 * If from not specified, use the name from getlogin() if the
105 	 * uid matches, otherwise, use the name from the password file
106 	 * corresponding to the uid.
107 	 */
108 	uid = getuid();
109 	if (!from && (!(from = getlogin()) ||
110 	    !(pw = getpwnam(from)) || pw->pw_uid != uid))
111 		from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
112 
113 	fd = store(from);
114 	for (eval = 0; *argv; ++argv)
115 		eval |= deliver(fd, *argv);
116 	exit(eval);
117 }
118 
store(from)119 store(from)
120 	char *from;
121 {
122 	FILE *fp;
123 	time_t tval;
124 	int fd, eline;
125 	char *tn, line[2048];
126 
127 	tn = strdup(_PATH_LOCTMP);
128 	if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+")))
129 		err(FATAL, "unable to open temporary file");
130 	(void)unlink(tn);
131 	free(tn);
132 
133 	(void)time(&tval);
134 	(void)fprintf(fp, "From %s %s", from, ctime(&tval));
135 
136 	line[0] = '\0';
137 	for (eline = 1; fgets(line, sizeof(line), stdin);) {
138 		if (line[0] == '\n')
139 			eline = 1;
140 		else {
141 			if (eline && line[0] == 'F' && !bcmp(line, "From ", 5))
142 				(void)putc('>', fp);
143 			eline = 0;
144 		}
145 		(void)fprintf(fp, "%s", line);
146 		if (ferror(fp))
147 			break;
148 	}
149 
150 	/* If message not newline terminated, need an extra. */
151 	if (!index(line, '\n'))
152 		(void)putc('\n', fp);
153 	/* Output a newline; note, empty messages are allowed. */
154 	(void)putc('\n', fp);
155 
156 	(void)fflush(fp);
157 	if (ferror(fp))
158 		err(FATAL, "temporary file write error");
159 	return(fd);
160 }
161 
deliver(fd,name)162 deliver(fd, name)
163 	int fd;
164 	char *name;
165 {
166 	struct stat sb;
167 	struct passwd *pw;
168 	int created, mbfd, nr, nw, off, rval;
169 	char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
170 	off_t curoff, lseek();
171 
172 	/*
173 	 * Disallow delivery to unknown names -- special mailboxes can be
174 	 * handled in the sendmail aliases file.
175 	 */
176 	if (!(pw = getpwnam(name))) {
177 		err(NOTFATAL, "unknown name: %s", name);
178 		return(1);
179 	}
180 
181 	(void)sprintf(path, "%s/%s", _PATH_MAILDIR, name);
182 
183 	if (!(created = lstat(path, &sb)) &&
184 	    (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) {
185 		err(NOTFATAL, "%s: linked file", path);
186 		return(1);
187 	}
188 
189 	/*
190 	 * There's a race here -- two processes think they both created
191 	 * the file.  This means the file cannot be unlinked.
192 	 */
193 	if ((mbfd =
194 	    open(path, O_APPEND|O_CREAT|O_WRONLY|O_EXLOCK,
195 			S_IRUSR|S_IWUSR)) < 0) {
196 		err(NOTFATAL, "%s: %s", path, strerror(errno));
197 		return(1);
198 	}
199 
200 	rval = 0;
201 	curoff = lseek(mbfd, 0L, SEEK_END);
202 	(void)sprintf(biffmsg, "%s@%ld\n", name, curoff);
203 	if (lseek(fd, 0L, SEEK_SET) == (off_t)-1) {
204 		err(FATAL, "temporary file: %s", strerror(errno));
205 		rval = 1;
206 		goto bad;
207 	}
208 
209 	while ((nr = read(fd, buf, sizeof(buf))) > 0)
210 		for (off = 0; off < nr; nr -= nw, off += nw)
211 			if ((nw = write(mbfd, buf + off, nr)) < 0) {
212 				err(NOTFATAL, "%s: %s", path, strerror(errno));
213 				goto trunc;
214 			}
215 	if (nr < 0) {
216 		err(FATAL, "temporary file: %s", strerror(errno));
217 trunc:		(void)ftruncate(mbfd, curoff);
218 		rval = 1;
219 	}
220 
221 	/*
222 	 * Set the owner and group.  Historically, binmail repeated this at
223 	 * each mail delivery.  We no longer do this, assuming that if the
224 	 * ownership or permissions were changed there was a reason for doing
225 	 * so.
226 	 */
227 bad:	if (created)
228 		(void)fchown(mbfd, pw->pw_uid, pw->pw_gid);
229 
230 	(void)fsync(mbfd);		/* Don't wait for update. */
231 	(void)close(mbfd);		/* Implicit unlock. */
232 
233 	if (!rval)
234 		notifybiff(biffmsg);
235 	return(rval);
236 }
237 
238 void
notifybiff(msg)239 notifybiff(msg)
240 	char *msg;
241 {
242 	static struct sockaddr_in addr;
243 	static int f = -1;
244 	struct hostent *hp;
245 	struct servent *sp;
246 	int len;
247 
248 	if (!addr.sin_family) {
249 		/* Be silent if biff service not available. */
250 		if (!(sp = getservbyname("biff", "udp")))
251 			return;
252 		if (!(hp = gethostbyname("localhost"))) {
253 			err(NOTFATAL, "localhost: %s", strerror(errno));
254 			return;
255 		}
256 		addr.sin_family = hp->h_addrtype;
257 		bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
258 		addr.sin_port = sp->s_port;
259 	}
260 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
261 		err(NOTFATAL, "socket: %s", strerror(errno));
262 		return;
263 	}
264 	len = strlen(msg) + 1;
265 	if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
266 	    != len)
267 		err(NOTFATAL, "sendto biff: %s", strerror(errno));
268 }
269 
270 void
usage()271 usage()
272 {
273 	err(FATAL, "usage: mail.local [-f from] user ...");
274 }
275 
276 #if __STDC__
277 #include <stdarg.h>
278 #else
279 #include <varargs.h>
280 #endif
281 
282 void
283 #if __STDC__
err(int isfatal,const char * fmt,...)284 err(int isfatal, const char *fmt, ...)
285 #else
286 err(isfatal, fmt)
287 	int isfatal;
288 	char *fmt;
289 	va_dcl
290 #endif
291 {
292 	va_list ap;
293 #if __STDC__
294 	va_start(ap, fmt);
295 #else
296 	va_start(ap);
297 #endif
298 	vsyslog(LOG_ERR, fmt, ap);
299 	va_end(ap);
300 	if (isfatal)
301 		exit(1);
302 }
303