xref: /openbsd-src/usr.sbin/smtpd/lka.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: lka.c,v 1.172 2014/07/10 15:54:55 eric Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
5  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
6  * Copyright (c) 2012 Eric Faurot <eric@faurot.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/socket.h>
25 #include <sys/wait.h>
26 #include <sys/uio.h>
27 
28 #include <netinet/in.h>
29 
30 #include <ctype.h>
31 #include <err.h>
32 #include <errno.h>
33 #include <event.h>
34 #include <imsg.h>
35 #include <openssl/err.h>
36 #include <openssl/ssl.h>
37 #include <pwd.h>
38 #include <resolv.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include "smtpd.h"
46 #include "log.h"
47 #include "ssl.h"
48 
49 static void lka_imsg(struct mproc *, struct imsg *);
50 static void lka_shutdown(void);
51 static void lka_sig_handler(int, short, void *);
52 static int lka_authenticate(const char *, const char *, const char *);
53 static int lka_credentials(const char *, const char *, char *, size_t);
54 static int lka_userinfo(const char *, const char *, struct userinfo *);
55 static int lka_addrname(const char *, const struct sockaddr *,
56     struct addrname *);
57 static int lka_X509_verify(struct ca_vrfy_req_msg *, const char *, const char *);
58 
59 static void
60 lka_imsg(struct mproc *p, struct imsg *imsg)
61 {
62 	struct table		*table;
63 	int			 ret;
64 	struct pki		*pki;
65 	struct iovec		iov[2];
66 	static struct ca_vrfy_req_msg	*req_ca_vrfy_smtp = NULL;
67 	static struct ca_vrfy_req_msg	*req_ca_vrfy_mta = NULL;
68 	struct ca_vrfy_req_msg		*req_ca_vrfy_chain;
69 	struct ca_vrfy_resp_msg		resp_ca_vrfy;
70 	struct ca_cert_req_msg		*req_ca_cert;
71 	struct ca_cert_resp_msg		 resp_ca_cert;
72 	struct sockaddr_storage	 ss;
73 	struct userinfo		 userinfo;
74 	struct addrname		 addrname;
75 	struct envelope		 evp;
76 	struct msg		 m;
77 	union lookup		 lk;
78 	char			 buf[SMTPD_MAXLINESIZE];
79 	const char		*tablename, *username, *password, *label;
80 	uint64_t		 reqid;
81 	size_t			 i;
82 	int			 v;
83 	const char	        *cafile = NULL;
84 
85 	if (imsg->hdr.type == IMSG_MTA_DNS_HOST ||
86 	    imsg->hdr.type == IMSG_MTA_DNS_PTR ||
87 	    imsg->hdr.type == IMSG_SMTP_DNS_PTR ||
88 	    imsg->hdr.type == IMSG_MTA_DNS_MX ||
89 	    imsg->hdr.type == IMSG_MTA_DNS_MX_PREFERENCE) {
90 		dns_imsg(p, imsg);
91 		return;
92 	}
93 
94 	if (p->proc == PROC_PONY) {
95 		switch (imsg->hdr.type) {
96 		case IMSG_SMTP_EXPAND_RCPT:
97 			m_msg(&m, imsg);
98 			m_get_id(&m, &reqid);
99 			m_get_envelope(&m, &evp);
100 			m_end(&m);
101 			lka_session(reqid, &evp);
102 			return;
103 
104 		case IMSG_SMTP_LOOKUP_HELO:
105 			m_msg(&m, imsg);
106 			m_get_id(&m, &reqid);
107 			m_get_string(&m, &tablename);
108 			m_get_sockaddr(&m, (struct sockaddr *)&ss);
109 			m_end(&m);
110 
111 			ret = lka_addrname(tablename, (struct sockaddr*)&ss,
112 			    &addrname);
113 
114 			m_create(p, IMSG_SMTP_LOOKUP_HELO, 0, 0, -1);
115 			m_add_id(p, reqid);
116 			m_add_int(p, ret);
117 			if (ret == LKA_OK)
118 				m_add_string(p, addrname.name);
119 			m_close(p);
120 			return;
121 
122 		case IMSG_SMTP_SSL_INIT:
123 			req_ca_cert = imsg->data;
124 			resp_ca_cert.reqid = req_ca_cert->reqid;
125 
126 			xlowercase(buf, req_ca_cert->name, sizeof(buf));
127 			log_debug("debug: lka: looking up pki \"%s\"", buf);
128 			pki = dict_get(env->sc_pki_dict, buf);
129 			if (pki == NULL) {
130 				resp_ca_cert.status = CA_FAIL;
131 				m_compose(p, IMSG_SMTP_SSL_INIT, 0, 0, -1, &resp_ca_cert,
132 				    sizeof(resp_ca_cert));
133 				return;
134 			}
135 			resp_ca_cert.status = CA_OK;
136 			resp_ca_cert.cert_len = pki->pki_cert_len;
137 			iov[0].iov_base = &resp_ca_cert;
138 			iov[0].iov_len = sizeof(resp_ca_cert);
139 			iov[1].iov_base = pki->pki_cert;
140 			iov[1].iov_len = pki->pki_cert_len;
141 			m_composev(p, IMSG_SMTP_SSL_INIT, 0, 0, -1, iov, nitems(iov));
142 			return;
143 
144 		case IMSG_SMTP_SSL_VERIFY_CERT:
145 			req_ca_vrfy_smtp = xmemdup(imsg->data, sizeof *req_ca_vrfy_smtp, "lka:ca_vrfy");
146 			req_ca_vrfy_smtp->cert = xmemdup((char *)imsg->data +
147 			    sizeof *req_ca_vrfy_smtp, req_ca_vrfy_smtp->cert_len, "lka:ca_vrfy");
148 			req_ca_vrfy_smtp->chain_cert = xcalloc(req_ca_vrfy_smtp->n_chain,
149 			    sizeof (unsigned char *), "lka:ca_vrfy");
150 			req_ca_vrfy_smtp->chain_cert_len = xcalloc(req_ca_vrfy_smtp->n_chain,
151 			    sizeof (off_t), "lka:ca_vrfy");
152 			return;
153 
154 		case IMSG_SMTP_SSL_VERIFY_CHAIN:
155 			if (req_ca_vrfy_smtp == NULL)
156 				fatalx("lka:ca_vrfy: chain without a certificate");
157 			req_ca_vrfy_chain = imsg->data;
158 			req_ca_vrfy_smtp->chain_cert[req_ca_vrfy_smtp->chain_offset] = xmemdup((char *)imsg->data +
159 			    sizeof *req_ca_vrfy_chain, req_ca_vrfy_chain->cert_len, "lka:ca_vrfy");
160 			req_ca_vrfy_smtp->chain_cert_len[req_ca_vrfy_smtp->chain_offset] = req_ca_vrfy_chain->cert_len;
161 			req_ca_vrfy_smtp->chain_offset++;
162 			return;
163 
164 		case IMSG_SMTP_SSL_VERIFY:
165 			if (req_ca_vrfy_smtp == NULL)
166 				fatalx("lka:ca_vrfy: verify without a certificate");
167 
168 			resp_ca_vrfy.reqid = req_ca_vrfy_smtp->reqid;
169 			pki = dict_xget(env->sc_pki_dict, req_ca_vrfy_smtp->pkiname);
170 			cafile = CA_FILE;
171 			if (pki->pki_ca_file)
172 				cafile = pki->pki_ca_file;
173 			if (! lka_X509_verify(req_ca_vrfy_smtp, cafile, NULL))
174 				resp_ca_vrfy.status = CA_FAIL;
175 			else
176 				resp_ca_vrfy.status = CA_OK;
177 
178 			m_compose(p, IMSG_SMTP_SSL_VERIFY, 0, 0, -1, &resp_ca_vrfy,
179 			    sizeof resp_ca_vrfy);
180 
181 			for (i = 0; i < req_ca_vrfy_smtp->n_chain; ++i)
182 				free(req_ca_vrfy_smtp->chain_cert[i]);
183 			free(req_ca_vrfy_smtp->chain_cert);
184 			free(req_ca_vrfy_smtp->chain_cert_len);
185 			free(req_ca_vrfy_smtp->cert);
186 			free(req_ca_vrfy_smtp);
187 			return;
188 
189 		case IMSG_SMTP_AUTHENTICATE:
190 			m_msg(&m, imsg);
191 			m_get_id(&m, &reqid);
192 			m_get_string(&m, &tablename);
193 			m_get_string(&m, &username);
194 			m_get_string(&m, &password);
195 			m_end(&m);
196 
197 			if (!tablename[0]) {
198 				m_create(p_parent, IMSG_LKA_AUTHENTICATE,
199 				    0, 0, -1);
200 				m_add_id(p_parent, reqid);
201 				m_add_string(p_parent, username);
202 				m_add_string(p_parent, password);
203 				m_close(p_parent);
204 				return;
205 			}
206 
207 			ret = lka_authenticate(tablename, username, password);
208 
209 			m_create(p, IMSG_SMTP_AUTHENTICATE, 0, 0, -1);
210 			m_add_id(p, reqid);
211 			m_add_int(p, ret);
212 			m_close(p);
213 			return;
214 		}
215 	}
216 
217 	if (p->proc == PROC_PONY) {
218 		switch (imsg->hdr.type) {
219 		case IMSG_MDA_LOOKUP_USERINFO:
220 			m_msg(&m, imsg);
221 			m_get_id(&m, &reqid);
222 			m_get_string(&m, &tablename);
223 			m_get_string(&m, &username);
224 			m_end(&m);
225 
226 			ret = lka_userinfo(tablename, username, &userinfo);
227 
228 			m_create(p, IMSG_MDA_LOOKUP_USERINFO, 0, 0, -1);
229 			m_add_id(p, reqid);
230 			m_add_int(p, ret);
231 			if (ret == LKA_OK)
232 				m_add_data(p, &userinfo, sizeof(userinfo));
233 			m_close(p);
234 			return;
235 		}
236 	}
237 
238 	if (p->proc == PROC_PONY) {
239 		switch (imsg->hdr.type) {
240 
241 		case IMSG_MTA_SSL_INIT:
242 			req_ca_cert = imsg->data;
243 			resp_ca_cert.reqid = req_ca_cert->reqid;
244 
245 			xlowercase(buf, req_ca_cert->name, sizeof(buf));
246 			log_debug("debug: lka: looking up pki \"%s\"", buf);
247 			pki = dict_get(env->sc_pki_dict, buf);
248 			if (pki == NULL) {
249 				resp_ca_cert.status = CA_FAIL;
250 				m_compose(p, IMSG_MTA_SSL_INIT, 0, 0, -1, &resp_ca_cert,
251 				    sizeof(resp_ca_cert));
252 				return;
253 			}
254 			resp_ca_cert.status = CA_OK;
255 			resp_ca_cert.cert_len = pki->pki_cert_len;
256 			iov[0].iov_base = &resp_ca_cert;
257 			iov[0].iov_len = sizeof(resp_ca_cert);
258 			iov[1].iov_base = pki->pki_cert;
259 			iov[1].iov_len = pki->pki_cert_len;
260 			m_composev(p, IMSG_MTA_SSL_INIT, 0, 0, -1, iov, nitems(iov));
261 			return;
262 
263 		case IMSG_MTA_SSL_VERIFY_CERT:
264 			req_ca_vrfy_mta = xmemdup(imsg->data, sizeof *req_ca_vrfy_mta, "lka:ca_vrfy");
265 			req_ca_vrfy_mta->cert = xmemdup((char *)imsg->data +
266 			    sizeof *req_ca_vrfy_mta, req_ca_vrfy_mta->cert_len, "lka:ca_vrfy");
267 			req_ca_vrfy_mta->chain_cert = xcalloc(req_ca_vrfy_mta->n_chain,
268 			    sizeof (unsigned char *), "lka:ca_vrfy");
269 			req_ca_vrfy_mta->chain_cert_len = xcalloc(req_ca_vrfy_mta->n_chain,
270 			    sizeof (off_t), "lka:ca_vrfy");
271 			return;
272 
273 		case IMSG_MTA_SSL_VERIFY_CHAIN:
274 			if (req_ca_vrfy_mta == NULL)
275 				fatalx("lka:ca_vrfy: verify without a certificate");
276 
277 			req_ca_vrfy_chain = imsg->data;
278 			req_ca_vrfy_mta->chain_cert[req_ca_vrfy_mta->chain_offset] = xmemdup((char *)imsg->data +
279 			    sizeof *req_ca_vrfy_chain, req_ca_vrfy_chain->cert_len, "lka:ca_vrfy");
280 			req_ca_vrfy_mta->chain_cert_len[req_ca_vrfy_mta->chain_offset] = req_ca_vrfy_chain->cert_len;
281 			req_ca_vrfy_mta->chain_offset++;
282 			return;
283 
284 		case IMSG_MTA_SSL_VERIFY:
285 			if (req_ca_vrfy_mta == NULL)
286 				fatalx("lka:ca_vrfy: verify without a certificate");
287 
288 			resp_ca_vrfy.reqid = req_ca_vrfy_mta->reqid;
289 			pki = dict_get(env->sc_pki_dict, req_ca_vrfy_mta->pkiname);
290 
291 			cafile = CA_FILE;
292 			if (pki && pki->pki_ca_file)
293 				cafile = pki->pki_ca_file;
294 			if (! lka_X509_verify(req_ca_vrfy_mta, cafile, NULL))
295 				resp_ca_vrfy.status = CA_FAIL;
296 			else
297 				resp_ca_vrfy.status = CA_OK;
298 
299 			m_compose(p, IMSG_MTA_SSL_VERIFY, 0, 0, -1, &resp_ca_vrfy,
300 			    sizeof resp_ca_vrfy);
301 
302 			for (i = 0; i < req_ca_vrfy_mta->n_chain; ++i)
303 				free(req_ca_vrfy_mta->chain_cert[i]);
304 			free(req_ca_vrfy_mta->chain_cert);
305 			free(req_ca_vrfy_mta->chain_cert_len);
306 			free(req_ca_vrfy_mta->cert);
307 			free(req_ca_vrfy_mta);
308 			return;
309 
310 		case IMSG_MTA_LOOKUP_CREDENTIALS:
311 			m_msg(&m, imsg);
312 			m_get_id(&m, &reqid);
313 			m_get_string(&m, &tablename);
314 			m_get_string(&m, &label);
315 			m_end(&m);
316 
317 			lka_credentials(tablename, label, buf, sizeof(buf));
318 
319 			m_create(p, IMSG_MTA_LOOKUP_CREDENTIALS, 0, 0, -1);
320 			m_add_id(p, reqid);
321 			m_add_string(p, buf);
322 			m_close(p);
323 			return;
324 
325 		case IMSG_MTA_LOOKUP_SOURCE:
326 			m_msg(&m, imsg);
327 			m_get_id(&m, &reqid);
328 			m_get_string(&m, &tablename);
329 			m_end(&m);
330 
331 			table = table_find(tablename, NULL);
332 
333 			m_create(p, IMSG_MTA_LOOKUP_SOURCE, 0, 0, -1);
334 			m_add_id(p, reqid);
335 
336 			if (table == NULL) {
337 				log_warn("warn: source address table %s missing",
338 				    tablename);
339 				m_add_int(p, LKA_TEMPFAIL);
340 			}
341 			else {
342 				ret = table_fetch(table, NULL, K_SOURCE, &lk);
343 				if (ret == -1)
344 					m_add_int(p, LKA_TEMPFAIL);
345 				else if (ret == 0)
346 					m_add_int(p, LKA_PERMFAIL);
347 				else {
348 					m_add_int(p, LKA_OK);
349 					m_add_sockaddr(p,
350 					    (struct sockaddr *)&lk.source.addr);
351 				}
352 			}
353 			m_close(p);
354 			return;
355 
356 		case IMSG_MTA_LOOKUP_HELO:
357 			m_msg(&m, imsg);
358 			m_get_id(&m, &reqid);
359 			m_get_string(&m, &tablename);
360 			m_get_sockaddr(&m, (struct sockaddr *)&ss);
361 			m_end(&m);
362 
363 			ret = lka_addrname(tablename, (struct sockaddr*)&ss,
364 			    &addrname);
365 
366 			m_create(p, IMSG_MTA_LOOKUP_HELO, 0, 0, -1);
367 			m_add_id(p, reqid);
368 			m_add_int(p, ret);
369 			if (ret == LKA_OK)
370 				m_add_string(p, addrname.name);
371 			m_close(p);
372 			return;
373 
374 		}
375 	}
376 
377 	if (p->proc == PROC_PARENT) {
378 		switch (imsg->hdr.type) {
379 		case IMSG_CONF_START:
380 			return;
381 
382 		case IMSG_CONF_END:
383 			if (verbose & TRACE_TABLES)
384 				table_dump_all();
385 			table_open_all();
386 
387 			/* Start fulfilling requests */
388 			mproc_enable(p_pony);
389 			return;
390 
391 		case IMSG_LKA_OPEN_FORWARD:
392 			lka_session_forward_reply(imsg->data, imsg->fd);
393 			return;
394 
395 		case IMSG_LKA_AUTHENTICATE:
396 			imsg->hdr.type = IMSG_SMTP_AUTHENTICATE;
397 			m_forward(p_pony, imsg);
398 			return;
399 		}
400 	}
401 
402 	if (p->proc == PROC_CONTROL) {
403 		switch (imsg->hdr.type) {
404 
405 		case IMSG_CTL_VERBOSE:
406 			m_msg(&m, imsg);
407 			m_get_int(&m, &v);
408 			m_end(&m);
409 			log_verbose(v);
410 			return;
411 
412 		case IMSG_CTL_PROFILE:
413 			m_msg(&m, imsg);
414 			m_get_int(&m, &v);
415 			m_end(&m);
416 			profiling = v;
417 			return;
418 
419 		case IMSG_CTL_UPDATE_TABLE:
420 			table = table_find(imsg->data, NULL);
421 			if (table == NULL) {
422 				log_warnx("warn: Lookup table not found: "
423 				    "\"%s\"", (char *)imsg->data);
424 				return;
425 			}
426 			table_update(table);
427 			return;
428 		}
429 	}
430 
431 	errx(1, "lka_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
432 }
433 
434 static void
435 lka_sig_handler(int sig, short event, void *p)
436 {
437 	int status;
438 	pid_t pid;
439 
440 	switch (sig) {
441 	case SIGINT:
442 	case SIGTERM:
443 		lka_shutdown();
444 		break;
445 	case SIGCHLD:
446 		do {
447 			pid = waitpid(-1, &status, WNOHANG);
448 		} while (pid > 0 || (pid == -1 && errno == EINTR));
449 		break;
450 	default:
451 		fatalx("lka_sig_handler: unexpected signal");
452 	}
453 }
454 
455 void
456 lka_shutdown(void)
457 {
458 	log_info("info: lookup agent exiting");
459 	_exit(0);
460 }
461 
462 pid_t
463 lka(void)
464 {
465 	pid_t		 pid;
466 	struct passwd	*pw;
467 	struct event	 ev_sigint;
468 	struct event	 ev_sigterm;
469 	struct event	 ev_sigchld;
470 
471 	switch (pid = fork()) {
472 	case -1:
473 		fatal("lka: cannot fork");
474 	case 0:
475 		post_fork(PROC_LKA);
476 		break;
477 	default:
478 		return (pid);
479 	}
480 
481 	purge_config(PURGE_LISTENERS);
482 
483 	if ((pw = getpwnam(SMTPD_USER)) == NULL)
484 		fatalx("unknown user " SMTPD_USER);
485 
486 	config_process(PROC_LKA);
487 
488 	if (setgroups(1, &pw->pw_gid) ||
489 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
490 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
491 		fatal("lka: cannot drop privileges");
492 
493 	imsg_callback = lka_imsg;
494 	event_init();
495 
496 	signal_set(&ev_sigint, SIGINT, lka_sig_handler, NULL);
497 	signal_set(&ev_sigterm, SIGTERM, lka_sig_handler, NULL);
498 	signal_set(&ev_sigchld, SIGCHLD, lka_sig_handler, NULL);
499 	signal_add(&ev_sigint, NULL);
500 	signal_add(&ev_sigterm, NULL);
501 	signal_add(&ev_sigchld, NULL);
502 	signal(SIGPIPE, SIG_IGN);
503 	signal(SIGHUP, SIG_IGN);
504 
505 	config_peer(PROC_PARENT);
506 	config_peer(PROC_QUEUE);
507 	config_peer(PROC_CONTROL);
508 	config_peer(PROC_PONY);
509 	config_done();
510 
511 	/* Ignore them until we get our config */
512 	mproc_disable(p_pony);
513 
514 	if (event_dispatch() < 0)
515 		fatal("event_dispatch");
516 	lka_shutdown();
517 
518 	return (0);
519 }
520 
521 static int
522 lka_authenticate(const char *tablename, const char *user, const char *password)
523 {
524 	struct table		*table;
525 	union lookup		 lk;
526 
527 	log_debug("debug: lka: authenticating for %s:%s", tablename, user);
528 	table = table_find(tablename, NULL);
529 	if (table == NULL) {
530 		log_warnx("warn: could not find table %s needed for authentication",
531 		    tablename);
532 		return (LKA_TEMPFAIL);
533 	}
534 
535 	switch (table_lookup(table, NULL, user, K_CREDENTIALS, &lk)) {
536 	case -1:
537 		log_warnx("warn: user credentials lookup fail for %s:%s",
538 		    tablename, user);
539 		return (LKA_TEMPFAIL);
540 	case 0:
541 		return (LKA_PERMFAIL);
542 	default:
543 		if (!strcmp(lk.creds.password, crypt(password, lk.creds.password)))
544 			return (LKA_OK);
545 		return (LKA_PERMFAIL);
546 	}
547 }
548 
549 static int
550 lka_credentials(const char *tablename, const char *label, char *dst, size_t sz)
551 {
552 	struct table		*table;
553 	union lookup		 lk;
554 	char			*buf;
555 	int			 buflen, r;
556 
557 	table = table_find(tablename, NULL);
558 	if (table == NULL) {
559 		log_warnx("warn: credentials table %s missing", tablename);
560 		return (LKA_TEMPFAIL);
561 	}
562 
563 	dst[0] = '\0';
564 
565 	switch(table_lookup(table, NULL, label, K_CREDENTIALS, &lk)) {
566 	case -1:
567 		log_warnx("warn: credentials lookup fail for %s:%s",
568 		    tablename, label);
569 		return (LKA_TEMPFAIL);
570 	case 0:
571 		log_warnx("warn: credentials not found for %s:%s",
572 		    tablename, label);
573 		return (LKA_PERMFAIL);
574 	default:
575 		if ((buflen = asprintf(&buf, "%c%s%c%s", '\0',
576 		    lk.creds.username, '\0', lk.creds.password)) == -1) {
577 			log_warn("warn");
578 			return (LKA_TEMPFAIL);
579 		}
580 
581 		r = base64_encode((unsigned char *)buf, buflen, dst, sz);
582 		free(buf);
583 
584 		if (r == -1) {
585 			log_warnx("warn: credentials parse error for %s:%s",
586 			    tablename, label);
587 			return (LKA_TEMPFAIL);
588 		}
589 		return (LKA_OK);
590 	}
591 }
592 
593 static int
594 lka_userinfo(const char *tablename, const char *username, struct userinfo *res)
595 {
596 	struct table	*table;
597 	union lookup	 lk;
598 
599 	log_debug("debug: lka: userinfo %s:%s", tablename, username);
600 	table = table_find(tablename, NULL);
601 	if (table == NULL) {
602 		log_warnx("warn: cannot find user table %s", tablename);
603 		return (LKA_TEMPFAIL);
604 	}
605 
606 	switch (table_lookup(table, NULL, username, K_USERINFO, &lk)) {
607 	case -1:
608 		log_warnx("warn: failure during userinfo lookup %s:%s",
609 		    tablename, username);
610 		return (LKA_TEMPFAIL);
611 	case 0:
612 		return (LKA_PERMFAIL);
613 	default:
614 		*res = lk.userinfo;
615 		return (LKA_OK);
616 	}
617 }
618 
619 static int
620 lka_addrname(const char *tablename, const struct sockaddr *sa,
621     struct addrname *res)
622 {
623 	struct table	*table;
624 	union lookup	 lk;
625 	const char	*source;
626 
627 	source = sa_to_text(sa);
628 
629 	log_debug("debug: lka: helo %s:%s", tablename, source);
630 	table = table_find(tablename, NULL);
631 	if (table == NULL) {
632 		log_warnx("warn: cannot find helo table %s", tablename);
633 		return (LKA_TEMPFAIL);
634 	}
635 
636 	switch (table_lookup(table, NULL, source, K_ADDRNAME, &lk)) {
637 	case -1:
638 		log_warnx("warn: failure during helo lookup %s:%s",
639 		    tablename, source);
640 		return (LKA_TEMPFAIL);
641 	case 0:
642 		return (LKA_PERMFAIL);
643 	default:
644 		*res = lk.addrname;
645 		return (LKA_OK);
646 	}
647 }
648 
649 static int
650 lka_X509_verify(struct ca_vrfy_req_msg *vrfy,
651     const char *CAfile, const char *CRLfile)
652 {
653 	X509			*x509;
654 	X509			*x509_tmp;
655 	STACK_OF(X509)		*x509_chain;
656 	const unsigned char    	*d2i;
657 	size_t			i;
658 	int			ret = 0;
659 	const char		*errstr;
660 
661 	x509 = NULL;
662 	x509_tmp = NULL;
663 	x509_chain = NULL;
664 
665 	d2i = vrfy->cert;
666 	if (d2i_X509(&x509, &d2i, vrfy->cert_len) == NULL) {
667 		x509 = NULL;
668 		goto end;
669 	}
670 
671 	if (vrfy->n_chain) {
672 		x509_chain = sk_X509_new_null();
673 		for (i = 0; i < vrfy->n_chain; ++i) {
674 			d2i = vrfy->chain_cert[i];
675 			if (d2i_X509(&x509_tmp, &d2i, vrfy->chain_cert_len[i]) == NULL)
676 				goto end;
677 			sk_X509_insert(x509_chain, x509_tmp, i);
678 			x509_tmp = NULL;
679 		}
680 	}
681 	if (! ca_X509_verify(x509, x509_chain, CAfile, NULL, &errstr))
682 		log_debug("debug: lka: X509 verify: %s", errstr);
683 	else
684 		ret = 1;
685 
686 end:
687 	if (x509)
688 		X509_free(x509);
689 	if (x509_tmp)
690 		X509_free(x509_tmp);
691 	if (x509_chain)
692 		sk_X509_pop_free(x509_chain, X509_free);
693 
694 	return ret;
695 }
696