GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: libexec/mail.local/mail.local.c Lines: 0 145 0.0 %
Date: 2017-11-07 Branches: 0 129 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: mail.local.c,v 1.35 2015/12/12 20:09:28 mmcc Exp $	*/
2
3
/*-
4
 * Copyright (c) 1996-1998 Theo de Raadt <deraadt@theos.com>
5
 * Copyright (c) 1996-1998 David Mazieres <dm@lcs.mit.edu>
6
 * Copyright (c) 1990 The Regents of the University of California.
7
 * All rights reserved.
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 * 3. 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
#include <sys/types.h>
35
#include <sys/stat.h>
36
#include <sys/socket.h>
37
#include <netinet/in.h>
38
#include <syslog.h>
39
#include <fcntl.h>
40
#include <netdb.h>
41
#include <pwd.h>
42
#include <time.h>
43
#include <unistd.h>
44
#include <limits.h>
45
#include <errno.h>
46
#include <stdio.h>
47
#include <stdlib.h>
48
#include <string.h>
49
#include "pathnames.h"
50
#include "mail.local.h"
51
52
int
53
main(int argc, char *argv[])
54
{
55
	struct passwd *pw;
56
	int ch, fd, eval, lockfile=1, holdme=0;
57
	uid_t uid;
58
	char *from;
59
60
	openlog("mail.local", LOG_PERROR, LOG_MAIL);
61
62
	from = NULL;
63
	while ((ch = getopt(argc, argv, "lLdf:r:H")) != -1)
64
		switch (ch) {
65
		case 'd':		/* backward compatible */
66
			break;
67
		case 'f':
68
		case 'r':		/* backward compatible */
69
			if (from)
70
				merr(FATAL, "multiple -f options");
71
			from = optarg;
72
			break;
73
		case 'l':
74
			lockfile=1;
75
			break;
76
		case 'L':
77
			lockfile=0;
78
			break;
79
		case 'H':
80
			holdme=1;
81
			break;
82
		default:
83
			usage();
84
		}
85
	argc -= optind;
86
	argv += optind;
87
88
	/* Support -H flag for backwards compat */
89
	if (holdme) {
90
		execl(_PATH_LOCKSPOOL, "lockspool", (char *)NULL);
91
		merr(FATAL, "execl: lockspool: %s", strerror(errno));
92
	} else {
93
		if (!*argv)
94
			usage();
95
		if (geteuid() != 0)
96
			merr(FATAL, "may only be run by the superuser");
97
	}
98
99
	/*
100
	 * If from not specified, use the name from getlogin() if the
101
	 * uid matches, otherwise, use the name from the password file
102
	 * corresponding to the uid.
103
	 */
104
	uid = getuid();
105
	if (!from && (!(from = getlogin()) ||
106
	    !(pw = getpwnam(from)) || pw->pw_uid != uid))
107
		from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
108
109
	fd = storemail(from);
110
	for (eval = 0; *argv; ++argv)
111
		eval |= deliver(fd, *argv, lockfile);
112
	exit(eval);
113
}
114
115
int
116
storemail(char *from)
117
{
118
	FILE *fp = NULL;
119
	time_t tval;
120
	int fd, eline;
121
	size_t len;
122
	char *line, *tbuf;
123
124
	if ((tbuf = strdup(_PATH_LOCTMP)) == NULL)
125
		merr(FATAL, "unable to allocate memory");
126
	if ((fd = mkstemp(tbuf)) == -1 || !(fp = fdopen(fd, "w+")))
127
		merr(FATAL, "unable to open temporary file");
128
	(void)unlink(tbuf);
129
	free(tbuf);
130
131
	(void)time(&tval);
132
	(void)fprintf(fp, "From %s %s", from, ctime(&tval));
133
134
	for (eline = 1, tbuf = NULL; (line = fgetln(stdin, &len));) {
135
		/* We have to NUL-terminate the line since fgetln does not */
136
		if (line[len - 1] == '\n')
137
			line[len - 1] = '\0';
138
		else {
139
			/* No trailing newline, so alloc space and copy */
140
			if ((tbuf = malloc(len + 1)) == NULL)
141
				merr(FATAL, "unable to allocate memory");
142
			memcpy(tbuf, line, len);
143
			tbuf[len] = '\0';
144
			line = tbuf;
145
		}
146
		if (line[0] == '\0')
147
			eline = 1;
148
		else {
149
			if (eline && line[0] == 'F' && len > 5 &&
150
			    !memcmp(line, "From ", 5))
151
				(void)putc('>', fp);
152
			eline = 0;
153
		}
154
		(void)fprintf(fp, "%s\n", line);
155
		if (ferror(fp))
156
			break;
157
	}
158
	free(tbuf);
159
160
	/* Output a newline; note, empty messages are allowed. */
161
	(void)putc('\n', fp);
162
	(void)fflush(fp);
163
	if (ferror(fp))
164
		merr(FATAL, "temporary file write error");
165
	return(fd);
166
}
167
168
int
169
deliver(int fd, char *name, int lockfile)
170
{
171
	struct stat sb, fsb;
172
	struct passwd *pw;
173
	int mbfd=-1, rval=1, lfd=-1;
174
	char biffmsg[100], buf[8*1024], path[PATH_MAX];
175
	off_t curoff;
176
	size_t off;
177
	ssize_t nr, nw;
178
179
	/*
180
	 * Disallow delivery to unknown names -- special mailboxes can be
181
	 * handled in the sendmail aliases file.
182
	 */
183
	if (!(pw = getpwnam(name))) {
184
		merr(NOTFATAL, "unknown name: %s", name);
185
		return(1);
186
	}
187
188
	(void)snprintf(path, sizeof path, "%s/%s", _PATH_MAILDIR, name);
189
190
	if (lockfile) {
191
		lfd = getlock(name, pw);
192
		if (lfd == -1)
193
			return (1);
194
	}
195
196
	/* after this point, always exit via bad to remove lockfile */
197
retry:
198
	if (lstat(path, &sb)) {
199
		if (errno != ENOENT) {
200
			merr(NOTFATAL, "%s: %s", path, strerror(errno));
201
			goto bad;
202
		}
203
		if ((mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|O_EXLOCK,
204
		    S_IRUSR|S_IWUSR)) < 0) {
205
			if (errno == EEXIST) {
206
				/* file appeared since lstat */
207
				goto retry;
208
			} else {
209
				merr(NOTFATAL, "%s: %s", path, strerror(errno));
210
				goto bad;
211
			}
212
		}
213
		/*
214
		 * Set the owner and group.  Historically, binmail repeated
215
		 * this at each mail delivery.  We no longer do this, assuming
216
		 * that if the ownership or permissions were changed there
217
		 * was a reason for doing so.
218
		 */
219
		if (fchown(mbfd, pw->pw_uid, pw->pw_gid) < 0) {
220
			merr(NOTFATAL, "chown %u:%u: %s",
221
			    pw->pw_uid, pw->pw_gid, name);
222
			goto bad;
223
		}
224
	} else {
225
		if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
226
			merr(NOTFATAL, "%s: linked or special file", path);
227
			goto bad;
228
		}
