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