xref: /openbsd-src/usr.sbin/smtpd/mproc.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: mproc.c,v 1.35 2019/10/03 05:50:28 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2012 Eric Faurot <eric@faurot.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/tree.h>
22 #include <sys/queue.h>
23 #include <sys/uio.h>
24 
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <arpa/nameser.h>
28 
29 #include <err.h>
30 #include <errno.h>
31 #include <event.h>
32 #include <imsg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <limits.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include "smtpd.h"
40 #include "log.h"
41 
42 static void mproc_dispatch(int, short, void *);
43 
44 static ssize_t imsg_read_nofd(struct imsgbuf *);
45 
46 int
47 mproc_fork(struct mproc *p, const char *path, char *argv[])
48 {
49 	int sp[2];
50 
51 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1)
52 		return (-1);
53 
54 	io_set_nonblocking(sp[0]);
55 	io_set_nonblocking(sp[1]);
56 
57 	if ((p->pid = fork()) == -1)
58 		goto err;
59 
60 	if (p->pid == 0) {
61 		/* child process */
62 		dup2(sp[0], STDIN_FILENO);
63 		if (closefrom(STDERR_FILENO + 1) == -1)
64 			exit(1);
65 
66 		execv(path, argv);
67 		err(1, "execv: %s", path);
68 	}
69 
70 	/* parent process */
71 	close(sp[0]);
72 	mproc_init(p, sp[1]);
73 	return (0);
74 
75 err:
76 	log_warn("warn: Failed to start process %s, instance of %s", argv[0], path);
77 	close(sp[0]);
78 	close(sp[1]);
79 	return (-1);
80 }
81 
82 void
83 mproc_init(struct mproc *p, int fd)
84 {
85 	imsg_init(&p->imsgbuf, fd);
86 }
87 
88 void
89 mproc_clear(struct mproc *p)
90 {
91 	log_debug("debug: clearing p=%s, fd=%d, pid=%d", p->name, p->imsgbuf.fd, p->pid);
92 
93 	event_del(&p->ev);
94 	close(p->imsgbuf.fd);
95 	imsg_clear(&p->imsgbuf);
96 }
97 
98 void
99 mproc_enable(struct mproc *p)
100 {
101 	if (p->enable == 0) {
102 		log_trace(TRACE_MPROC, "mproc: %s -> %s: enabled",
103 		    proc_name(smtpd_process),
104 		    proc_name(p->proc));
105 		p->enable = 1;
106 	}
107 	mproc_event_add(p);
108 }
109 
110 void
111 mproc_disable(struct mproc *p)
112 {
113 	if (p->enable == 1) {
114 		log_trace(TRACE_MPROC, "mproc: %s -> %s: disabled",
115 		    proc_name(smtpd_process),
116 		    proc_name(p->proc));
117 		p->enable = 0;
118 	}
119 	mproc_event_add(p);
120 }
121 
122 void
123 mproc_event_add(struct mproc *p)
124 {
125 	short	events;
126 
127 	if (p->enable)
128 		events = EV_READ;
129 	else
130 		events = 0;
131 
132 	if (p->imsgbuf.w.queued)
133 		events |= EV_WRITE;
134 
135 	if (p->events)
136 		event_del(&p->ev);
137 
138 	p->events = events;
139 	if (events) {
140 		event_set(&p->ev, p->imsgbuf.fd, events, mproc_dispatch, p);
141 		event_add(&p->ev, NULL);
142 	}
143 }
144 
145 static void
146 mproc_dispatch(int fd, short event, void *arg)
147 {
148 	struct mproc	*p = arg;
149 	struct imsg	 imsg;
150 	ssize_t		 n;
151 
152 	p->events = 0;
153 
154 	if (event & EV_READ) {
155 
156 		if (p->proc == PROC_CLIENT)
157 			n = imsg_read_nofd(&p->imsgbuf);
158 		else
159 			n = imsg_read(&p->imsgbuf);
160 
161 		switch (n) {
162 		case -1:
163 			if (errno == EAGAIN)
164 				break;
165 			log_warn("warn: %s -> %s: imsg_read",
166 			    proc_name(smtpd_process),  p->name);
167 			fatal("exiting");
168 			/* NOTREACHED */
169 		case 0:
170 			/* this pipe is dead, so remove the event handler */
171 			log_debug("debug: %s -> %s: pipe closed",
172 			    proc_name(smtpd_process),  p->name);
173 			p->handler(p, NULL);
174 			return;
175 		default:
176 			break;
177 		}
178 	}
179 
180 	if (event & EV_WRITE) {
181 		n = msgbuf_write(&p->imsgbuf.w);
182 		if (n == 0 || (n == -1 && errno != EAGAIN)) {
183 			/* this pipe is dead, so remove the event handler */
184 			log_debug("debug: %s -> %s: pipe closed",
185 			    proc_name(smtpd_process),  p->name);
186 			p->handler(p, NULL);
187 			return;
188 		}
189 	}
190 
191 	for (;;) {
192 		if ((n = imsg_get(&p->imsgbuf, &imsg)) == -1) {
193 
194 			if (smtpd_process == PROC_CONTROL &&
195 			    p->proc == PROC_CLIENT) {
196 				log_warnx("warn: client sent invalid imsg "
197 				    "over control socket");
198 				p->handler(p, NULL);
199 				return;
200 			}
201 			log_warn("fatal: %s: error in imsg_get for %s",
202 			    proc_name(smtpd_process),  p->name);
203 			fatalx(NULL);
204 		}
205 		if (n == 0)
206 			break;
207 
208 		p->handler(p, &imsg);
209 
210 		imsg_free(&imsg);
211 	}
212 
213 	mproc_event_add(p);
214 }
215 
216 /* This should go into libutil */
217 static ssize_t
218 imsg_read_nofd(struct imsgbuf *ibuf)
219 {
220 	ssize_t	 n;
221 	char	*buf;
222 	size_t	 len;
223 
224 	buf = ibuf->r.buf + ibuf->r.wpos;
225 	len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
226 
227 	while ((n = recv(ibuf->fd, buf, len, 0)) == -1) {
228 		if (errno != EINTR)
229 			return (n);
230 	}
231 
232 	ibuf->r.wpos += n;
233 	return (n);
234 }
235 
236 void
237 m_forward(struct mproc *p, struct imsg *imsg)
238 {
239 	imsg_compose(&p->imsgbuf, imsg->hdr.type, imsg->hdr.peerid,
240 	    imsg->hdr.pid, imsg->fd, imsg->data,
241 	    imsg->hdr.len - sizeof(imsg->hdr));
242 
243 	if (imsg->hdr.type != IMSG_STAT_DECREMENT &&
244 	    imsg->hdr.type != IMSG_STAT_INCREMENT)
245 		log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (forward)",
246 		    proc_name(smtpd_process),
247 		    proc_name(p->proc),
248 		    imsg->hdr.len - sizeof(imsg->hdr),
249 		    imsg_to_str(imsg->hdr.type));
250 
251 	mproc_event_add(p);
252 }
253 
254 void
255 m_compose(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd,
256     void *data, size_t len)
257 {
258 	imsg_compose(&p->imsgbuf, type, peerid, pid, fd, data, len);
259 
260 	if (type != IMSG_STAT_DECREMENT &&
261 	    type != IMSG_STAT_INCREMENT)
262 		log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s",
263 		    proc_name(smtpd_process),
264 		    proc_name(p->proc),
265 		    len,
266 		    imsg_to_str(type));
267 
268 	mproc_event_add(p);
269 }
270 
271 void
272 m_composev(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid,
273     int fd, const struct iovec *iov, int n)
274 {
275 	size_t	len;
276 	int	i;
277 
278 	imsg_composev(&p->imsgbuf, type, peerid, pid, fd, iov, n);
279 
280 	len = 0;
281 	for (i = 0; i < n; i++)
282 		len += iov[i].iov_len;
283 
284 	if (type != IMSG_STAT_DECREMENT &&
285 	    type != IMSG_STAT_INCREMENT)
286 		log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s",
287 		    proc_name(smtpd_process),
288 		    proc_name(p->proc),
289 		    len,
290 		    imsg_to_str(type));
291 
292 	mproc_event_add(p);
293 }
294 
295 void
296 m_create(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd)
297 {
298 	p->m_pos = 0;
299 	p->m_type = type;
300 	p->m_peerid = peerid;
301 	p->m_pid = pid;
302 	p->m_fd = fd;
303 }
304 
305 void
306 m_add(struct mproc *p, const void *data, size_t len)
307 {
308 	size_t	 alloc;
309 	void	*tmp;
310 
311 	if (p->m_pos + len + IMSG_HEADER_SIZE > MAX_IMSGSIZE) {
312 		log_warnx("warn: message too large");
313 		fatal(NULL);
314 	}
315 
316 	alloc = p->m_alloc ? p->m_alloc : 128;
317 	while (p->m_pos + len > alloc)
318 		alloc *= 2;
319 	if (alloc != p->m_alloc) {
320 		log_trace(TRACE_MPROC, "mproc: %s -> %s: realloc %zu -> %zu",
321 		    proc_name(smtpd_process),
322 		    proc_name(p->proc),
323 		    p->m_alloc,
324 		    alloc);
325 
326 		tmp = recallocarray(p->m_buf, p->m_alloc, alloc, 1);
327 		if (tmp == NULL)
328 			fatal("realloc");
329 		p->m_alloc = alloc;
330 		p->m_buf = tmp;
331 	}
332 
333 	memmove(p->m_buf + p->m_pos, data, len);
334 	p->m_pos += len;
335 }
336 
337 void
338 m_close(struct mproc *p)
339 {
340 	if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd,
341 	    p->m_buf, p->m_pos) == -1)
342 		fatal("imsg_compose");
343 
344 	log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s",
345 		    proc_name(smtpd_process),
346 		    proc_name(p->proc),
347 		    p->m_pos,
348 		    imsg_to_str(p->m_type));
349 
350 	mproc_event_add(p);
351 }
352 
353 void
354 m_flush(struct mproc *p)
355 {
356 	if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd,
357 	    p->m_buf, p->m_pos) == -1)
358 		fatal("imsg_compose");
359 
360 	log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (flush)",
361 	    proc_name(smtpd_process),
362 	    proc_name(p->proc),
363 	    p->m_pos,
364 	    imsg_to_str(p->m_type));
365 
366 	p->m_pos = 0;
367 
368 	if (imsg_flush(&p->imsgbuf) == -1)
369 		fatal("imsg_flush");
370 }
371 
372 static struct imsg * current;
373 
374 static void
375 m_error(const char *error)
376 {
377 	char	buf[512];
378 
379 	(void)snprintf(buf, sizeof buf, "%s: %s: %s",
380 	    proc_name(smtpd_process),
381 	    imsg_to_str(current->hdr.type),
382 	    error);
383 	fatalx("%s", buf);
384 }
385 
386 void
387 m_msg(struct msg *m, struct imsg *imsg)
388 {
389 	current = imsg;
390 	m->pos = imsg->data;
391 	m->end = m->pos + (imsg->hdr.len - sizeof(imsg->hdr));
392 }
393 
394 void
395 m_end(struct msg *m)
396 {
397 	if (m->pos != m->end)
398 		m_error("not at msg end");
399 }
400 
401 int
402 m_is_eom(struct msg *m)
403 {
404 	return (m->pos == m->end);
405 }
406 
407 static inline void
408 m_get(struct msg *m, void *dst, size_t sz)
409 {
410 	if (sz > MAX_IMSGSIZE ||
411 	    m->end - m->pos < (ssize_t)sz)
412 		fatalx("msg too short");
413 
414 	memmove(dst, m->pos, sz);
415 	m->pos += sz;
416 }
417 
418 void
419 m_add_int(struct mproc *m, int v)
420 {
421 	m_add(m, &v, sizeof(v));
422 };
423 
424 void
425 m_add_u32(struct mproc *m, uint32_t u32)
426 {
427 	m_add(m, &u32, sizeof(u32));
428 };
429 
430 void
431 m_add_size(struct mproc *m, size_t sz)
432 {
433 	m_add(m, &sz, sizeof(sz));
434 };
435 
436 void
437 m_add_time(struct mproc *m, time_t v)
438 {
439 	m_add(m, &v, sizeof(v));
440 };
441 
442 void
443 m_add_timeval(struct mproc *m, struct timeval *tv)
444 {
445 	m_add(m, tv, sizeof(*tv));
446 }
447 
448 
449 void
450 m_add_string(struct mproc *m, const char *v)
451 {
452 	if (v) {
453 		m_add(m, "s", 1);
454 		m_add(m, v, strlen(v) + 1);
455 	}
456 	else
457 		m_add(m, "\0", 1);
458 };
459 
460 void
461 m_add_data(struct mproc *m, const void *v, size_t len)
462 {
463 	m_add_size(m, len);
464 	m_add(m, v, len);
465 };
466 
467 void
468 m_add_id(struct mproc *m, uint64_t v)
469 {
470 	m_add(m, &v, sizeof(v));
471 }
472 
473 void
474 m_add_evpid(struct mproc *m, uint64_t v)
475 {
476 	m_add(m, &v, sizeof(v));
477 }
478 
479 void
480 m_add_msgid(struct mproc *m, uint32_t v)
481 {
482 	m_add(m, &v, sizeof(v));
483 }
484 
485 void
486 m_add_sockaddr(struct mproc *m, const struct sockaddr *sa)
487 {
488 	m_add_size(m, sa->sa_len);
489 	m_add(m, sa, sa->sa_len);
490 }
491 
492 void
493 m_add_mailaddr(struct mproc *m, const struct mailaddr *maddr)
494 {
495 	m_add(m, maddr, sizeof(*maddr));
496 }
497 
498 void
499 m_add_envelope(struct mproc *m, const struct envelope *evp)
500 {
501 	char	buf[sizeof(*evp)];
502 
503 	envelope_dump_buffer(evp, buf, sizeof(buf));
504 	m_add_evpid(m, evp->id);
505 	m_add_string(m, buf);
506 }
507 
508 void
509 m_add_params(struct mproc *m, struct dict *d)
510 {
511 	const char *key;
512 	char *value;
513 	void *iter;
514 
515 	if (d == NULL) {
516 		m_add_size(m, 0);
517 		return;
518 	}
519 	m_add_size(m, dict_count(d));
520 	iter = NULL;
521 	while (dict_iter(d, &iter, &key, (void **)&value)) {
522 		m_add_string(m, key);
523 		m_add_string(m, value);
524 	}
525 }
526 
527 void
528 m_get_int(struct msg *m, int *i)
529 {
530 	m_get(m, i, sizeof(*i));
531 }
532 
533 void
534 m_get_u32(struct msg *m, uint32_t *u32)
535 {
536 	m_get(m, u32, sizeof(*u32));
537 }
538 
539 void
540 m_get_size(struct msg *m, size_t *sz)
541 {
542 	m_get(m, sz, sizeof(*sz));
543 }
544 
545 void
546 m_get_time(struct msg *m, time_t *t)
547 {
548 	m_get(m, t, sizeof(*t));
549 }
550 
551 void
552 m_get_timeval(struct msg *m, struct timeval *tv)
553 {
554 	m_get(m, tv, sizeof(*tv));
555 }
556 
557 void
558 m_get_string(struct msg *m, const char **s)
559 {
560 	uint8_t	*end;
561 	char c;
562 
563 	if (m->pos >= m->end)
564 		m_error("msg too short");
565 
566 	c = *m->pos++;
567 	if (c == '\0') {
568 		*s = NULL;
569 		return;
570 	}
571 
572 	if (m->pos >= m->end)
573 		m_error("msg too short");
574 	end = memchr(m->pos, 0, m->end - m->pos);
575 	if (end == NULL)
576 		m_error("unterminated string");
577 
578 	*s = m->pos;
579 	m->pos = end + 1;
580 }
581 
582 void
583 m_get_data(struct msg *m, const void **data, size_t *sz)
584 {
585 	m_get_size(m, sz);
586 
587 	if (*sz == 0) {
588 		*data = NULL;
589 		return;
590 	}
591 
592 	if (m->pos + *sz > m->end)
593 		m_error("msg too short");
594 
595 	*data = m->pos;
596 	m->pos += *sz;
597 }
598 
599 void
600 m_get_evpid(struct msg *m, uint64_t *evpid)
601 {
602 	m_get(m, evpid, sizeof(*evpid));
603 }
604 
605 void
606 m_get_msgid(struct msg *m, uint32_t *msgid)
607 {
608 	m_get(m, msgid, sizeof(*msgid));
609 }
610 
611 void
612 m_get_id(struct msg *m, uint64_t *id)
613 {
614 	m_get(m, id, sizeof(*id));
615 }
616 
617 void
618 m_get_sockaddr(struct msg *m, struct sockaddr *sa)
619 {
620 	size_t len;
621 
622 	m_get_size(m, &len);
623 	m_get(m, sa, len);
624 }
625 
626 void
627 m_get_mailaddr(struct msg *m, struct mailaddr *maddr)
628 {
629 	m_get(m, maddr, sizeof(*maddr));
630 }
631 
632 void
633 m_get_envelope(struct msg *m, struct envelope *evp)
634 {
635 	uint64_t	 evpid;
636 	const char	*buf;
637 
638 	m_get_evpid(m, &evpid);
639 	m_get_string(m, &buf);
640 
641 	if (!envelope_load_buffer(evp, buf, strlen(buf)))
642 		fatalx("failed to retrieve envelope");
643 	evp->id = evpid;
644 }
645 
646 void
647 m_get_params(struct msg *m, struct dict *d)
648 {
649 	size_t	c;
650 	const char *key;
651 	const char *value;
652 	char *tmp;
653 
654 	dict_init(d);
655 
656 	m_get_size(m, &c);
657 
658 	for (; c; c--) {
659 		m_get_string(m, &key);
660 		m_get_string(m, &value);
661 		if ((tmp = strdup(value)) == NULL)
662 			fatal("m_get_params");
663 		dict_set(d, key, tmp);
664 	}
665 }
666 
667 void
668 m_clear_params(struct dict *d)
669 {
670 	char *value;
671 
672 	while (dict_poproot(d, (void **)&value))
673 		free(value);
674 }
675