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