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