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