xref: /openbsd-src/usr.sbin/smtpd/lka_session.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: lka_session.c,v 1.94 2020/12/31 08:27:15 martijn Exp $	*/
2 
3 /*
4  * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
5  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/queue.h>
22 #include <sys/tree.h>
23 #include <sys/socket.h>
24 #include <sys/wait.h>
25 
26 #include <netinet/in.h>
27 
28 #include <ctype.h>
29 #include <errno.h>
30 #include <event.h>
31 #include <imsg.h>
32 #include <resolv.h>
33 #include <pwd.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <limits.h>
40 
41 #include "smtpd.h"
42 #include "log.h"
43 
44 #define	EXPAND_DEPTH	10
45 
46 #define	F_WAITING	0x01
47 
48 struct lka_session {
49 	uint64_t		 id; /* given by smtp */
50 
51 	TAILQ_HEAD(, envelope)	 deliverylist;
52 	struct expand		 expand;
53 
54 	int			 flags;
55 	int			 error;
56 	const char		*errormsg;
57 	struct envelope		 envelope;
58 	struct xnodes		 nodes;
59 	/* waiting for fwdrq */
60 	struct rule		*rule;
61 	struct expandnode	*node;
62 };
63 
64 static void lka_expand(struct lka_session *, struct rule *,
65     struct expandnode *);
66 static void lka_submit(struct lka_session *, struct rule *,
67     struct expandnode *);
68 static void lka_resume(struct lka_session *);
69 
70 static int		init;
71 static struct tree	sessions;
72 
73 void
74 lka_session(uint64_t id, struct envelope *envelope)
75 {
76 	struct lka_session	*lks;
77 	struct expandnode	 xn;
78 
79 	if (init == 0) {
80 		init = 1;
81 		tree_init(&sessions);
82 	}
83 
84 	lks = xcalloc(1, sizeof(*lks));
85 	lks->id = id;
86 	RB_INIT(&lks->expand.tree);
87 	TAILQ_INIT(&lks->deliverylist);
88 	tree_xset(&sessions, lks->id, lks);
89 
90 	lks->envelope = *envelope;
91 
92 	TAILQ_INIT(&lks->nodes);
93 	memset(&xn, 0, sizeof xn);
94 	xn.type = EXPAND_ADDRESS;
95 	xn.u.mailaddr = lks->envelope.rcpt;
96 	lks->expand.parent = NULL;
97 	lks->expand.rule = NULL;
98 	lks->expand.queue = &lks->nodes;
99 	expand_insert(&lks->expand, &xn);
100 	lka_resume(lks);
101 }
102 
103 void
104 lka_session_forward_reply(struct forward_req *fwreq, int fd)
105 {
106 	struct lka_session     *lks;
107 	struct dispatcher      *dsp;
108 	struct rule	       *rule;
109 	struct expandnode      *xn;
110 	int			ret;
111 
112 	lks = tree_xget(&sessions, fwreq->id);
113 	xn = lks->node;
114 	rule = lks->rule;
115 
116 	lks->flags &= ~F_WAITING;
117 
118 	switch (fwreq->status) {
119 	case 0:
120 		/* permanent failure while lookup ~/.forward */
121 		log_trace(TRACE_EXPAND, "expand: ~/.forward failed for user %s",
122 		    fwreq->user);
123 		lks->error = LKA_PERMFAIL;
124 		break;
125 	case 1:
126 		if (fd == -1) {
127 			dsp = dict_get(env->sc_dispatchers, lks->rule->dispatcher);
128 			if (dsp->u.local.forward_only) {
129 				log_trace(TRACE_EXPAND, "expand: no .forward "
130 				    "for user %s on forward-only rule", fwreq->user);
131 				lks->error = LKA_TEMPFAIL;
132 			}
133 			else if (dsp->u.local.expand_only) {
134 				log_trace(TRACE_EXPAND, "expand: no .forward "
135 				    "for user %s and no default action on rule", fwreq->user);
136 				lks->error = LKA_PERMFAIL;
137 			}
138 			else {
139 				log_trace(TRACE_EXPAND, "expand: no .forward for "
140 				    "user %s, just deliver", fwreq->user);
141 				lka_submit(lks, rule, xn);
142 			}
143 		}
144 		else {
145 			dsp = dict_get(env->sc_dispatchers, rule->dispatcher);
146 
147 			/* expand for the current user and rule */
148 			lks->expand.rule = rule;
149 			lks->expand.parent = xn;
150 
151 			/* forwards_get() will close the descriptor no matter what */
152 			ret = forwards_get(fd, &lks->expand);
153 			if (ret == -1) {
154 				log_trace(TRACE_EXPAND, "expand: temporary "
155 				    "forward error for user %s", fwreq->user);
156 				lks->error = LKA_TEMPFAIL;
157 			}
158 			else if (ret == 0) {
159 				if (dsp->u.local.forward_only) {
160 					log_trace(TRACE_EXPAND, "expand: empty .forward "
161 					    "for user %s on forward-only rule", fwreq->user);
162 					lks->error = LKA_TEMPFAIL;
163 				}
164 				else if (dsp->u.local.expand_only) {
165 					log_trace(TRACE_EXPAND, "expand: empty .forward "
166 					    "for user %s and no default action on rule", fwreq->user);
167 					lks->error = LKA_PERMFAIL;
168 				}
169 				else {
170 					log_trace(TRACE_EXPAND, "expand: empty .forward "
171 					    "for user %s, just deliver", fwreq->user);
172 					lka_submit(lks, rule, xn);
173 				}
174 			}
175 		}
176 		break;
177 	default:
178 		/* temporary failure while looking up ~/.forward */
179 		lks->error = LKA_TEMPFAIL;
180 	}
181 
182 	if (lks->error == LKA_TEMPFAIL && lks->errormsg == NULL)
183 		lks->errormsg = "424 4.2.4 Mailing list expansion problem";
184 	if (lks->error == LKA_PERMFAIL && lks->errormsg == NULL)
185 		lks->errormsg = "524 5.2.4 Mailing list expansion problem";
186 
187 	lka_resume(lks);
188 }
189 
190 static void
191 lka_resume(struct lka_session *lks)
192 {
193 	struct envelope		*ep;
194 	struct expandnode	*xn;
195 
196 	if (lks->error)
197 		goto error;
198 
199 	/* pop next node and expand it */
200 	while ((xn = TAILQ_FIRST(&lks->nodes))) {
201 		TAILQ_REMOVE(&lks->nodes, xn, tq_entry);
202 		lka_expand(lks, xn->rule, xn);
203 		if (lks->flags & F_WAITING)
204 			return;
205 		if (lks->error)
206 			goto error;
207 	}
208 
209 	/* delivery list is empty, reject */
210 	if (TAILQ_FIRST(&lks->deliverylist) == NULL) {
211 		log_trace(TRACE_EXPAND, "expand: lka_done: expanded to empty "
212 		    "delivery list");
213 		lks->error = LKA_PERMFAIL;
214 		lks->errormsg = "524 5.2.4 Mailing list expansion problem";
215 	}
216     error:
217 	if (lks->error) {
218 		m_create(p_dispatcher, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1);
219 		m_add_id(p_dispatcher, lks->id);
220 		m_add_int(p_dispatcher, lks->error);
221 
222 		if (lks->errormsg)
223 			m_add_string(p_dispatcher, lks->errormsg);
224 		else {
225 			if (lks->error == LKA_PERMFAIL)
226 				m_add_string(p_dispatcher, "550 Invalid recipient");
227 			else if (lks->error == LKA_TEMPFAIL)
228 				m_add_string(p_dispatcher, "451 Temporary failure");
229 		}
230 
231 		m_close(p_dispatcher);
232 		while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) {
233 			TAILQ_REMOVE(&lks->deliverylist, ep, entry);
234 			free(ep);
235 		}
236 	}
237 	else {
238 		/* Process the delivery list and submit envelopes to queue */
239 		while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) {
240 			TAILQ_REMOVE(&lks->deliverylist, ep, entry);
241 			m_create(p_queue, IMSG_LKA_ENVELOPE_SUBMIT, 0, 0, -1);
242 			m_add_id(p_queue, lks->id);
243 			m_add_envelope(p_queue, ep);
244 			m_close(p_queue);
245 			free(ep);
246 		}
247 
248 		m_create(p_queue, IMSG_LKA_ENVELOPE_COMMIT, 0, 0, -1);
249 		m_add_id(p_queue, lks->id);
250 		m_close(p_queue);
251 	}
252 
253 	expand_clear(&lks->expand);
254 	tree_xpop(&sessions, lks->id);
255 	free(lks);
256 }
257 
258 static void
259 lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn)
260 {
261 	struct forward_req	fwreq;
262 	struct envelope		ep;
263 	struct expandnode	node;
264 	struct mailaddr		maddr;
265 	struct dispatcher      *dsp;
266 	struct table	       *userbase;
267 	int			r;
268 	union lookup		lk;
269 	char		       *tag;
270 	const char	       *srs_decoded;
271 
272 	if (xn->depth >= EXPAND_DEPTH) {
273 		log_trace(TRACE_EXPAND, "expand: lka_expand: node too deep.");
274 		lks->error = LKA_PERMFAIL;
275 		lks->errormsg = "524 5.2.4 Mailing list expansion problem";
276 		return;
277 	}
278 
279 	switch (xn->type) {
280 	case EXPAND_INVALID:
281 	case EXPAND_INCLUDE:
282 		fatalx("lka_expand: unexpected type");
283 		break;
284 
285 	case EXPAND_ADDRESS:
286 
287 		log_trace(TRACE_EXPAND, "expand: lka_expand: address: %s@%s "
288 		    "[depth=%d]",
289 		    xn->u.mailaddr.user, xn->u.mailaddr.domain, xn->depth);
290 
291 
292 		ep = lks->envelope;
293 		ep.dest = xn->u.mailaddr;
294 		if (xn->parent) /* nodes with parent are forward addresses */
295 			ep.flags |= EF_INTERNAL;
296 
297 		/* handle SRS */
298 		if (env->sc_srs_key != NULL &&
299 		    ep.sender.user[0] == '\0' &&
300 		    (strncasecmp(ep.rcpt.user, "SRS0=", 5) == 0 ||
301 			strncasecmp(ep.rcpt.user, "SRS1=", 5) == 0)) {
302 			srs_decoded = srs_decode(mailaddr_to_text(&ep.rcpt));
303 			if (srs_decoded &&
304 			    text_to_mailaddr(&ep.rcpt, srs_decoded)) {
305 				/* flag envelope internal and override rcpt */
306 				ep.flags |= EF_INTERNAL;
307 				xn->u.mailaddr = ep.rcpt;
308 				lks->envelope = ep;
309 			}
310 			else {
311 				log_warn("SRS failed to decode: %s",
312 				    mailaddr_to_text(&ep.rcpt));
313 			}
314 		}
315 
316 		/* Pass the node through the ruleset */
317 		rule = ruleset_match(&ep);
318 		if (rule == NULL || rule->reject) {
319 			lks->error = (errno == EAGAIN) ?
320 			    LKA_TEMPFAIL : LKA_PERMFAIL;
321 			break;
322 		}
323 
324 		dsp = dict_xget(env->sc_dispatchers, rule->dispatcher);
325 		if (dsp->type == DISPATCHER_REMOTE) {
326 			lka_submit(lks, rule, xn);
327 		}
328 		else if (dsp->u.local.table_virtual) {
329 			/* expand */
330 			lks->expand.rule = rule;
331 			lks->expand.parent = xn;
332 
333 			/* temporary replace the mailaddr with a copy where
334 			 * we eventually strip the '+'-part before lookup.
335 			 */
336 			maddr = xn->u.mailaddr;
337 			xlowercase(maddr.user, xn->u.mailaddr.user,
338 			    sizeof maddr.user);
339 			r = aliases_virtual_get(&lks->expand, &maddr);
340 			if (r == -1) {
341 				lks->error = LKA_TEMPFAIL;
342 				log_trace(TRACE_EXPAND, "expand: lka_expand: "
343 				    "error in virtual alias lookup");
344 			}
345 			else if (r == 0) {
346 				lks->error = LKA_PERMFAIL;
347 				log_trace(TRACE_EXPAND, "expand: lka_expand: "
348 				    "no aliases for virtual");
349 			}
350 			if (lks->error == LKA_TEMPFAIL && lks->errormsg == NULL)
351 				lks->errormsg = "424 4.2.4 Mailing list expansion problem";
352 			if (lks->error == LKA_PERMFAIL && lks->errormsg == NULL)
353 				lks->errormsg = "524 5.2.4 Mailing list expansion problem";
354 		}
355 		else {
356 			lks->expand.rule = rule;
357 			lks->expand.parent = xn;
358 			xn->rule = rule;
359 
360 			memset(&node, 0, sizeof node);
361 			node.type = EXPAND_USERNAME;
362 			xlowercase(node.u.user, xn->u.mailaddr.user,
363 			    sizeof node.u.user);
364 			expand_insert(&lks->expand, &node);
365 		}
366 		break;
367 
368 	case EXPAND_USERNAME:
369 		log_trace(TRACE_EXPAND, "expand: lka_expand: username: %s "
370 		    "[depth=%d, sameuser=%d]",
371 		    xn->u.user, xn->depth, xn->sameuser);
372 
373 		/* expand aliases with the given rule */
374 		dsp = dict_xget(env->sc_dispatchers, rule->dispatcher);
375 
376 		lks->expand.rule = rule;
377 		lks->expand.parent = xn;
378 
379 		if (!xn->sameuser &&
380 		    (dsp->u.local.table_alias || dsp->u.local.table_virtual)) {
381 			if (dsp->u.local.table_alias)
382 				r = aliases_get(&lks->expand, xn->u.user);
383 			if (dsp->u.local.table_virtual)
384 				r = aliases_virtual_get(&lks->expand, &xn->u.mailaddr);
385 			if (r == -1) {
386 				log_trace(TRACE_EXPAND, "expand: lka_expand: "
387 				    "error in alias lookup");
388 				lks->error = LKA_TEMPFAIL;
389 				if (lks->errormsg == NULL)
390 					lks->errormsg = "424 4.2.4 Mailing list expansion problem";
391 			}
392 			if (r)
393 				break;
394 		}
395 
396 		/* gilles+hackers@ -> gilles@ */
397 		if ((tag = strchr(xn->u.user, *env->sc_subaddressing_delim)) != NULL) {
398 			*tag++ = '\0';
399 			(void)strlcpy(xn->subaddress, tag, sizeof xn->subaddress);
400 		}
401 
402 		userbase = table_find(env, dsp->u.local.table_userbase);
403 		r = table_lookup(userbase, K_USERINFO, xn->u.user, &lk);
404 		if (r == -1) {
405 			log_trace(TRACE_EXPAND, "expand: lka_expand: "
406 			    "backend error while searching user");
407 			lks->error = LKA_TEMPFAIL;
408 			break;
409 		}
410 		if (r == 0) {
411 			log_trace(TRACE_EXPAND, "expand: lka_expand: "
412 			    "user-part does not match system user");
413 			lks->error = LKA_PERMFAIL;
414 			break;
415 		}
416 		xn->realuser = 1;
417 
418 		if (xn->sameuser && xn->parent->forwarded) {
419 			log_trace(TRACE_EXPAND, "expand: lka_expand: same "
420 			    "user, submitting");
421 			lka_submit(lks, rule, xn);
422 			break;
423 		}
424 
425 		/* no aliases found, query forward file */
426 		lks->rule = rule;
427 		lks->node = xn;
428 		xn->forwarded = 1;
429 
430 		memset(&fwreq, 0, sizeof(fwreq));
431 		fwreq.id = lks->id;
432 		(void)strlcpy(fwreq.user, lk.userinfo.username, sizeof(fwreq.user));
433 		(void)strlcpy(fwreq.directory, lk.userinfo.directory, sizeof(fwreq.directory));
434 		fwreq.uid = lk.userinfo.uid;
435 		fwreq.gid = lk.userinfo.gid;
436 
437 		m_compose(p_parent, IMSG_LKA_OPEN_FORWARD, 0, 0, -1,
438 		    &fwreq, sizeof(fwreq));
439 		lks->flags |= F_WAITING;
440 		break;
441 
442 	case EXPAND_FILENAME:
443 		dsp = dict_xget(env->sc_dispatchers, rule->dispatcher);
444 		if (dsp->u.local.forward_only) {
445 			log_trace(TRACE_EXPAND, "expand: filename matched on forward-only rule");
446 			lks->error = LKA_TEMPFAIL;
447 			break;
448 		}
449 		log_trace(TRACE_EXPAND, "expand: lka_expand: filename: %s "
450 		    "[depth=%d]", xn->u.buffer, xn->depth);
451 		lka_submit(lks, rule, xn);
452 		break;
453 
454 	case EXPAND_ERROR:
455 		dsp = dict_xget(env->sc_dispatchers, rule->dispatcher);
456 		if (dsp->u.local.forward_only) {
457 			log_trace(TRACE_EXPAND, "expand: error matched on forward-only rule");
458 			lks->error = LKA_TEMPFAIL;
459 			break;
460 		}
461 		log_trace(TRACE_EXPAND, "expand: lka_expand: error: %s "
462 		    "[depth=%d]", xn->u.buffer, xn->depth);
463 		if (xn->u.buffer[0] == '4')
464 			lks->error = LKA_TEMPFAIL;
465 		else if (xn->u.buffer[0] == '5')
466 			lks->error = LKA_PERMFAIL;
467 		lks->errormsg = xn->u.buffer;
468 		break;
469 
470 	case EXPAND_FILTER:
471 		dsp = dict_xget(env->sc_dispatchers, rule->dispatcher);
472 		if (dsp->u.local.forward_only) {
473 			log_trace(TRACE_EXPAND, "expand: filter matched on forward-only rule");
474 			lks->error = LKA_TEMPFAIL;
475 			break;
476 		}
477 		log_trace(TRACE_EXPAND, "expand: lka_expand: filter: %s "
478 		    "[depth=%d]", xn->u.buffer, xn->depth);
479 		lka_submit(lks, rule, xn);
480 		break;
481 	}
482 }
483 
484 static struct expandnode *
485 lka_find_ancestor(struct expandnode *xn, enum expand_type type)
486 {
487 	while (xn && (xn->type != type))
488 		xn = xn->parent;
489 	if (xn == NULL) {
490 		log_warnx("warn: lka_find_ancestor: no ancestors of type %d",
491 		    type);
492 		fatalx(NULL);
493 	}
494 	return (xn);
495 }
496 
497 static void
498 lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn)
499 {
500 	struct envelope		*ep;
501 	struct dispatcher	*dsp;
502 	const char		*user;
503 	const char		*format;
504 
505 	ep = xmemdup(&lks->envelope, sizeof *ep);
506 	(void)strlcpy(ep->dispatcher, rule->dispatcher, sizeof ep->dispatcher);
507 
508 	dsp = dict_xget(env->sc_dispatchers, ep->dispatcher);
509 
510 	switch (dsp->type) {
511 	case DISPATCHER_REMOTE:
512 		if (xn->type != EXPAND_ADDRESS)
513 			fatalx("lka_deliver: expect address");
514 		ep->type = D_MTA;
515 		ep->dest = xn->u.mailaddr;
516 		break;
517 
518 	case DISPATCHER_BOUNCE:
519 	case DISPATCHER_LOCAL:
520 		if (xn->type != EXPAND_USERNAME &&
521 		    xn->type != EXPAND_FILENAME &&
522 		    xn->type != EXPAND_FILTER)
523 			fatalx("lka_deliver: wrong type: %d", xn->type);
524 
525 		ep->type = D_MDA;
526 		ep->dest = lka_find_ancestor(xn, EXPAND_ADDRESS)->u.mailaddr;
527 		if (xn->type == EXPAND_USERNAME) {
528 			(void)strlcpy(ep->mda_user, xn->u.user, sizeof(ep->mda_user));
529 			(void)strlcpy(ep->mda_subaddress, xn->subaddress, sizeof(ep->mda_subaddress));
530 		}
531 		else {
532 			user = !xn->parent->realuser ?
533 			    SMTPD_USER :
534 			    xn->parent->u.user;
535 			(void)strlcpy(ep->mda_user, user, sizeof (ep->mda_user));
536 
537 			/* this battle needs to be fought ... */
538 			if (xn->type == EXPAND_FILTER &&
539 			    strcmp(ep->mda_user, SMTPD_USER) == 0)
540 				log_warnx("commands executed from aliases "
541 				    "run with %s privileges", SMTPD_USER);
542 
543 			if (xn->type == EXPAND_FILENAME)
544 				format = "/usr/libexec/mail.mboxfile -f %%{mbox.from} %s";
545 			else if (xn->type == EXPAND_FILTER)
546 				format = "%s";
547 			(void)snprintf(ep->mda_exec, sizeof(ep->mda_exec),
548 			    format, xn->u.buffer);
549 		}
550 		break;
551 	}
552 
553 	TAILQ_INSERT_TAIL(&lks->deliverylist, ep, entry);
554 }
555