xref: /openbsd-src/usr.sbin/smtpd/mda.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: mda.c,v 1.59 2011/08/29 21:43:08 chl Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
5  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6  * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/queue.h>
23 #include <sys/tree.h>
24 #include <sys/param.h>
25 #include <sys/socket.h>
26 
27 #include <event.h>
28 #include <imsg.h>
29 #include <pwd.h>
30 #include <signal.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 #include <unistd.h>
36 #include <vis.h>
37 
38 #include "smtpd.h"
39 #include "log.h"
40 
41 static void mda_imsg(struct imsgev *, struct imsg *);
42 static void mda_shutdown(void);
43 static void mda_sig_handler(int, short, void *);
44 static void mda_store(struct mda_session *);
45 static void mda_store_event(int, short, void *);
46 static struct mda_session *mda_lookup(u_int32_t);
47 
48 u_int32_t mda_id;
49 
50 static void
51 mda_imsg(struct imsgev *iev, struct imsg *imsg)
52 {
53 	char			 output[128], *error, *parent_error;
54 	struct deliver		 deliver;
55 	struct mda_session	*s;
56 	struct delivery		*d;
57 	struct delivery_mda	*d_mda;
58 	struct mailaddr		*maddr;
59 
60 	if (iev->proc == PROC_QUEUE) {
61 		switch (imsg->hdr.type) {
62 		case IMSG_MDA_SESS_NEW:
63 			/* make new session based on provided args */
64 			s = calloc(1, sizeof *s);
65 			if (s == NULL)
66 				fatal(NULL);
67 			msgbuf_init(&s->w);
68 			s->msg = *(struct envelope *)imsg->data;
69 			s->msg.delivery.status = DS_TEMPFAILURE;
70 			s->id = mda_id++;
71 			s->datafp = fdopen(imsg->fd, "r");
72 			if (s->datafp == NULL)
73 				fatalx("mda: fdopen");
74 			LIST_INSERT_HEAD(&env->mda_sessions, s, entry);
75 
76 			/* request parent to fork a helper process */
77 			d     = &s->msg.delivery;
78 			d_mda = &s->msg.delivery.agent.mda;
79 			switch (d_mda->method) {
80 			case A_EXT:
81 				deliver.mode = A_EXT;
82 				strlcpy(deliver.user, d_mda->as_user,
83 				    sizeof (deliver.user));
84 				strlcpy(deliver.to, d_mda->to.buffer,
85 				    sizeof deliver.to);
86 				break;
87 
88 			case A_MBOX:
89 				deliver.mode = A_EXT;
90 				strlcpy(deliver.user, "root",
91 				    sizeof (deliver.user));
92 				snprintf(deliver.to, sizeof (deliver.to),
93 				    "%s -f %s@%s %s", PATH_MAILLOCAL,
94 				    d->from.user,
95 				    d->from.domain,
96 				    d_mda->to.user);
97 				break;
98 
99 			case A_MAILDIR:
100 				deliver.mode = A_MAILDIR;
101 				strlcpy(deliver.user, d_mda->as_user,
102 				    sizeof deliver.user);
103 				strlcpy(deliver.to, d_mda->to.buffer,
104 				    sizeof deliver.to);
105 				break;
106 
107 			case A_FILENAME:
108 				deliver.mode = A_FILENAME;
109 				strlcpy(deliver.user, d_mda->as_user,
110 				    sizeof deliver.user);
111 				strlcpy(deliver.to, d_mda->to.buffer,
112 				    sizeof deliver.to);
113 				break;
114 
115 			default:
116 				log_debug("mda: unknown rule action: %d", d_mda->method);
117 				fatalx("mda: unknown rule action");
118 			}
119 
120 			imsg_compose_event(env->sc_ievs[PROC_PARENT],
121 			    IMSG_PARENT_FORK_MDA, s->id, 0, -1, &deliver,
122 			    sizeof deliver);
123 			return;
124 		}
125 	}
126 
127 	if (iev->proc == PROC_PARENT) {
128 		switch (imsg->hdr.type) {
129 		case IMSG_PARENT_FORK_MDA:
130 			s = mda_lookup(imsg->hdr.peerid);
131 
132 			if (imsg->fd < 0)
133 				fatalx("mda: fd pass fail");
134 			s->w.fd = imsg->fd;
135 
136 			mda_store(s);
137 			return;
138 
139 		case IMSG_MDA_DONE:
140 			s = mda_lookup(imsg->hdr.peerid);
141 
142 			/*
143 			 * Grab last line of mda stdout/stderr if available.
144 			 */
145 			output[0] = '\0';
146 			if (imsg->fd != -1) {
147 				char *ln, *buf;
148 				FILE *fp;
149 				size_t len;
150 
151 				buf = NULL;
152 				if (lseek(imsg->fd, 0, SEEK_SET) < 0)
153 					fatalx("lseek");
154 				fp = fdopen(imsg->fd, "r");
155 				if (fp == NULL)
156 					fatal("mda: fdopen");
157 				while ((ln = fgetln(fp, &len))) {
158 					if (ln[len - 1] == '\n')
159 						ln[len - 1] = '\0';
160 					else {
161 						buf = malloc(len + 1);
162 						if (buf == NULL)
163 							fatal(NULL);
164 						memcpy(buf, ln, len);
165 						buf[len] = '\0';
166 						ln = buf;
167 					}
168 					strlcpy(output, "\"", sizeof output);
169 					strnvis(output + 1, ln,
170 					    sizeof(output) - 2,
171 					    VIS_SAFE | VIS_CSTYLE);
172 					strlcat(output, "\"", sizeof output);
173 					log_debug("mda_out: %s", output);
174 				}
175 				free(buf);
176 				fclose(fp);
177 			}
178 
179 			/*
180 			 * Choose between parent's description of error and
181 			 * child's output, the latter having preference over
182 			 * the former.
183 			 */
184 			error = NULL;
185 			parent_error = imsg->data;
186 			if (strcmp(parent_error, "exited okay") == 0) {
187 				if (!feof(s->datafp) || s->w.queued)
188 					error = "mda exited prematurely";
189 			} else {
190 				if (output[0])
191 					error = output;
192 				else
193 					error = parent_error;
194 			}
195 
196 			/* update queue entry */
197 			if (error == NULL)
198 				s->msg.delivery.status = DS_ACCEPTED;
199 			else
200 				strlcpy(s->msg.delivery.errorline, error,
201 				    sizeof s->msg.delivery.errorline);
202 			imsg_compose_event(env->sc_ievs[PROC_QUEUE],
203 			    IMSG_QUEUE_MESSAGE_UPDATE, 0, 0, -1, &s->msg,
204 			    sizeof s->msg);
205 
206 			/*
207 			 * XXX: which struct path gets used for logging depends
208 			 * on whether lka did aliases or .forward processing;
209 			 * lka may need to be changed to present data in more
210 			 * unified way.
211 			 */
212 			if (s->msg.rule.r_action == A_MAILDIR ||
213 			    s->msg.rule.r_action == A_MBOX)
214 				maddr = &s->msg.delivery.rcpt;
215 			else
216 				maddr = &s->msg.delivery.rcpt_orig;
217 
218 			/* log status */
219 			if (error && asprintf(&error, "Error (%s)", error) < 0)
220 				fatal("mda: asprintf");
221 			log_info("%016llx: to=<%s@%s>, delay=%lld, stat=%s",
222 			    s->msg.delivery.id, maddr->user, maddr->domain,
223 			    (long long int) (time(NULL) - s->msg.delivery.creation),
224 			    error ? error : "Sent");
225 			free(error);
226 
227 			/* destroy session */
228 			LIST_REMOVE(s, entry);
229 			if (s->w.fd != -1)
230 				close(s->w.fd);
231 			if (s->datafp)
232 				fclose(s->datafp);
233 			msgbuf_clear(&s->w);
234 			event_del(&s->ev);
235 			free(s);
236 
237 			/* update queue's session count */
238 			imsg_compose_event(env->sc_ievs[PROC_QUEUE],
239 			    IMSG_MDA_SESS_NEW, 0, 0, -1, NULL, 0);
240 			return;
241 
242 		case IMSG_CTL_VERBOSE:
243 			log_verbose(*(int *)imsg->data);
244 			return;
245 		}
246 	}
247 
248 	fatalx("mda_imsg: unexpected imsg");
249 }
250 
251 static void
252 mda_sig_handler(int sig, short event, void *p)
253 {
254 	switch (sig) {
255 	case SIGINT:
256 	case SIGTERM:
257 		mda_shutdown();
258 		break;
259 	default:
260 		fatalx("mda_sig_handler: unexpected signal");
261 	}
262 }
263 
264 static void
265 mda_shutdown(void)
266 {
267 	log_info("mail delivery agent exiting");
268 	_exit(0);
269 }
270 
271 pid_t
272 mda(void)
273 {
274 	pid_t		 pid;
275 	struct passwd	*pw;
276 
277 	struct event	 ev_sigint;
278 	struct event	 ev_sigterm;
279 
280 	struct peer peers[] = {
281 		{ PROC_PARENT,	imsg_dispatch },
282 		{ PROC_QUEUE,	imsg_dispatch }
283 	};
284 
285 	switch (pid = fork()) {
286 	case -1:
287 		fatal("mda: cannot fork");
288 	case 0:
289 		break;
290 	default:
291 		return (pid);
292 	}
293 
294 	purge_config(PURGE_EVERYTHING);
295 
296 	pw = env->sc_pw;
297 
298 	if (chroot(pw->pw_dir) == -1)
299 		fatal("mda: chroot");
300 	if (chdir("/") == -1)
301 		fatal("mda: chdir(\"/\")");
302 
303 	smtpd_process = PROC_MDA;
304 	setproctitle("%s", env->sc_title[smtpd_process]);
305 
306 	if (setgroups(1, &pw->pw_gid) ||
307 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
308 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
309 		fatal("mda: cannot drop privileges");
310 
311 	LIST_INIT(&env->mda_sessions);
312 
313 	imsg_callback = mda_imsg;
314 	event_init();
315 
316 	signal_set(&ev_sigint, SIGINT, mda_sig_handler, NULL);
317 	signal_set(&ev_sigterm, SIGTERM, mda_sig_handler, NULL);
318 	signal_add(&ev_sigint, NULL);
319 	signal_add(&ev_sigterm, NULL);
320 	signal(SIGPIPE, SIG_IGN);
321 	signal(SIGHUP, SIG_IGN);
322 
323 	config_pipes(peers, nitems(peers));
324 	config_peers(peers, nitems(peers));
325 
326 	if (event_dispatch() < 0)
327 		fatal("event_dispatch");
328 	mda_shutdown();
329 
330 	return (0);
331 }
332 
333 static void
334 mda_store(struct mda_session *s)
335 {
336 	char		*p;
337 	struct ibuf	*buf;
338 	int		 len;
339 
340 	if (s->msg.delivery.from.user[0] && s->msg.delivery.from.domain[0])
341 		/* XXX: remove user provided Return-Path, if any */
342 		len = asprintf(&p, "Return-Path: %s@%s\nDelivered-To: %s@%s\n",
343 		    s->msg.delivery.from.user, s->msg.delivery.from.domain,
344 		    s->msg.delivery.rcpt_orig.user,
345 		    s->msg.delivery.rcpt_orig.domain);
346 	else
347 		len = asprintf(&p, "Delivered-To: %s@%s\n",
348 		    s->msg.delivery.rcpt_orig.user,
349 		    s->msg.delivery.rcpt_orig.domain);
350 
351 	if (len == -1)
352 		fatal("mda_store: asprintf");
353 
354 	session_socket_blockmode(s->w.fd, BM_NONBLOCK);
355 	if ((buf = ibuf_open(len)) == NULL)
356 		fatal(NULL);
357 	if (ibuf_add(buf, p, len) < 0)
358 		fatal(NULL);
359 	ibuf_close(&s->w, buf);
360 	event_set(&s->ev, s->w.fd, EV_WRITE, mda_store_event, s);
361 	event_add(&s->ev, NULL);
362 	free(p);
363 }
364 
365 static void
366 mda_store_event(int fd, short event, void *p)
367 {
368 	char			 tmp[16384];
369 	struct mda_session	*s = p;
370 	struct ibuf		*buf;
371 	size_t			 len;
372 
373 	if (s->w.queued == 0) {
374 		if ((buf = ibuf_dynamic(0, sizeof tmp)) == NULL)
375 			fatal(NULL);
376 		len = fread(tmp, 1, sizeof tmp, s->datafp);
377 		if (ferror(s->datafp))
378 			fatal("mda_store_event: fread failed");
379 		if (feof(s->datafp) && len == 0) {
380 			close(s->w.fd);
381 			s->w.fd = -1;
382 			return;
383 		}
384 		if (ibuf_add(buf, tmp, len) < 0)
385 			fatal(NULL);
386 		ibuf_close(&s->w, buf);
387 	}
388 
389 	if (ibuf_write(&s->w) < 0) {
390 		close(s->w.fd);
391 		s->w.fd = -1;
392 		return;
393 	}
394 
395 	event_set(&s->ev, fd, EV_WRITE, mda_store_event, s);
396 	event_add(&s->ev, NULL);
397 }
398 
399 static struct mda_session *
400 mda_lookup(u_int32_t id)
401 {
402 	struct mda_session *s;
403 
404 	LIST_FOREACH(s, &env->mda_sessions, entry)
405 		if (s->id == id)
406 			break;
407 
408 	if (s == NULL)
409 		fatalx("mda: bogus session id");
410 
411 	return s;
412 }
413