diff options
Diffstat (limited to 'usr.sbin/smtpd/lka.c')
-rw-r--r-- | usr.sbin/smtpd/lka.c | 363 |
1 files changed, 158 insertions, 205 deletions
diff --git a/usr.sbin/smtpd/lka.c b/usr.sbin/smtpd/lka.c index 2b6ef76815f..c2376e32db2 100644 --- a/usr.sbin/smtpd/lka.c +++ b/usr.sbin/smtpd/lka.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lka.c,v 1.118 2010/09/20 09:01:09 gilles Exp $ */ +/* $OpenBSD: lka.c,v 1.119 2010/10/09 22:05:35 gilles Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -52,35 +52,30 @@ int lka_resolve_node(struct smtpd *, char *tag, struct path *, struct expandnod int lka_verify_mail(struct smtpd *, struct path *); struct rule *ruleset_match(struct smtpd *, char *, struct path *, struct sockaddr_storage *); int lka_resolve_path(struct smtpd *, struct lkasession *, struct path *); -struct lkasession *lka_session_init(struct smtpd *, struct message *); -void lka_request_forwardfile(struct smtpd *, struct lkasession *, struct path *); +struct lkasession *lka_session_init(struct smtpd *, struct submit_status *); +void lka_request_forwardfile(struct smtpd *, struct lkasession *, char *); void lka_clear_expandtree(struct expandtree *); void lka_clear_deliverylist(struct deliverylist *); -char *lka_encode_secret(struct map_secret *); -size_t lka_expand(char *, size_t, struct path *,struct path *); +int lka_encode_credentials(char *, size_t, struct map_secret *); +size_t lka_expand(char *, size_t, struct path *, struct path *); void lka_rcpt_action(struct smtpd *, char *, struct path *); void lka_session_destroy(struct smtpd *, struct lkasession *); void lka_expansion_done(struct smtpd *, struct lkasession *); -void lka_session_fail(struct smtpd *, struct lkasession *); -int lka_queue_append(struct smtpd *, struct lkasession *, int); - -u_int32_t lka_id; +void lka_session_fail(struct smtpd *, struct lkasession *, struct submit_status *); void lka_imsg(struct smtpd *env, struct imsgev *iev, struct imsg *imsg) { struct lkasession skey; + struct submit_status *ss; struct forward_req *fwreq; struct lkasession *s; - struct message *m; + struct secret *secret; struct mapel *mapel; struct rule *rule; struct path *path; struct map *map; - struct map_secret *map_secret; - char *secret; void *tmp; - int status; if (imsg->hdr.type == IMSG_DNS_A || imsg->hdr.type == IMSG_DNS_MX || imsg->hdr.type == IMSG_DNS_PTR) { @@ -91,67 +86,67 @@ lka_imsg(struct smtpd *env, struct imsgev *iev, struct imsg *imsg) if (iev->proc == PROC_MFA) { switch (imsg->hdr.type) { case IMSG_LKA_MAIL: - m = imsg->data; - status = 0; - if (m->sender.user[0] || m->sender.domain[0]) - if (! lka_verify_mail(env, &m->sender)) - status = S_MESSAGE_PERMFAILURE; - imsg_compose_event(iev, IMSG_LKA_MAIL, - m->id, 0, -1, &status, sizeof status); + ss = imsg->data; + ss->code = 530; + if (ss->u.path.user[0] == '\0' && + ss->u.path.domain[0] == '\0') + ss->code = 250; + else + if (lka_verify_mail(env, &ss->u.path)) + ss->code = 250; + imsg_compose_event(iev, IMSG_LKA_MAIL, 0, 0, -1, ss, + sizeof *ss); return; - case IMSG_LKA_RCPT: - m = imsg->data; - rule = ruleset_match(env, m->tag, &m->recipient, &m->session_ss); - if (rule == NULL) { - log_debug("lka: rule not found"); - status = S_MESSAGE_PERMFAILURE; - imsg_compose_event(iev, IMSG_LKA_RCPT, m->id, 0, -1, - &status, sizeof status); - return; + case IMSG_LKA_RULEMATCH: + ss = imsg->data; + ss->code = 530; + rule = ruleset_match(env, ss->msg.tag, &ss->u.path, + &ss->ss); + if (rule) { + ss->code = 250; + ss->u.path.rule = *rule; } - m->recipient.rule = *rule; - s = lka_session_init(env, m); - if (! lka_resolve_path(env, s, &m->recipient)) - lka_session_fail(env, s); - else - lka_expand_pickup(env, s); + imsg_compose_event(iev, IMSG_LKA_RULEMATCH, 0, 0, -1, + ss, sizeof *ss); return; - } - } - if (iev->proc == PROC_QUEUE) { - switch (imsg->hdr.type) { - case IMSG_QUEUE_APPEND: - skey.id = imsg->hdr.peerid; - s = SPLAY_FIND(lkatree, &env->lka_sessions, &skey); - if (s == NULL) - fatalx("lka: session missing"); - memcpy(&status, imsg->data, sizeof status); - lka_queue_append(env, s, status); + case IMSG_LKA_RCPT: + ss = imsg->data; + ss->code = 250; + path = &ss->u.path; + s = lka_session_init(env, ss); + if (! lka_resolve_path(env, s, path)) + lka_session_fail(env, s, ss); + else + lka_expand_pickup(env, s); return; } } if (iev->proc == PROC_MTA) { switch (imsg->hdr.type) { - case IMSG_LKA_SECRET: + case IMSG_LKA_SECRET: { + struct map_secret *map_secret; + secret = imsg->data; map = map_findbyname(env, "secrets"); if (map == NULL) fatalx("lka: secrets map not found"); - map_secret = map_lookup(env, map->m_id, imsg->data, K_SECRET); - if (map_secret) - secret = lka_encode_secret(map_secret); - else - secret = ""; - if (*secret == '\0') - log_warnx("%s secret not found", (char *)imsg->data); - imsg_compose_event(iev, IMSG_LKA_SECRET, - imsg->hdr.peerid, 0, -1, secret, - strlen(secret) + 1); + map_secret = map_lookup(env, map->m_id, secret->host, K_SECRET); + log_debug("lka: %s secret lookup (%d)", secret->host, + map_secret != NULL); + secret->secret[0] = '\0'; + if (map_secret == NULL) + log_warnx("%s secret not found", secret->host); + else if (lka_encode_credentials(secret->secret, + sizeof secret->secret, map_secret) == 0) + log_warnx("%s secret parse fail", secret->host); + imsg_compose_event(iev, IMSG_LKA_SECRET, 0, 0, -1, secret, + sizeof *secret); free(map_secret); return; } + } } if (iev->proc == PROC_PARENT) { @@ -378,29 +373,29 @@ lka_expand(char *buf, size_t len, struct path *path, struct path *sender) struct rule r; size_t ret, lret = 0; struct passwd *pw; - + bzero(r.r_value.buffer, MAX_RULEBUFFER_LEN); pbuf = r.r_value.buffer; - + ret = 0; for (p = path->rule.r_value.buffer; *p != '\0'; - ++p, len -= lret, pbuf += lret, ret += lret) { + ++p, len -= lret, pbuf += lret, ret += lret) { if (p == path->rule.r_value.buffer && *p == '~') { if (*(p + 1) == '/' || *(p + 1) == '\0') { pw = getpwnam(path->pw_name); if (pw == NULL) return 0; - + lret = strlcat(pbuf, pw->pw_dir, len); if (lret >= len) return 0; continue; } - + if (*(p + 1) != '/') { char username[MAXLOGNAME]; char *delim; - + lret = strlcpy(username, p + 1, sizeof(username)); if (lret >= sizeof(username)) @@ -473,10 +468,10 @@ copy: lret = 1; *pbuf = *p; } - + /* + 1 to include the NUL byte. */ memcpy(path->rule.r_value.buffer, r.r_value.buffer, ret + 1); - + return ret; } @@ -555,34 +550,32 @@ lka_resolve_node(struct smtpd *env, char *tag, struct path *path, struct expandn } void -lka_expand_pickup(struct smtpd *env, struct lkasession *s) +lka_expand_pickup(struct smtpd *env, struct lkasession *lkasession) { int ret; - if (s->pending) - return; - - if (s->flags & F_ERROR) { - lka_expansion_done(env, s); - return; - } - /* we want to do five iterations of lka_expand_resume() but * we need to be interruptible in case lka_expand_resume() * has sent an imsg and expects an answer. */ - while (s->iterations < 5) { - s->iterations++; - ret = lka_expand_resume(env, s); - if (ret == -1) - s->flags |= F_ERROR; - if (s->pending) - return; - if (ret == 0) + ret = 0; + while (! (lkasession->flags & F_ERROR) && + ! lkasession->pending && lkasession->iterations < 5) { + ++lkasession->iterations; + ret = lka_expand_resume(env, lkasession); + if (ret == -1) { + lkasession->ss.code = 530; + lkasession->flags |= F_ERROR; + } + + if (lkasession->pending || ret <= 0) break; } - lka_expansion_done(env, s); + if (lkasession->pending) + return; + + lka_expansion_done(env, lkasession); } int @@ -631,91 +624,45 @@ lka_expand_resume(struct smtpd *env, struct lkasession *lkasession) } void -lka_expansion_done(struct smtpd *env, struct lkasession *s) +lka_expansion_done(struct smtpd *env, struct lkasession *lkasession) { - int status; + struct message message; + struct path *path; /* delivery list is empty OR expansion led to an error, reject */ - if (TAILQ_EMPTY(&s->deliverylist) || s->flags & F_ERROR) { - if (TAILQ_EMPTY(&s->deliverylist)) - log_debug("lka_expansion_done: list empty"); - else - log_debug("lka_expansion_done: session error"); - goto error; - } else if (! lka_queue_append(env, s, 0)) - goto error; - return; - -error: - status = S_MESSAGE_PERMFAILURE; - imsg_compose_event(env->sc_ievs[PROC_MFA], IMSG_LKA_RCPT, - s->message.id, 0, -1, &status, sizeof status); - lka_clear_expandtree(&s->expandtree); - lka_clear_deliverylist(&s->deliverylist); - lka_session_destroy(env, s); -} - -int -lka_queue_append(struct smtpd *env, struct lkasession *s, int status) -{ - struct path *path; - struct message message; - struct passwd *pw; - const char *errstr; - char *sep; - uid_t uid; - int ret; - - path = TAILQ_FIRST(&s->deliverylist); - if (path == NULL || status) { - imsg_compose_event(env->sc_ievs[PROC_MFA], IMSG_LKA_RCPT, - s->message.id, 0, -1, &status, sizeof status); - lka_clear_expandtree(&s->expandtree); - lka_clear_deliverylist(&s->deliverylist); - lka_session_destroy(env, s); - return 0; + if (TAILQ_FIRST(&lkasession->deliverylist) == NULL || + lkasession->flags & F_ERROR) { + imsg_compose_event(env->sc_ievs[PROC_MFA], IMSG_LKA_RCPT, 0, 0, + -1, &lkasession->ss, sizeof(struct submit_status)); + goto done; } - /* send next item to queue */ - message = s->message; - if (path->rule.r_action != A_RELAY && - path->rule.r_action != A_RELAYVIA) { - log_debug("lka_expand: before: [%s]", path->rule.r_value.buffer); - ret = lka_expand(path->rule.r_value.buffer, + /* process the delivery list and submit envelopes to queue */ + message = lkasession->message; + while ((path = TAILQ_FIRST(&lkasession->deliverylist)) != NULL) { + lka_expand(path->rule.r_value.buffer, sizeof(path->rule.r_value.buffer), path, &message.sender); - log_debug("lka_expand: after: [%s]", path->rule.r_value.buffer); - if (! ret) { - log_debug("lka_expand: returned failure."); - return 0; - } + message.recipient = *path; + queue_submit_envelope(env, &message); + + TAILQ_REMOVE(&lkasession->deliverylist, path, entry); + free(path); } + queue_commit_envelopes(env, &message); - message.recipient = *path; - sep = strchr(message.session_hostname, '@'); - if (sep) { - *sep = '\0'; - uid = strtonum(message.session_hostname, 0, UID_MAX, &errstr); - if (errstr) - fatalx("lka: invalid uid"); - pw = getpwuid(uid); - if (pw == NULL) - fatalx("lka: non-existent uid"); /* XXX */ - strlcpy(message.sender.pw_name, pw->pw_name, sizeof message.sender.pw_name); - } - imsg_compose_event(env->sc_ievs[PROC_QUEUE], IMSG_QUEUE_APPEND, - s->id, 0, -1, &message, sizeof message); - TAILQ_REMOVE(&s->deliverylist, path, entry); - free(path); - return 1; +done: + lka_clear_expandtree(&lkasession->expandtree); + lka_clear_deliverylist(&lkasession->deliverylist); + lka_session_destroy(env, lkasession); } int -lka_resolve_path(struct smtpd *env, struct lkasession *s, struct path *path) +lka_resolve_path(struct smtpd *env, struct lkasession *lkasession, struct path *path) { if (IS_RELAY(*path)) { path = path_dup(path); path->flags |= F_PATH_RELAY; - TAILQ_INSERT_TAIL(&s->deliverylist, path, entry); + TAILQ_INSERT_TAIL(&lkasession->deliverylist, path, entry); return 1; } @@ -735,27 +682,37 @@ lka_resolve_path(struct smtpd *env, struct lkasession *s, struct path *path) if (aliases_exist(env, path->rule.r_amap, username)) { path->flags |= F_PATH_ALIAS; - return aliases_get(env, path->rule.r_amap, - &s->expandtree, username); + if (! aliases_get(env, path->rule.r_amap, + &lkasession->expandtree, path->user)) + return 0; + return 1; } + if (strlen(username) >= MAXLOGNAME) + return 0; + path->flags |= F_PATH_ACCOUNT; pw = getpwnam(username); if (pw == NULL) return 0; - strlcpy(path->pw_name, pw->pw_name, sizeof path->pw_name); + (void)strlcpy(path->pw_name, pw->pw_name, + sizeof(path->pw_name)); if (path->flags & F_PATH_FORWARDED) - TAILQ_INSERT_TAIL(&s->deliverylist, path, entry); + TAILQ_INSERT_TAIL(&lkasession->deliverylist, path, entry); else - lka_request_forwardfile(env, s, path); + lka_request_forwardfile(env, lkasession, path->pw_name); + return 1; } case C_VDOM: { if (aliases_virtual_exist(env, path->rule.r_condition.c_map, path)) { path->flags |= F_PATH_VIRTUAL; - return aliases_virtual_get(env, path->rule.r_condition.c_map, &s->expandtree, path); + if (! aliases_virtual_get(env, path->rule.r_condition.c_map, + &lkasession->expandtree, path)) + return 0; + return 1; } break; } @@ -821,76 +778,72 @@ lka_clear_deliverylist(struct deliverylist *deliverylist) } } -char * -lka_encode_secret(struct map_secret *map_secret) +int +lka_encode_credentials(char *dst, size_t size, struct map_secret *map_secret) { - static char dst[1024]; - char *src; - int src_sz; + char *buf; + int buflen; - src_sz = asprintf(&src, "%c%s%c%s", '\0', map_secret->username, '\0', - map_secret->password); - if (src_sz == -1) + if ((buflen = asprintf(&buf, "%c%s%c%s", '\0', map_secret->username, + '\0', map_secret->password)) == -1) fatal(NULL); - if (__b64_ntop(src, src_sz, dst, sizeof dst) == -1) { - free(src); - return NULL; + + if (__b64_ntop((unsigned char *)buf, buflen, dst, size) == -1) { + free(buf); + return 0; } - free(src); - dst[sizeof(dst) - 1] = '\0'; - return dst; + free(buf); + return 1; } struct lkasession * -lka_session_init(struct smtpd *env, struct message *m) +lka_session_init(struct smtpd *env, struct submit_status *ss) { - struct lkasession *s; - - s = calloc(1, sizeof *s); - if (s == NULL) - fatal(NULL); - - s->id = lka_id++; - s->path = m->recipient; - s->message = *m; - - RB_INIT(&s->expandtree); - TAILQ_INIT(&s->deliverylist); - SPLAY_INSERT(lkatree, &env->lka_sessions, s); - - return s; + struct lkasession *lkasession; + + lkasession = calloc(1, sizeof(struct lkasession)); + if (lkasession == NULL) + fatal("lka_session_init: calloc"); + + lkasession->id = generate_uid(); + lkasession->path = ss->u.path; + lkasession->message = ss->msg; + lkasession->ss = *ss; + + RB_INIT(&lkasession->expandtree); + TAILQ_INIT(&lkasession->deliverylist); + SPLAY_INSERT(lkatree, &env->lka_sessions, lkasession); + + return lkasession; } void -lka_session_fail(struct smtpd *env, struct lkasession *s) +lka_session_fail(struct smtpd *env, struct lkasession *lkasession, struct submit_status *ss) { - int status; - - log_debug("lka: initina lka_resolve_path failed"); - status = S_MESSAGE_PERMFAILURE; - imsg_compose_event(env->sc_ievs[PROC_MFA], IMSG_LKA_RCPT, - s->message.id, 0, -1, &status, sizeof status); - lka_session_destroy(env, s); + ss->code = 530; + imsg_compose_event(env->sc_ievs[PROC_MFA], IMSG_LKA_RCPT, 0, 0, -1, + ss, sizeof(*ss)); + lka_session_destroy(env, lkasession); } void -lka_session_destroy(struct smtpd *env, struct lkasession *s) +lka_session_destroy(struct smtpd *env, struct lkasession *lkasession) { - SPLAY_REMOVE(lkatree, &env->lka_sessions, s); - free(s); + SPLAY_REMOVE(lkatree, &env->lka_sessions, lkasession); + free(lkasession); } void -lka_request_forwardfile(struct smtpd *env, struct lkasession *s, struct path *path) +lka_request_forwardfile(struct smtpd *env, struct lkasession *lkasession, char *username) { struct forward_req fwreq; - fwreq.id = s->id; - strlcpy(fwreq.pw_name, path->pw_name, sizeof fwreq.pw_name); + fwreq.id = lkasession->id; + (void)strlcpy(fwreq.pw_name, username, sizeof(fwreq.pw_name)); imsg_compose_event(env->sc_ievs[PROC_PARENT], IMSG_PARENT_FORWARD_OPEN, 0, 0, -1, - &fwreq, sizeof fwreq); - s->pending++; + &fwreq, sizeof(fwreq)); + ++lkasession->pending; } SPLAY_GENERATE(lkatree, lkasession, nodes, lkasession_cmp); |