GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/smtpd/smtpd/../lka_session.c Lines: 0 273 0.0 %
Date: 2017-11-07 Branches: 0 149 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: lka_session.c,v 1.81 2017/05/26 21:30:00 gilles 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), "lka_session");
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.rule = NULL;
97
	lks->expand.queue = &lks->nodes;
98
	expand_insert(&lks->expand, &xn);
99
	lka_resume(lks);
100
}
101
102
void
103
lka_session_forward_reply(struct forward_req *fwreq, int fd)
104
{
105
	struct lka_session     *lks;
106
	struct rule	       *rule;
107
	struct expandnode      *xn;
108
	int			ret;
109
110
	lks = tree_xget(&sessions, fwreq->id);
111
	xn = lks->node;
112
	rule = lks->rule;
113
114
	lks->flags &= ~F_WAITING;
115
116
	switch (fwreq->status) {
117
	case 0:
118
		/* permanent failure while lookup ~/.forward */
119
		log_trace(TRACE_EXPAND, "expand: ~/.forward failed for user %s",
120
		    fwreq->user);
121
		lks->error = LKA_PERMFAIL;
122
		break;
123
	case 1:
124
		if (fd == -1) {
125
			if (lks->expand.rule->r_forwardonly) {
126
				log_trace(TRACE_EXPAND, "expand: no .forward "
127
				    "for user %s on forward-only rule", fwreq->user);
128
				lks->error = LKA_TEMPFAIL;
129
			}
130
			else if (lks->expand.rule->r_action == A_NONE) {
131
				log_trace(TRACE_EXPAND, "expand: no .forward "
132
				    "for user %s and no default action on rule", fwreq->user);
133
				lks->error = LKA_PERMFAIL;
134
			}
135
			else {
136
				log_trace(TRACE_EXPAND, "expand: no .forward for "
137
				    "user %s, just deliver", fwreq->user);
138
				lka_submit(lks, rule, xn);
139
			}
140
		}
141
		else {
142
			/* expand for the current user and rule */
143
			lks->expand.rule = rule;
144
			lks->expand.parent = xn;
145
			lks->expand.alias = 0;
146
			xn->mapping = rule->r_mapping;
147
			xn->userbase = rule->r_userbase;
148
			/* forwards_get() will close the descriptor no matter what */
149
			ret = forwards_get(fd, &lks->expand);
150
			if (ret == -1) {
151
				log_trace(TRACE_EXPAND, "expand: temporary "
152
				    "forward error for user %s", fwreq->user);
153
				lks->error = LKA_TEMPFAIL;
154
			}
155
			else if (ret == 0) {
156
				if (lks->expand.rule->r_forwardonly) {
157
					log_trace(TRACE_EXPAND, "expand: empty .forward "
158
					    "for user %s on forward-only rule", fwreq->user);
159
					lks->error = LKA_TEMPFAIL;
160
				}
161
				else if (lks->expand.rule->r_action == A_NONE) {
162
					log_trace(TRACE_EXPAND, "expand: empty .forward "
163
					    "for user %s and no default action on rule", fwreq->user);
164
					lks->error = LKA_PERMFAIL;
165
				}
166
				else {
167
					log_trace(TRACE_EXPAND, "expand: empty .forward "
168
					    "for user %s, just deliver", fwreq->user);
169
					lka_submit(lks, rule, xn);
170
				}
171
			}
172
		}
173
		break;
174
	default:
175
		/* temporary failure while looking up ~/.forward */
176
		lks->error = LKA_TEMPFAIL;
177
	}
178
179
	lka_resume(lks);
180
}
181
182
static void
183
lka_resume(struct lka_session *lks)
184
{
185
	struct envelope		*ep;
186
	struct expandnode	*xn;
187
188
	if (lks->error)
189
		goto error;
190
191
	/* pop next node and expand it */
192
	while ((xn = TAILQ_FIRST(&lks->nodes))) {
193
		TAILQ_REMOVE(&lks->nodes, xn, tq_entry);
194
		lka_expand(lks, xn->rule, xn);
195
		if (lks->flags & F_WAITING)
196
			return;
197
		if (lks->error)
198
			goto error;
199
	}
200
201
	/* delivery list is empty, reject */
202
	if (TAILQ_FIRST(&lks->deliverylist) == NULL) {
203
		log_trace(TRACE_EXPAND, "expand: lka_done: expanded to empty "
204
		    "delivery list");
205
		lks->error = LKA_PERMFAIL;
206
	}
207
    error:
208
	if (lks->error) {
209
		m_create(p_pony, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1);
210
		m_add_id(p_pony, lks->id);
211
		m_add_int(p_pony, lks->error);
212
213
		if (lks->errormsg)
214
			m_add_string(p_pony, lks->errormsg);
215
		else {
216
			if (lks->error == LKA_PERMFAIL)
217
				m_add_string(p_pony, "550 Invalid recipient");
218
			else if (lks->error == LKA_TEMPFAIL)
219
				m_add_string(p_pony, "451 Temporary failure");
220
		}
221
222
		m_close(p_pony);
223
		while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) {
224
			TAILQ_REMOVE(&lks->deliverylist, ep, entry);
225
			free(ep);
226
		}
227
	}
