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