static void lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) { struct forward_req fwreq; struct envelope ep; struct expandnode node; struct mailaddr maddr; int r; union lookup lk; if (xn->depth >= EXPAND_DEPTH) { log_trace(TRACE_EXPAND, "expand: lka_expand: node too deep."); lks->error = LKA_PERMFAIL; return; } switch (xn->type) { case EXPAND_INVALID: case EXPAND_INCLUDE: fatalx("lka_expand: unexpected type"); break; case EXPAND_ADDRESS: log_trace(TRACE_EXPAND, "expand: lka_expand: address: %s@%s " "[depth=%d]", xn->u.mailaddr.user, xn->u.mailaddr.domain, xn->depth); /* Pass the node through the ruleset */ ep = lks->envelope; ep.dest = xn->u.mailaddr; if (xn->parent) /* nodes with parent are forward addresses */ ep.flags |= EF_INTERNAL; rule = ruleset_match(&ep); if (rule == NULL || rule->r_decision == R_REJECT) { lks->error = (errno == EAGAIN) ? LKA_TEMPFAIL : LKA_PERMFAIL; break; } xn->mapping = rule->r_mapping; xn->userbase = rule->r_userbase; if (rule->r_action == A_RELAY || rule->r_action == A_RELAYVIA) { lka_submit(lks, rule, xn); } else if (rule->r_desttype == DEST_VDOM) { /* expand */ lks->expand.rule = rule; lks->expand.parent = xn; lks->expand.alias = 1; /* temporary replace the mailaddr with a copy where * we eventually strip the '+'-part before lookup. */ maddr = xn->u.mailaddr; mailaddr_to_username(&xn->u.mailaddr, maddr.user, sizeof maddr.user); r = aliases_virtual_get(&lks->expand, &maddr); if (r == -1) { lks->error = LKA_TEMPFAIL; log_trace(TRACE_EXPAND, "expand: lka_expand: " "error in virtual alias lookup"); } else if (r == 0) { lks->error = LKA_PERMFAIL; log_trace(TRACE_EXPAND, "expand: lka_expand: " "no aliases for virtual"); } } else { lks->expand.rule = rule; lks->expand.parent = xn; lks->expand.alias = 1; bzero(&node, sizeof node); node.type = EXPAND_USERNAME; mailaddr_to_username(&xn->u.mailaddr, node.u.user, sizeof node.u.user); node.mapping = rule->r_mapping; node.userbase = rule->r_userbase; expand_insert(&lks->expand, &node); } break; case EXPAND_USERNAME: log_trace(TRACE_EXPAND, "expand: lka_expand: username: %s " "[depth=%d]", xn->u.user, xn->depth); if (xn->sameuser) { log_trace(TRACE_EXPAND, "expand: lka_expand: same " "user, submitting"); lka_submit(lks, rule, xn); break; } /* expand aliases with the given rule */ lks->expand.rule = rule; lks->expand.parent = xn; lks->expand.alias = 1; xn->mapping = rule->r_mapping; xn->userbase = rule->r_userbase; if (rule->r_mapping) { r = aliases_get(&lks->expand, xn->u.user); if (r == -1) { log_trace(TRACE_EXPAND, "expand: lka_expand: " "error in alias lookup"); lks->error = LKA_TEMPFAIL; } if (r) break; } /* A username should not exceed the size of a system user */ if (strlen(xn->u.user) >= sizeof fwreq.user) { log_trace(TRACE_EXPAND, "expand: lka_expand: " "user-part too long to be a system user"); lks->error = LKA_PERMFAIL; break; } r = table_lookup(rule->r_userbase, xn->u.user, K_USERINFO, &lk); if (r == -1) { log_trace(TRACE_EXPAND, "expand: lka_expand: " "backend error while searching user"); lks->error = LKA_TEMPFAIL; break; } if (r == 0) { log_trace(TRACE_EXPAND, "expand: lka_expand: " "user-part does not match system user"); lks->error = LKA_PERMFAIL; break; } /* no aliases found, query forward file */ lks->rule = rule; lks->node = xn; fwreq.id = lks->id; (void)strlcpy(fwreq.user, lk.userinfo.username, sizeof(fwreq.user)); (void)strlcpy(fwreq.directory, lk.userinfo.directory, sizeof(fwreq.directory)); fwreq.uid = lk.userinfo.uid; fwreq.gid = lk.userinfo.gid; m_compose(p_parent, IMSG_PARENT_FORWARD_OPEN, 0, 0, -1, &fwreq, sizeof(fwreq)); lks->flags |= F_WAITING; break; case EXPAND_FILENAME: log_trace(TRACE_EXPAND, "expand: lka_expand: filename: %s " "[depth=%d]", xn->u.buffer, xn->depth); lka_submit(lks, rule, xn); break; case EXPAND_ERROR: log_trace(TRACE_EXPAND, "expand: lka_expand: error: %s " "[depth=%d]", xn->u.buffer, xn->depth); if (xn->u.buffer[0] == '4') lks->error = LKA_TEMPFAIL; else if (xn->u.buffer[0] == '5') lks->error = LKA_PERMFAIL; lks->errormsg = xn->u.buffer; break; case EXPAND_FILTER: log_trace(TRACE_EXPAND, "expand: lka_expand: filter: %s " "[depth=%d]", xn->u.buffer, xn->depth); lka_submit(lks, rule, xn); break; } }
static void lka_imsg(struct imsgev *iev, struct imsg *imsg) { struct submit_status *ss; struct secret *secret; struct mapel *mapel; struct rule *rule; struct map *map; struct map *mp; void *tmp; if (imsg->hdr.type == IMSG_DNS_HOST || imsg->hdr.type == IMSG_DNS_MX || imsg->hdr.type == IMSG_DNS_PTR) { dns_async(iev, imsg->hdr.type, imsg->data); return; } if (iev->proc == PROC_MFA) { switch (imsg->hdr.type) { case IMSG_LKA_MAIL: ss = imsg->data; ss->code = 530; if (ss->u.maddr.user[0] == '\0' && ss->u.maddr.domain[0] == '\0') ss->code = 250; else if (lka_verify_mail(&ss->u.maddr)) ss->code = 250; imsg_compose_event(iev, IMSG_LKA_MAIL, 0, 0, -1, ss, sizeof *ss); return; case IMSG_LKA_RULEMATCH: ss = imsg->data; rule = ruleset_match(&ss->envelope); if (rule == NULL) ss->code = (errno == EAGAIN) ? 451 : 530; else ss->code = (rule->r_decision == R_ACCEPT) ? 250 : 530; imsg_compose_event(iev, IMSG_LKA_RULEMATCH, 0, 0, -1, ss, sizeof *ss); return; case IMSG_LKA_RCPT: lka_session(imsg->data); return; } } if (iev->proc == PROC_MTA) { switch (imsg->hdr.type) { case IMSG_LKA_SECRET: { struct map_credentials *map_credentials; secret = imsg->data; map = map_findbyname(secret->mapname); if (map == NULL) { log_warn("warn: lka: credentials map %s is missing", secret->mapname); imsg_compose_event(iev, IMSG_LKA_SECRET, 0, 0, -1, secret, sizeof *secret); return; } map_credentials = map_lookup(map->m_id, secret->host, K_CREDENTIALS); log_debug("debug: lka: %s credentials lookup (%d)", secret->host, map_credentials != NULL); secret->secret[0] = '\0'; if (map_credentials == NULL) log_warnx("warn: %s credentials not found", secret->host); else if (lka_encode_credentials(secret->secret, sizeof secret->secret, map_credentials) == 0) log_warnx("warn: %s credentials parse fail", secret->host); imsg_compose_event(iev, IMSG_LKA_SECRET, 0, 0, -1, secret, sizeof *secret); free(map_credentials); return; } } } if (iev->proc == PROC_PARENT) { switch (imsg->hdr.type) { case IMSG_CONF_START: env->sc_rules_reload = xcalloc(1, sizeof *env->sc_rules, "lka:sc_rules_reload"); env->sc_maps_reload = xcalloc(1, sizeof *env->sc_maps, "lka:sc_maps_reload"); TAILQ_INIT(env->sc_rules_reload); TAILQ_INIT(env->sc_maps_reload); return; case IMSG_CONF_RULE: rule = xmemdup(imsg->data, sizeof *rule, "lka:rule"); TAILQ_INSERT_TAIL(env->sc_rules_reload, rule, r_entry); return; case IMSG_CONF_MAP: map = xmemdup(imsg->data, sizeof *map, "lka:map"); TAILQ_INIT(&map->m_contents); TAILQ_INSERT_TAIL(env->sc_maps_reload, map, m_entry); tmp = env->sc_maps; env->sc_maps = env->sc_maps_reload; mp = map_open(map); if (mp == NULL) errx(1, "lka: could not open map \"%s\"", map->m_name); map_close(map, mp); env->sc_maps = tmp; return; case IMSG_CONF_RULE_SOURCE: rule = TAILQ_LAST(env->sc_rules_reload, rulelist); tmp = env->sc_maps; env->sc_maps = env->sc_maps_reload; rule->r_sources = map_findbyname(imsg->data); if (rule->r_sources == NULL) fatalx("lka: maps inconsistency"); env->sc_maps = tmp; return; case IMSG_CONF_MAP_CONTENT: map = TAILQ_LAST(env->sc_maps_reload, maplist); mapel = xmemdup(imsg->data, sizeof *mapel, "lka:mapel"); TAILQ_INSERT_TAIL(&map->m_contents, mapel, me_entry); return; case IMSG_CONF_END: if (env->sc_rules) purge_config(PURGE_RULES); if (env->sc_maps) purge_config(PURGE_MAPS); env->sc_rules = env->sc_rules_reload; env->sc_maps = env->sc_maps_reload; /* start fulfilling requests */ event_add(&env->sc_ievs[PROC_MTA]->ev, NULL); event_add(&env->sc_ievs[PROC_MFA]->ev, NULL); event_add(&env->sc_ievs[PROC_SMTP]->ev, NULL); return; case IMSG_CTL_VERBOSE: log_verbose(*(int *)imsg->data); return; case IMSG_PARENT_FORWARD_OPEN: lka_session_forward_reply(imsg->data, imsg->fd); return; } } if (iev->proc == PROC_CONTROL) { switch (imsg->hdr.type) { case IMSG_LKA_UPDATE_MAP: map = map_findbyname(imsg->data); if (map == NULL) { log_warnx("warn: lka: no such map \"%s\"", (char *)imsg->data); return; } map_update(map); return; } } errx(1, "lka_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); }