228
	else {
229
		/* Process the delivery list and submit envelopes to queue */
230
		while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) {
231
			TAILQ_REMOVE(&lks->deliverylist, ep, entry);
232
			m_create(p_queue, IMSG_LKA_ENVELOPE_SUBMIT, 0, 0, -1);
233
			m_add_id(p_queue, lks->id);
234
			m_add_envelope(p_queue, ep);
235
			m_close(p_queue);
236
			free(ep);
237
		}
238
239
		m_create(p_queue, IMSG_LKA_ENVELOPE_COMMIT, 0, 0, -1);
240
		m_add_id(p_queue, lks->id);
241
		m_close(p_queue);
242
	}
243
244
	expand_clear(&lks->expand);
245
	tree_xpop(&sessions, lks->id);
246
	free(lks);
247
}
248
249
static void
250
lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn)
251
{
252
	struct forward_req	fwreq;
253
	struct envelope		ep;
254
	struct expandnode	node;
255
	struct mailaddr		maddr;
256
	int			r;
257
	union lookup		lk;
258
	char		       *tag;
259
260
	if (xn->depth >= EXPAND_DEPTH) {
261
		log_trace(TRACE_EXPAND, "expand: lka_expand: node too deep.");
262
		lks->error = LKA_PERMFAIL;
263
		return;
264
	}
265
266
	switch (xn->type) {
267
	case EXPAND_INVALID:
268
	case EXPAND_INCLUDE:
269
		fatalx("lka_expand: unexpected type");
270
		break;
271
272
	case EXPAND_ADDRESS:
273
274
		log_trace(TRACE_EXPAND, "expand: lka_expand: address: %s@%s "
275
		    "[depth=%d]",
276
		    xn->u.mailaddr.user, xn->u.mailaddr.domain, xn->depth);
277
278
		/* Pass the node through the ruleset */
279
		ep = lks->envelope;
280
		ep.dest = xn->u.mailaddr;
281
		if (xn->parent) /* nodes with parent are forward addresses */
282
			ep.flags |= EF_INTERNAL;
283
		rule = ruleset_match(&ep);
284
		if (rule == NULL || rule->r_decision == R_REJECT) {
285
			lks->error = (errno == EAGAIN) ?
286
			    LKA_TEMPFAIL : LKA_PERMFAIL;
287
			break;
288
		}
289
290
		xn->mapping = rule->r_mapping;
291
		xn->userbase = rule->r_userbase;
292
293
		if (rule->r_action == A_RELAY || rule->r_action == A_RELAYVIA) {
294
			lka_submit(lks, rule, xn);
295
		}
296
		else if (rule->r_desttype == DEST_VDOM) {
297
			/* expand */
298
			lks->expand.rule = rule;
299
			lks->expand.parent = xn;
300
			lks->expand.alias = 1;
301
302
			/* temporary replace the mailaddr with a copy where
303
			 * we eventually strip the '+'-part before lookup.
304
			 */
305
			maddr = xn->u.mailaddr;
306
			xlowercase(maddr.user, xn->u.mailaddr.user,
307
			    sizeof maddr.user);
308
			r = aliases_virtual_get(&lks->expand, &maddr);
309
			if (r == -1) {
310
				lks->error = LKA_TEMPFAIL;
311
				log_trace(TRACE_EXPAND, "expand: lka_expand: "
312
				    "error in virtual alias lookup");
313
			}
314
			else if (r == 0) {
315
				lks->error = LKA_PERMFAIL;
316
				log_trace(TRACE_EXPAND, "expand: lka_expand: "
317
				    "no aliases for virtual");
318
			}
319
		}
320
		else {
321
			lks->expand.rule = rule;
322
			lks->expand.parent = xn;
323
			lks->expand.alias = 1;
324
			memset(&node, 0, sizeof node);
325
			node.type = EXPAND_USERNAME;
326
			xlowercase(node.u.user, xn->u.mailaddr.user,
327
			    sizeof node.u.user);
328
			node.mapping = rule->r_mapping;
329
			node.userbase = rule->r_userbase;
330
			expand_insert(&lks->expand, &node);
331
		}
332
		break;
333
334
	case EXPAND_USERNAME:
335
		log_trace(TRACE_EXPAND, "expand: lka_expand: username: %s "
336
		    "[depth=%d]", xn->u.user, xn->depth);
337
338
		if (xn->sameuser) {
339
			log_trace(TRACE_EXPAND, "expand: lka_expand: same "
340
			    "user, submitting");
341
			lka_submit(lks, rule, xn);
342
			break;
343
		}
344
345
		/* expand aliases with the given rule */
346
		lks->expand.rule = rule;
347
		lks->expand.parent = xn;
348
		lks->expand.alias = 1;
349
		xn->mapping = rule->r_mapping;
350
		xn->userbase = rule->r_userbase;
351
		if (rule->r_mapping) {
352
			r = aliases_get(&lks->expand, xn->u.user);
353
			if (r == -1) {
354
				log_trace(TRACE_EXPAND, "expand: lka_expand: "
355
				    "error in alias lookup");
356
				lks->error = LKA_TEMPFAIL;
357
			}
358
			if (r)
359
				break;
360
		}
361
362
		/* gilles+hackers@ -> gilles@ */
363
		if ((tag = strchr(xn->u.user, *env->sc_subaddressing_delim)) != NULL)
364
			*tag++ = '\0';
365
366
		r = table_lookup(rule->r_userbase, NULL, xn->u.user, K_USERINFO, &lk);
367
		if (r == -1) {
368
			log_trace(TRACE_EXPAND, "expand: lka_expand: "
369
			    "backend error while searching user");
370
			lks->error = LKA_TEMPFAIL;
371
			break;
372
		}
373
		if (r == 0) {
374
			log_trace(TRACE_EXPAND, "expand: lka_expand: "
375
			    "user-part does not match system user");
376
			lks->error = LKA_PERMFAIL;
377
			break;
378
		}
379
380
		/* no aliases found, query forward file */
381
		lks->rule = rule;
382
		lks->node = xn;
383
384
		memset(&fwreq, 0, sizeof(fwreq));
385
		fwreq.id = lks->id;
386
		(void)strlcpy(fwreq.user, lk.userinfo.username, sizeof(fwreq.user));
387
		(void)strlcpy(fwreq.directory, lk.userinfo.directory, sizeof(fwreq.directory));
388
		fwreq.uid = lk.userinfo.uid;
389
		fwreq.gid = lk.userinfo.gid;
390
391
		m_compose(p_parent, IMSG_LKA_OPEN_FORWARD, 0, 0, -1,
392
		    &fwreq, sizeof(fwreq));
393
		lks->flags |= F_WAITING;
394
		break;
395
396
	case EXPAND_FILENAME:
397
		if (rule->r_forwardonly) {
398
			log_trace(TRACE_EXPAND, "expand: filename matched on forward-only rule");
399
			lks->error = LKA_TEMPFAIL;
400
			break;
401
		}
402
		log_trace(TRACE_EXPAND, "expand: lka_expand: filename: %s "
403
		    "[depth=%d]", xn->u.buffer, xn->depth);
404
		lka_submit(lks, rule, xn);
405
		break;
406
407
	case EXPAND_ERROR:
408
		if (rule->r_forwardonly) {
409
			log_trace(TRACE_EXPAND, "expand: error matched on forward-only rule");
410
			lks->error = LKA_TEMPFAIL;
411
			break;
412
		}
413
		log_trace(TRACE_EXPAND, "expand: lka_expand: error: %s "
414
		    "[depth=%d]", xn->u.buffer, xn->depth);
415
		if (xn->u.buffer[0] == '4')
416
			lks->error = LKA_TEMPFAIL;
417
		else if (xn->u.buffer[0] == '5')
418
			lks->error = LKA_PERMFAIL;
419
		lks->errormsg = xn->u.buffer;
420
		break;
421
422
	case EXPAND_FILTER:
423
		if (rule->r_forwardonly) {
424
			log_trace(TRACE_EXPAND, "expand: filter matched on forward-only rule");
425
			lks->error = LKA_TEMPFAIL;
426
			break;
427
		}
428
		log_trace(TRACE_EXPAND, "expand: lka_expand: filter: %s "
429
		    "[depth=%d]", xn->u.buffer, xn->depth);
430
		lka_submit(lks, rule, xn);
431
		break;
432
433
	case EXPAND_MAILDIR:
434
		log_trace(TRACE_EXPAND, "expand: lka_expand: maildir: %s "
435
		    "[depth=%d]", xn->u.buffer, xn->depth);
436
		r = table_lookup(rule->r_userbase, NULL,
437
		    xn->parent->u.user, K_USERINFO, &lk);
438
		if (r == -1) {
439
			log_trace(TRACE_EXPAND, "expand: lka_expand: maildir: "
440
			    "backend error while searching user");
441
			lks->error = LKA_TEMPFAIL;
442
			break;
443
		}
444
		if (r == 0) {
445
			log_trace(TRACE_EXPAND, "expand: lka_expand: maildir: "
446
			    "user-part does not match system user");
447
			lks->error = LKA_PERMFAIL;
448
			break;
449
		}
450
451
		lka_submit(lks, rule, xn);
452
		break;
453
	}
454
}
455
456
static struct expandnode *
457
lka_find_ancestor(struct expandnode *xn, enum expand_type type)
458
{
459
	while (xn && (xn->type != type))
460
		xn = xn->parent;
461
	if (xn == NULL) {
462
		log_warnx("warn: lka_find_ancestor: no ancestors of type %d",
463
		    type);
464
		fatalx(NULL);
465
	}
466
	return (xn);
467
}
468
469
static void
470
lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn)
471
{
472
	union lookup		 lk;
473
	struct envelope		*ep;
474
	struct expandnode	*xn2;
475
	int			 r;
476
477
	ep = xmemdup(&lks->envelope, sizeof *ep, "lka_submit");
478
	ep->expire = rule->r_qexpire;
479
480
	switch (rule->r_action) {
481
	case A_RELAY:
482
	case A_RELAYVIA:
483
		if (xn->type != EXPAND_ADDRESS)
484
			fatalx("lka_deliver: expect address");
485
		ep->type = D_MTA;
486
		ep->dest = xn->u.mailaddr;
487
		ep->agent.mta.relay = rule->r_value.relayhost;
488
489
		/* only rewrite if not a bounce */
490
		if (ep->sender.user[0] && rule->r_as && rule->r_as->user[0])
491
			(void)strlcpy(ep->sender.user, rule->r_as->user,
492
			    sizeof ep->sender.user);
493
		if (ep->sender.user[0] && rule->r_as && rule->r_as->domain[0])
494
			(void)strlcpy(ep->sender.domain, rule->r_as->domain,
495
			    sizeof ep->sender.domain);
496
		break;
497
	case A_NONE:
498
	case A_MBOX:
499
	case A_MAILDIR:
500
	case A_FILENAME:
501
	case A_MDA:
502
	case A_LMTP:
503
		ep->type = D_MDA;
504
		ep->dest = lka_find_ancestor(xn, EXPAND_ADDRESS)->u.mailaddr;
505
506
		/* set username */
507
		if ((xn->type == EXPAND_FILTER || xn->type == EXPAND_FILENAME)
508
		    && xn->alias) {
509
			(void)strlcpy(ep->agent.mda.username, SMTPD_USER,
510
			    sizeof(ep->agent.mda.username));
511
		}
512
		else {
513
			xn2 = lka_find_ancestor(xn, EXPAND_USERNAME);
514
			(void)strlcpy(ep->agent.mda.username, xn2->u.user,
515
			    sizeof(ep->agent.mda.username));
516
		}
517
518
		r = table_lookup(rule->r_userbase, NULL, ep->agent.mda.username,
519
		    K_USERINFO, &lk);
520
		if (r <= 0) {
521
			lks->error = (r == -1) ? LKA_TEMPFAIL : LKA_PERMFAIL;
522
			free(ep);
523
			return;
524
		}
525
		(void)strlcpy(ep->agent.mda.usertable, rule->r_userbase->t_name,
526
		    sizeof ep->agent.mda.usertable);
527
		(void)strlcpy(ep->agent.mda.username, lk.userinfo.username,
528
		    sizeof ep->agent.mda.username);
529
		strlcpy(ep->agent.mda.delivery_user, rule->r_delivery_user,
530
		    sizeof ep->agent.mda.delivery_user);
531
532
		if (xn->type == EXPAND_FILENAME) {
533
			ep->agent.mda.method = A_FILENAME;
534
			(void)strlcpy(ep->agent.mda.buffer, xn->u.buffer,
535
			    sizeof ep->agent.mda.buffer);
536
		}
537
		else if (xn->type == EXPAND_FILTER) {
538
			ep->agent.mda.method = A_MDA;
539
			(void)strlcpy(ep->agent.mda.buffer, xn->u.buffer,
540
			    sizeof ep->agent.mda.buffer);
541
		}
542
		else if (xn->type == EXPAND_USERNAME) {
543
			ep->agent.mda.method = rule->r_action;
544
			(void)strlcpy(ep->agent.mda.buffer, rule->r_value.buffer,
545
			    sizeof ep->agent.mda.buffer);
546
		}
547
		else if (xn->type == EXPAND_MAILDIR) {
548
			ep->agent.mda.method = A_MAILDIR;
549
			(void)strlcpy(ep->agent.mda.buffer, xn->u.buffer,
550
			    sizeof ep->agent.mda.buffer);
551
		}
552
		else
553
			fatalx("lka_deliver: bad node type");
554
555
		r = mda_expand_format(ep->agent.mda.buffer,
556
		    sizeof(ep->agent.mda.buffer), ep, &lk.userinfo);
557
		if (!r) {
558
			lks->error = LKA_TEMPFAIL;
559
			log_warnx("warn: format string error while"
560
			    " expanding for user %s", ep->agent.mda.username);
561
			free(ep);
562
			return;
563
		}
564
		break;
565
	default:
566
		fatalx("lka_submit: bad rule action");
567
	}
568
569
	TAILQ_INSERT_TAIL(&lks->deliverylist, ep, entry);
570
}