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