229
		if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK,
230
		    S_IRUSR|S_IWUSR)) < 0) {
231
			merr(NOTFATAL, "%s: %s", path, strerror(errno));
232
			goto bad;
233
		}
234
		if (fstat(mbfd, &fsb)) {
235
			/* relating error to path may be bad style */
236
			merr(NOTFATAL, "%s: %s", path, strerror(errno));
237
			goto bad;
238
		}
239
		if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino) {
240
			merr(NOTFATAL, "%s: changed after open", path);
241
			goto bad;
242
		}
243
		/* paranoia? */
244
		if (fsb.st_nlink != 1 || !S_ISREG(fsb.st_mode)) {
245
			merr(NOTFATAL, "%s: linked or special file", path);
246
			goto bad;
247
		}
248
	}
249
250
	curoff = lseek(mbfd, 0, SEEK_END);
251
	(void)snprintf(biffmsg, sizeof biffmsg, "%s@%lld\n", name, curoff);
252
	if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
253
		merr(NOTFATAL, "temporary file: %s", strerror(errno));
254
		goto bad;
255
	}
256
257
	while ((nr = read(fd, buf, sizeof(buf))) > 0)
258
		for (off = 0; off < nr;  off += nw)
259
			if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
260
				merr(NOTFATAL, "%s: %s", path, strerror(errno));
261
				(void)ftruncate(mbfd, curoff);
262
				goto bad;
263
			}
264
265
	if (nr == 0) {
266
		rval = 0;
267
	} else {
268
		(void)ftruncate(mbfd, curoff);
269
		merr(FATAL, "temporary file: %s", strerror(errno));
270
	}
271
272
bad:
273
	if (lfd != -1) {
274
		rellock();
275
		close(lfd);
276
	}
277
278
	if (mbfd != -1) {
279
		(void)fsync(mbfd);		/* Don't wait for update. */
280
		(void)close(mbfd);		/* Implicit unlock. */
281
	}
282
283
	if (!rval)
284
		notifybiff(biffmsg);
285
	return(rval);
286
}
287
288
void
289
notifybiff(char *msg)
290
{
291
	static struct addrinfo *res0;
292
	struct addrinfo hints, *res;
293
	static int f = -1;
294
	size_t len;
295
	int error;
296
297
	if (res0 == NULL) {
298
		memset(&hints, 0, sizeof(hints));
299
		hints.ai_family = PF_UNSPEC;
300
		hints.ai_socktype = SOCK_DGRAM;
301
302
		error = getaddrinfo("localhost", "biff", &hints, &res0);
303
		if (error) {
304
			/* Be silent if biff service not available. */
305
			if (error != EAI_SERVICE) {
306
				merr(NOTFATAL, "localhost: %s",
307
				    gai_strerror(error));
308
			}
309
			return;
310
		}
311
	}
312
313
	if (f == -1) {
314
		for (res = res0; res != NULL; res = res->ai_next) {
315
			f = socket(res->ai_family, res->ai_socktype,
316
			    res->ai_protocol);
317
			if (f != -1)
318
				break;
319
		}
320
	}
321
	if (f == -1) {
322
		merr(NOTFATAL, "socket: %s", strerror(errno));
323
		return;
324
	}
325
326
	len = strlen(msg) + 1;	/* XXX */
327
	if (sendto(f, msg, len, 0, res->ai_addr, res->ai_addrlen) != len)
328
		merr(NOTFATAL, "sendto biff: %s", strerror(errno));
329
}
330
331
void
332
usage(void)
333
{
334
	merr(FATAL, "usage: mail.local [-Ll] [-f from] user ...");
335
}