xref: /openbsd-src/usr.sbin/smtpd/lka.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: lka.c,v 1.133 2012/05/12 15:31:43 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
5  * Copyright (c) 2008 Gilles Chehade <gilles@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/param.h>
24 #include <sys/socket.h>
25 #include <sys/wait.h>
26 
27 #include <netinet/in.h>
28 
29 #include <ctype.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <event.h>
33 #include <imsg.h>
34 #include <pwd.h>
35 #include <resolv.h>
36 #include <signal.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 
42 #include "smtpd.h"
43 #include "log.h"
44 
45 struct rule *ruleset_match(struct envelope *);
46 static void lka_imsg(struct imsgev *, struct imsg *);
47 static void lka_shutdown(void);
48 static void lka_sig_handler(int, short, void *);
49 static int lka_verify_mail(struct mailaddr *);
50 static int lka_encode_credentials(char *, size_t, struct map_credentials *);
51 
52 void lka_session(struct submit_status *);
53 void lka_session_forward_reply(struct forward_req *, int);
54 
55 static void
56 lka_imsg(struct imsgev *iev, struct imsg *imsg)
57 {
58 	struct submit_status	*ss;
59 	struct secret		*secret;
60 	struct mapel		*mapel;
61 	struct rule		*rule;
62 	struct map		*map;
63 	void			*tmp;
64 
65 	log_imsg(PROC_LKA, iev->proc, imsg);
66 
67 	if (imsg->hdr.type == IMSG_DNS_HOST || imsg->hdr.type == IMSG_DNS_MX ||
68 	    imsg->hdr.type == IMSG_DNS_PTR) {
69 		dns_async(iev, imsg->hdr.type, imsg->data);
70 		return;
71 	}
72 
73 	if (iev->proc == PROC_MFA) {
74 		switch (imsg->hdr.type) {
75 		case IMSG_LKA_MAIL:
76 			ss = imsg->data;
77 			ss->code = 530;
78 			if (ss->u.maddr.user[0] == '\0' &&
79 			    ss->u.maddr.domain[0] == '\0')
80 				ss->code = 250;
81 			else
82 				if (lka_verify_mail(&ss->u.maddr))
83 					ss->code = 250;
84 			imsg_compose_event(iev, IMSG_LKA_MAIL, 0, 0, -1, ss,
85 			    sizeof *ss);
86 			return;
87 
88 		case IMSG_LKA_RULEMATCH:
89 			ss = imsg->data;
90 			ss->code = 530;
91 			rule = ruleset_match(&ss->envelope);
92 			if (rule) {
93 				ss->code = 250;
94 				ss->envelope.rule = *rule;
95 				if (IS_RELAY(*rule))
96 					ss->envelope.type = D_MTA;
97 				else
98 					ss->envelope.type = D_MDA;
99 			}
100 			imsg_compose_event(iev, IMSG_LKA_RULEMATCH, 0, 0, -1,
101 			    ss, sizeof *ss);
102 			return;
103 
104 		case IMSG_LKA_RCPT:
105 			lka_session(imsg->data);
106 			return;
107 		}
108 	}
109 
110 	if (iev->proc == PROC_MTA) {
111 		switch (imsg->hdr.type) {
112 		case IMSG_LKA_SECRET: {
113 			struct map_credentials *map_credentials;
114 
115 			secret = imsg->data;
116 			map = map_findbyname(secret->mapname);
117 			if (map == NULL) {
118 				log_warn("lka: credentials map %s is missing",
119 				    secret->mapname);
120 				imsg_compose_event(iev, IMSG_LKA_SECRET, 0, 0,
121 				    -1, secret, sizeof *secret);
122 				return;
123 			}
124 			map_credentials = map_lookup(map->m_id, secret->host,
125 			    K_CREDENTIALS);
126 			log_debug("lka: %s credentials lookup (%d)", secret->host,
127 			    map_credentials != NULL);
128 			secret->secret[0] = '\0';
129 			if (map_credentials == NULL)
130 				log_warnx("%s credentials not found",
131 				    secret->host);
132 			else if (lka_encode_credentials(secret->secret,
133 				     sizeof secret->secret, map_credentials) == 0)
134 				log_warnx("%s credentials parse fail",
135 				    secret->host);
136 			imsg_compose_event(iev, IMSG_LKA_SECRET, 0, 0, -1, secret,
137 			    sizeof *secret);
138 			free(map_credentials);
139 			return;
140 		}
141 		}
142 	}
143 
144 	if (iev->proc == PROC_PARENT) {
145 		switch (imsg->hdr.type) {
146 		case IMSG_CONF_START:
147 			env->sc_rules_reload = calloc(1, sizeof *env->sc_rules);
148 			if (env->sc_rules_reload == NULL)
149 				fatal(NULL);
150 			env->sc_maps_reload = calloc(1, sizeof *env->sc_maps);
151 			if (env->sc_maps_reload == NULL)
152 				fatal(NULL);
153 			TAILQ_INIT(env->sc_rules_reload);
154 			TAILQ_INIT(env->sc_maps_reload);
155 			return;
156 
157 		case IMSG_CONF_RULE:
158 			rule = calloc(1, sizeof *rule);
159 			if (rule == NULL)
160 				fatal(NULL);
161 			*rule = *(struct rule *)imsg->data;
162 			TAILQ_INSERT_TAIL(env->sc_rules_reload, rule, r_entry);
163 			return;
164 
165 		case IMSG_CONF_MAP:
166 			map = calloc(1, sizeof *map);
167 			if (map == NULL)
168 				fatal(NULL);
169 			*map = *(struct map *)imsg->data;
170 			TAILQ_INIT(&map->m_contents);
171 			TAILQ_INSERT_TAIL(env->sc_maps_reload, map, m_entry);
172 			return;
173 
174 		case IMSG_CONF_RULE_SOURCE:
175 			rule = TAILQ_LAST(env->sc_rules_reload, rulelist);
176 			tmp = env->sc_maps;
177 			env->sc_maps = env->sc_maps_reload;
178 			rule->r_sources = map_findbyname(imsg->data);
179 			if (rule->r_sources == NULL)
180 				fatalx("lka: maps inconsistency");
181 			env->sc_maps = tmp;
182 			return;
183 
184 		case IMSG_CONF_MAP_CONTENT:
185 			map = TAILQ_LAST(env->sc_maps_reload, maplist);
186 			mapel = calloc(1, sizeof *mapel);
187 			if (mapel == NULL)
188 				fatal(NULL);
189 			*mapel = *(struct mapel *)imsg->data;
190 			TAILQ_INSERT_TAIL(&map->m_contents, mapel, me_entry);
191 			return;
192 
193 		case IMSG_CONF_END:
194 			if (env->sc_rules)
195 				purge_config(PURGE_RULES);
196 			if (env->sc_maps)
197 				purge_config(PURGE_MAPS);
198 			env->sc_rules = env->sc_rules_reload;
199 			env->sc_maps = env->sc_maps_reload;
200 			return;
201 
202 		case IMSG_CTL_VERBOSE:
203 			log_verbose(*(int *)imsg->data);
204 			return;
205 
206 		case IMSG_PARENT_FORWARD_OPEN:
207 			lka_session_forward_reply(imsg->data, imsg->fd);
208 			return;
209 
210 		}
211 	}
212 
213 	errx(1, "lka_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
214 }
215 
216 static void
217 lka_sig_handler(int sig, short event, void *p)
218 {
219 	int status;
220 	pid_t pid;
221 
222 	switch (sig) {
223 	case SIGINT:
224 	case SIGTERM:
225 		lka_shutdown();
226 		break;
227 	case SIGCHLD:
228 		do {
229 			pid = waitpid(-1, &status, WNOHANG);
230 		} while (pid > 0 || (pid == -1 && errno == EINTR));
231 		break;
232 	default:
233 		fatalx("lka_sig_handler: unexpected signal");
234 	}
235 }
236 
237 void
238 lka_shutdown(void)
239 {
240 	log_info("lookup agent exiting");
241 	_exit(0);
242 }
243 
244 pid_t
245 lka(void)
246 {
247 	pid_t		 pid;
248 	struct passwd	*pw;
249 
250 	struct event	 ev_sigint;
251 	struct event	 ev_sigterm;
252 	struct event	 ev_sigchld;
253 
254 	struct peer peers[] = {
255 		{ PROC_PARENT,	imsg_dispatch },
256 		{ PROC_MFA,	imsg_dispatch },
257 		{ PROC_QUEUE,	imsg_dispatch },
258 		{ PROC_SMTP,	imsg_dispatch },
259 		{ PROC_MTA,	imsg_dispatch }
260 	};
261 
262 	switch (pid = fork()) {
263 	case -1:
264 		fatal("lka: cannot fork");
265 	case 0:
266 		break;
267 	default:
268 		return (pid);
269 	}
270 
271 	purge_config(PURGE_EVERYTHING);
272 
273 	pw = env->sc_pw;
274 
275 	smtpd_process = PROC_LKA;
276 	setproctitle("%s", env->sc_title[smtpd_process]);
277 
278 	if (setgroups(1, &pw->pw_gid) ||
279 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
280 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
281 		fatal("lka: cannot drop privileges");
282 
283 	imsg_callback = lka_imsg;
284 	event_init();
285 	SPLAY_INIT(&env->lka_sessions);
286 
287 	signal_set(&ev_sigint, SIGINT, lka_sig_handler, NULL);
288 	signal_set(&ev_sigterm, SIGTERM, lka_sig_handler, NULL);
289 	signal_set(&ev_sigchld, SIGCHLD, lka_sig_handler, NULL);
290 	signal_add(&ev_sigint, NULL);
291 	signal_add(&ev_sigterm, NULL);
292 	signal_add(&ev_sigchld, NULL);
293 	signal(SIGPIPE, SIG_IGN);
294 	signal(SIGHUP, SIG_IGN);
295 
296 	/*
297 	 * lka opens all kinds of files and sockets, so bump the limit to max.
298 	 * XXX: need to analyse the exact hard limit.
299 	 */
300 	fdlimit(1.0);
301 
302 	config_pipes(peers, nitems(peers));
303 	config_peers(peers, nitems(peers));
304 
305 	if (event_dispatch() < 0)
306 		fatal("event_dispatch");
307 	lka_shutdown();
308 
309 	return (0);
310 }
311 
312 int
313 lka_verify_mail(struct mailaddr *maddr)
314 {
315 	return 1;
316 }
317 
318 static int
319 lka_encode_credentials(char *dst, size_t size,
320     struct map_credentials *map_credentials)
321 {
322 	char	*buf;
323 	int	 buflen;
324 
325 	if ((buflen = asprintf(&buf, "%c%s%c%s", '\0', map_credentials->username,
326 		    '\0', map_credentials->password)) == -1)
327 		fatal(NULL);
328 
329 	if (__b64_ntop((unsigned char *)buf, buflen, dst, size) == -1) {
330 		free(buf);
331 		return 0;
332 	}
333 
334 	free(buf);
335 	return 1;
336 }
337