Annotation of src/libexec/mail.local/mail.local.c, Revision 1.26.10.2
1.26.10.2! martin 1: /* $NetBSD: mail.local.c,v 1.26.10.1 2016/07/19 14:23:10 martin Exp $ */
1.10 mrg 2:
1.1 cgd 3: /*-
1.11 mrg 4: * Copyright (c) 1990, 1993, 1994
5: * The Regents of the University of California. All rights reserved.
1.1 cgd 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.
1.22 agc 15: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 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:
1.11 mrg 32: #include <sys/cdefs.h>
1.1 cgd 33: #ifndef lint
1.25 lukem 34: __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\
35: The Regents of the University of California. All rights reserved.");
1.11 mrg 36: #if 0
37: static char sccsid[] = "@(#)mail.local.c 8.22 (Berkeley) 6/21/95";
38: #else
1.26.10.2! martin 39: __RCSID("$NetBSD: mail.local.c,v 1.26.10.1 2016/07/19 14:23:10 martin Exp $");
1.11 mrg 40: #endif
1.1 cgd 41: #endif /* not lint */
42:
43: #include <sys/param.h>
44: #include <sys/stat.h>
45: #include <sys/socket.h>
1.11 mrg 46:
1.1 cgd 47: #include <netinet/in.h>
1.11 mrg 48:
49: #include <errno.h>
1.1 cgd 50: #include <fcntl.h>
1.11 mrg 51: #include <pwd.h>
1.1 cgd 52: #include <netdb.h>
1.20 wiz 53: #include <stdarg.h>
1.1 cgd 54: #include <stdio.h>
55: #include <stdlib.h>
56: #include <string.h>
1.11 mrg 57: #include <syslog.h>
58: #include <time.h>
59: #include <unistd.h>
1.24 manu 60: #include <sysexits.h>
61:
1.11 mrg 62:
1.1 cgd 63: #include "pathnames.h"
64:
1.26 joerg 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);
1.1 cgd 71:
1.11 mrg 72: int
1.26 joerg 73: main(int argc, char *argv[])
1.1 cgd 74: {
75: struct passwd *pw;
1.11 mrg 76: int ch, fd, eval, lockfile = 0;
1.1 cgd 77: uid_t uid;
1.15 mycroft 78: const char *from;
1.1 cgd 79:
1.11 mrg 80: /* use a reasonable umask */
81: (void) umask(0077);
82:
1.1 cgd 83: openlog("mail.local", LOG_PERROR, LOG_MAIL);
84:
85: from = NULL;
1.13 enami 86: while ((ch = getopt(argc, argv, "ldf:r:")) != -1)
1.14 enami 87: switch (ch) {
1.1 cgd 88: case 'd': /* backward compatible */
89: break;
90: case 'f':
91: case 'r': /* backward compatible */
92: if (from)
1.24 manu 93: logerr(EX_USAGE, "multiple -f options");
1.1 cgd 94: from = optarg;
95: break;
1.2 deraadt 96: case 'l':
97: lockfile++;
98: break;
1.1 cgd 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);
1.24 manu 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: }
1.10 mrg 127: exit (eval);
1.1 cgd 128: }
129:
1.26 joerg 130: static int
131: store(const char *from)
1.1 cgd 132: {
1.11 mrg 133: FILE *fp = NULL; /* XXX gcc */
1.1 cgd 134: time_t tval;
135: int fd, eline;
136: char *tn, line[2048];
137:
138: tn = strdup(_PATH_LOCTMP);
1.21 itojun 139: if (!tn)
1.24 manu 140: logerr(EX_OSERR, "not enough core");
1.1 cgd 141: if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+")))
1.24 manu 142: logerr(EX_OSERR, "unable to open temporary file");
1.1 cgd 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 {
1.16 perry 154: if (eline && line[0] == 'F' && !memcmp(line, "From ", 5))
1.1 cgd 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))
1.24 manu 171: logerr(EX_OSERR, "temporary file write error");
1.26.10.1 martin 172: if ((fd = dup(fd)) == -1)
173: logerr(EX_OSERR, "dup failed");
1.23 christos 174: (void)fclose(fp);
1.1 cgd 175: return(fd);
176: }
177:
1.26 joerg 178: static int
179: deliver(int fd, char *name, int lockfile)
1.1 cgd 180: {
1.26.10.1 martin 181: struct stat sb, nsb;
1.24 manu 182: struct passwd pwres, *pw;
183: char pwbuf[1024];
1.26.10.1 martin 184: int created = 0, mbfd, nr, nw, off, rval=EX_OK, lfd = -1;
1.2 deraadt 185: char biffmsg[100], buf[8*1024], path[MAXPATHLEN], lpath[MAXPATHLEN];
1.6 pk 186: off_t curoff;
1.1 cgd 187:
188: /*
189: * Disallow delivery to unknown names -- special mailboxes can be
190: * handled in the sendmail aliases file.
191: */
1.24 manu 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);
1.1 cgd 199: }
200:
1.10 mrg 201: (void)snprintf(path, sizeof path, "%s/%s", _PATH_MAILDIR, name);
1.1 cgd 202:
1.10 mrg 203: if (lockfile) {
204: (void)snprintf(lpath, sizeof lpath, "%s/%s.lock",
205: _PATH_MAILDIR, name);
1.2 deraadt 206:
207: if((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL,
208: S_IRUSR|S_IWUSR)) < 0) {
1.24 manu 209: logwarn("%s: %s", lpath, strerror(errno));
210: return(EX_OSERR);
1.2 deraadt 211: }
212: }
213:
1.26.10.1 martin 214: if ((lstat(path, &sb) != -1) &&
1.1 cgd 215: (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) {
1.24 manu 216: logwarn("%s: linked file", path);
217: return(EX_OSERR);
1.1 cgd 218: }
1.24 manu 219:
1.10 mrg 220: if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK,
1.26.10.2! martin 221: S_IRUSR|S_IWUSR)) == -1) {
1.26.10.1 martin 222: /* create file */
1.2 deraadt 223: if ((mbfd = open(path, O_APPEND|O_CREAT|O_WRONLY|O_EXLOCK,
1.26.10.2! martin 224: S_IRUSR|S_IWUSR)) == -1) {
1.24 manu 225: logwarn("%s: %s", path, strerror(errno));
1.26.10.1 martin 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;
1.10 mrg 241: }
1.1 cgd 242: }
243:
1.26.10.1 martin 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:
1.18 lukem 250: (void)snprintf(biffmsg, sizeof biffmsg, "%s@%lld\n", name,
1.12 mrg 251: (long long)curoff);
1.6 pk 252: if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
1.24 manu 253: logwarn("temporary file: %s", strerror(errno));
254: rval = EX_OSERR;
1.1 cgd 255: goto bad;
256: }
257:
258: while ((nr = read(fd, buf, sizeof(buf))) > 0)
1.3 mycroft 259: for (off = 0; off < nr; off += nw)
260: if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
1.24 manu 261: logwarn("%s: %s", path, strerror(errno));
1.1 cgd 262: goto trunc;
263: }
264: if (nr < 0) {
1.24 manu 265: logwarn("temporary file: %s", strerror(errno));
1.1 cgd 266: trunc: (void)ftruncate(mbfd, curoff);
1.24 manu 267: rval = EX_OSERR;
1.1 cgd 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: */
1.2 deraadt 276: bad:
1.10 mrg 277: if (lockfile) {
278: if (lfd >= 0) {
1.2 deraadt 279: unlink(lpath);
280: close(lfd);
281: }
282: }
1.1 cgd 283:
1.26.10.1 martin 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: }
1.1 cgd 291:
1.24 manu 292: if (rval == EX_OK)
1.1 cgd 293: notifybiff(biffmsg);
1.24 manu 294:
295: return rval;
1.1 cgd 296: }
297:
298: void
1.26 joerg 299: notifybiff(char *msg)
1.1 cgd 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"))) {
1.24 manu 312: logwarn("localhost: %s", strerror(errno));
1.1 cgd 313: return;
314: }
1.9 mycroft 315: addr.sin_len = sizeof(struct sockaddr_in);
1.1 cgd 316: addr.sin_family = hp->h_addrtype;
1.9 mycroft 317: addr.sin_port = sp->s_port;
1.16 perry 318: memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
1.1 cgd 319: }
320: if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
1.24 manu 321: logwarn("socket: %s", strerror(errno));
1.1 cgd 322: return;
323: }
324: len = strlen(msg) + 1;
325: if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
326: != len)
1.24 manu 327: logwarn("sendto biff: %s", strerror(errno));
1.1 cgd 328: }
329:
1.26 joerg 330: static void
331: usage(void)
1.1 cgd 332: {
1.24 manu 333: logerr(EX_USAGE, "usage: mail.local [-l] [-f from] user ...");
334: }
335:
1.26 joerg 336: static void
1.24 manu 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 */
1.1 cgd 347: }
348:
1.26 joerg 349: static void
1.24 manu 350: logwarn(const char *fmt, ...)
1.1 cgd 351: {
352: va_list ap;
1.20 wiz 353:
1.1 cgd 354: va_start(ap, fmt);
355: vsyslog(LOG_ERR, fmt, ap);
356: va_end(ap);
1.24 manu 357: return;
1.1 cgd 358: }
CVSweb <webmaster@jp.NetBSD.org>