1 /* $OpenBSD: lka_session.c,v 1.94 2020/12/31 08:27:15 martijn Exp $ */ 2 3 /* 4 * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> 5 * Copyright (c) 2012 Eric Faurot <eric@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/socket.h> 24 #include <sys/wait.h> 25 26 #include <netinet/in.h> 27 28 #include <ctype.h> 29 #include <errno.h> 30 #include <event.h> 31 #include <imsg.h> 32 #include <resolv.h> 33 #include <pwd.h> 34 #include <signal.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <limits.h> 40 41 #include "smtpd.h" 42 #include "log.h" 43 44 #define EXPAND_DEPTH 10 45 46 #define F_WAITING 0x01 47 48 struct lka_session { 49 uint64_t id; /* given by smtp */ 50 51 TAILQ_HEAD(, envelope) deliverylist; 52 struct expand expand; 53 54 int flags; 55 int error; 56 const char *errormsg; 57 struct envelope envelope; 58 struct xnodes nodes; 59 /* waiting for fwdrq */ 60 struct rule *rule; 61 struct expandnode *node; 62 }; 63 64 static void lka_expand(struct lka_session *, struct rule *, 65 struct expandnode *); 66 static void lka_submit(struct lka_session *, struct rule *, 67 struct expandnode *); 68 static void lka_resume(struct lka_session *); 69 70 static int init; 71 static struct tree sessions; 72 73 void 74 lka_session(uint64_t id, struct envelope *envelope) 75 { 76 struct lka_session *lks; 77 struct expandnode xn; 78 79 if (init == 0) { 80 init = 1; 81 tree_init(&sessions); 82 } 83 84 lks = xcalloc(1, sizeof(*lks)); 85 lks->id = id; 86 RB_INIT(&lks->expand.tree); 87 TAILQ_INIT(&lks->deliverylist); 88 tree_xset(&sessions, lks->id, lks); 89 90 lks->envelope = *envelope; 91 92 TAILQ_INIT(&lks->nodes); 93 memset(&xn, 0, sizeof xn); 94 xn.type = EXPAND_ADDRESS; 95 xn.u.mailaddr = lks->envelope.rcpt; 96 lks->expand.parent = NULL; 97 lks->expand.rule = NULL; 98 lks->expand.queue = &lks->nodes; 99 expand_insert(&lks->expand, &xn); 100 lka_resume(lks); 101 } 102 103 void 104 lka_session_forward_reply(struct forward_req *fwreq, int fd) 105 { 106 struct lka_session *lks; 107 struct dispatcher *dsp; 108 struct rule *rule; 109 struct expandnode *xn; 110 int ret; 111 112 lks = tree_xget(&sessions, fwreq->id); 113 xn = lks->node; 114 rule = lks->rule; 115 116 lks->flags &= ~F_WAITING; 117 118 switch (fwreq->status) { 119 case 0: 120 /* permanent failure while lookup ~/.forward */ 121 log_trace(TRACE_EXPAND, "expand: ~/.forward failed for user %s", 122 fwreq->user); 123 lks->error = LKA_PERMFAIL; 124 break; 125 case 1: 126 if (fd == -1) { 127 dsp = dict_get(env->sc_dispatchers, lks->rule->dispatcher); 128 if (dsp->u.local.forward_only) { 129 log_trace(TRACE_EXPAND, "expand: no .forward " 130 "for user %s on forward-only rule", fwreq->user); 131 lks->error = LKA_TEMPFAIL; 132 } 133 else if (dsp->u.local.expand_only) { 134 log_trace(TRACE_EXPAND, "expand: no .forward " 135 "for user %s and no default action on rule", fwreq->user); 136 lks->error = LKA_PERMFAIL; 137 } 138 else { 139 log_trace(TRACE_EXPAND, "expand: no .forward for " 140 "user %s, just deliver", fwreq->user); 141 lka_submit(lks, rule, xn); 142 } 143 } 144 else { 145 dsp = dict_get(env->sc_dispatchers, rule->dispatcher); 146 147 /* expand for the current user and rule */ 148 lks->expand.rule = rule; 149 lks->expand.parent = xn; 150 151 /* forwards_get() will close the descriptor no matter what */ 152 ret = forwards_get(fd, &lks->expand); 153 if (ret == -1) { 154 log_trace(TRACE_EXPAND, "expand: temporary " 155 "forward error for user %s", fwreq->user); 156 lks->error = LKA_TEMPFAIL; 157 } 158 else if (ret == 0) { 159 if (dsp->u.local.forward_only) { 160 log_trace(TRACE_EXPAND, "expand: empty .forward " 161 "for user %s on forward-only rule", fwreq->user); 162 lks->error = LKA_TEMPFAIL; 163 } 164 else if (dsp->u.local.expand_only) { 165 log_trace(TRACE_EXPAND, "expand: empty .forward " 166 "for user %s and no default action on rule", fwreq->user); 167 lks->error = LKA_PERMFAIL; 168 } 169 else { 170 log_trace(TRACE_EXPAND, "expand: empty .forward " 171 "for user %s, just deliver", fwreq->user); 172 lka_submit(lks, rule, xn); 173 } 174 } 175 } 176 break; 177 default: 178 /* temporary failure while looking up ~/.forward */ 179 lks->error = LKA_TEMPFAIL; 180 } 181 182 if (lks->error == LKA_TEMPFAIL && lks->errormsg == NULL) 183 lks->errormsg = "424 4.2.4 Mailing list expansion problem"; 184 if (lks->error == LKA_PERMFAIL && lks->errormsg == NULL) 185 lks->errormsg = "524 5.2.4 Mailing list expansion problem"; 186 187 lka_resume(lks); 188 } 189 190 static void 191 lka_resume(struct lka_session *lks) 192 { 193 struct envelope *ep; 194 struct expandnode *xn; 195 196 if (lks->error) 197 goto error; 198 199 /* pop next node and expand it */ 200 while ((xn = TAILQ_FIRST(&lks->nodes))) { 201 TAILQ_REMOVE(&lks->nodes, xn, tq_entry); 202 lka_expand(lks, xn->rule, xn); 203 if (lks->flags & F_WAITING) 204 return; 205 if (lks->error) 206 goto error; 207 } 208 209 /* delivery list is empty, reject */ 210 if (TAILQ_FIRST(&lks->deliverylist) == NULL) { 211 log_trace(TRACE_EXPAND, "expand: lka_done: expanded to empty " 212 "delivery list"); 213 lks->error = LKA_PERMFAIL; 214 lks->errormsg = "524 5.2.4 Mailing list expansion problem"; 215 } 216 error: 217 if (lks->error) { 218 m_create(p_dispatcher, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1); 219 m_add_id(p_dispatcher, lks->id); 220 m_add_int(p_dispatcher, lks->error); 221 222 if (lks->errormsg) 223 m_add_string(p_dispatcher, lks->errormsg); 224 else { 225 if (lks->error == LKA_PERMFAIL) 226 m_add_string(p_dispatcher, "550 Invalid recipient"); 227 else if (lks->error == LKA_TEMPFAIL) 228 m_add_string(p_dispatcher, "451 Temporary failure"); 229 } 230 231 m_close(p_dispatcher); 232 while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) { 233 TAILQ_REMOVE(&lks->deliverylist, ep, entry); 234 free(ep); 235 } 236 } 237 else { 238 /* Process the delivery list and submit envelopes to queue */ 239 while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) { 240 TAILQ_REMOVE(&lks->deliverylist, ep, entry); 241 m_create(p_queue, IMSG_LKA_ENVELOPE_SUBMIT, 0, 0, -1); 242 m_add_id(p_queue, lks->id); 243 m_add_envelope(p_queue, ep); 244 m_close(p_queue); 245 free(ep); 246 } 247 248 m_create(p_queue, IMSG_LKA_ENVELOPE_COMMIT, 0, 0, -1); 249 m_add_id(p_queue, lks->id); 250 m_close(p_queue); 251 } 252 253 expand_clear(&lks->expand); 254 tree_xpop(&sessions, lks->id); 255 free(lks); 256 } 257 258 static void 259 lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) 260 { 261 struct forward_req fwreq; 262 struct envelope ep; 263 struct expandnode node; 264 struct mailaddr maddr; 265 struct dispatcher *dsp; 266 struct table *userbase; 267 int r; 268 union lookup lk; 269 char *tag; 270 const char *srs_decoded; 271 272 if (xn->depth >= EXPAND_DEPTH) { 273 log_trace(TRACE_EXPAND, "expand: lka_expand: node too deep."); 274 lks->error = LKA_PERMFAIL; 275 lks->errormsg = "524 5.2.4 Mailing list expansion problem"; 276 return; 277 } 278 279 switch (xn->type) { 280 case EXPAND_INVALID: 281 case EXPAND_INCLUDE: 282 fatalx("lka_expand: unexpected type"); 283 break; 284 285 case EXPAND_ADDRESS: 286 287 log_trace(TRACE_EXPAND, "expand: lka_expand: address: %s@%s " 288 "[depth=%d]", 289 xn->u.mailaddr.user, xn->u.mailaddr.domain, xn->depth); 290 291 292 ep = lks->envelope; 293 ep.dest = xn->u.mailaddr; 294 if (xn->parent) /* nodes with parent are forward addresses */ 295 ep.flags |= EF_INTERNAL; 296 297 /* handle SRS */ 298 if (env->sc_srs_key != NULL && 299 ep.sender.user[0] == '\0' && 300 (strncasecmp(ep.rcpt.user, "SRS0=", 5) == 0 || 301 strncasecmp(ep.rcpt.user, "SRS1=", 5) == 0)) { 302 srs_decoded = srs_decode(mailaddr_to_text(&ep.rcpt)); 303 if (srs_decoded && 304 text_to_mailaddr(&ep.rcpt, srs_decoded)) { 305 /* flag envelope internal and override rcpt */ 306 ep.flags |= EF_INTERNAL; 307 xn->u.mailaddr = ep.rcpt; 308 lks->envelope = ep; 309 } 310 else { 311 log_warn("SRS failed to decode: %s", 312 mailaddr_to_text(&ep.rcpt)); 313 } 314 } 315 316 /* Pass the node through the ruleset */ 317 rule = ruleset_match(&ep); 318 if (rule == NULL || rule->reject) { 319 lks->error = (errno == EAGAIN) ? 320 LKA_TEMPFAIL : LKA_PERMFAIL; 321 break; 322 } 323 324 dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); 325 if (dsp->type == DISPATCHER_REMOTE) { 326 lka_submit(lks, rule, xn); 327 } 328 else if (dsp->u.local.table_virtual) { 329 /* expand */ 330 lks->expand.rule = rule; 331 lks->expand.parent = xn; 332 333 /* temporary replace the mailaddr with a copy where 334 * we eventually strip the '+'-part before lookup. 335 */ 336 maddr = xn->u.mailaddr; 337 xlowercase(maddr.user, xn->u.mailaddr.user, 338 sizeof maddr.user); 339 r = aliases_virtual_get(&lks->expand, &maddr); 340 if (r == -1) { 341 lks->error = LKA_TEMPFAIL; 342 log_trace(TRACE_EXPAND, "expand: lka_expand: " 343 "error in virtual alias lookup"); 344 } 345 else if (r == 0) { 346 lks->error = LKA_PERMFAIL; 347 log_trace(TRACE_EXPAND, "expand: lka_expand: " 348 "no aliases for virtual"); 349 } 350 if (lks->error == LKA_TEMPFAIL && lks->errormsg == NULL) 351 lks->errormsg = "424 4.2.4 Mailing list expansion problem"; 352 if (lks->error == LKA_PERMFAIL && lks->errormsg == NULL) 353 lks->errormsg = "524 5.2.4 Mailing list expansion problem"; 354 } 355 else { 356 lks->expand.rule = rule; 357 lks->expand.parent = xn; 358 xn->rule = rule; 359 360 memset(&node, 0, sizeof node); 361 node.type = EXPAND_USERNAME; 362 xlowercase(node.u.user, xn->u.mailaddr.user, 363 sizeof node.u.user); 364 expand_insert(&lks->expand, &node); 365 } 366 break; 367 368 case EXPAND_USERNAME: 369 log_trace(TRACE_EXPAND, "expand: lka_expand: username: %s " 370 "[depth=%d, sameuser=%d]", 371 xn->u.user, xn->depth, xn->sameuser); 372 373 /* expand aliases with the given rule */ 374 dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); 375 376 lks->expand.rule = rule; 377 lks->expand.parent = xn; 378 379 if (!xn->sameuser && 380 (dsp->u.local.table_alias || dsp->u.local.table_virtual)) { 381 if (dsp->u.local.table_alias) 382 r = aliases_get(&lks->expand, xn->u.user); 383 if (dsp->u.local.table_virtual) 384 r = aliases_virtual_get(&lks->expand, &xn->u.mailaddr); 385 if (r == -1) { 386 log_trace(TRACE_EXPAND, "expand: lka_expand: " 387 "error in alias lookup"); 388 lks->error = LKA_TEMPFAIL; 389 if (lks->errormsg == NULL) 390 lks->errormsg = "424 4.2.4 Mailing list expansion problem"; 391 } 392 if (r) 393 break; 394 } 395 396 /* gilles+hackers@ -> gilles@ */ 397 if ((tag = strchr(xn->u.user, *env->sc_subaddressing_delim)) != NULL) { 398 *tag++ = '\0'; 399 (void)strlcpy(xn->subaddress, tag, sizeof xn->subaddress); 400 } 401 402 userbase = table_find(env, dsp->u.local.table_userbase); 403 r = table_lookup(userbase, K_USERINFO, xn->u.user, &lk); 404 if (r == -1) { 405 log_trace(TRACE_EXPAND, "expand: lka_expand: " 406 "backend error while searching user"); 407 lks->error = LKA_TEMPFAIL; 408 break; 409 } 410 if (r == 0) { 411 log_trace(TRACE_EXPAND, "expand: lka_expand: " 412 "user-part does not match system user"); 413 lks->error = LKA_PERMFAIL; 414 break; 415 } 416 xn->realuser = 1; 417 418 if (xn->sameuser && xn->parent->forwarded) { 419 log_trace(TRACE_EXPAND, "expand: lka_expand: same " 420 "user, submitting"); 421 lka_submit(lks, rule, xn); 422 break; 423 } 424 425 /* no aliases found, query forward file */ 426 lks->rule = rule; 427 lks->node = xn; 428 xn->forwarded = 1; 429 430 memset(&fwreq, 0, sizeof(fwreq)); 431 fwreq.id = lks->id; 432 (void)strlcpy(fwreq.user, lk.userinfo.username, sizeof(fwreq.user)); 433 (void)strlcpy(fwreq.directory, lk.userinfo.directory, sizeof(fwreq.directory)); 434 fwreq.uid = lk.userinfo.uid; 435 fwreq.gid = lk.userinfo.gid; 436 437 m_compose(p_parent, IMSG_LKA_OPEN_FORWARD, 0, 0, -1, 438 &fwreq, sizeof(fwreq)); 439 lks->flags |= F_WAITING; 440 break; 441 442 case EXPAND_FILENAME: 443 dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); 444 if (dsp->u.local.forward_only) { 445 log_trace(TRACE_EXPAND, "expand: filename matched on forward-only rule"); 446 lks->error = LKA_TEMPFAIL; 447 break; 448 } 449 log_trace(TRACE_EXPAND, "expand: lka_expand: filename: %s " 450 "[depth=%d]", xn->u.buffer, xn->depth); 451 lka_submit(lks, rule, xn); 452 break; 453 454 case EXPAND_ERROR: 455 dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); 456 if (dsp->u.local.forward_only) { 457 log_trace(TRACE_EXPAND, "expand: error matched on forward-only rule"); 458 lks->error = LKA_TEMPFAIL; 459 break; 460 } 461 log_trace(TRACE_EXPAND, "expand: lka_expand: error: %s " 462 "[depth=%d]", xn->u.buffer, xn->depth); 463 if (xn->u.buffer[0] == '4') 464 lks->error = LKA_TEMPFAIL; 465 else if (xn->u.buffer[0] == '5') 466 lks->error = LKA_PERMFAIL; 467 lks->errormsg = xn->u.buffer; 468 break; 469 470 case EXPAND_FILTER: 471 dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); 472 if (dsp->u.local.forward_only) { 473 log_trace(TRACE_EXPAND, "expand: filter matched on forward-only rule"); 474 lks->error = LKA_TEMPFAIL; 475 break; 476 } 477 log_trace(TRACE_EXPAND, "expand: lka_expand: filter: %s " 478 "[depth=%d]", xn->u.buffer, xn->depth); 479 lka_submit(lks, rule, xn); 480 break; 481 } 482 } 483 484 static struct expandnode * 485 lka_find_ancestor(struct expandnode *xn, enum expand_type type) 486 { 487 while (xn && (xn->type != type)) 488 xn = xn->parent; 489 if (xn == NULL) { 490 log_warnx("warn: lka_find_ancestor: no ancestors of type %d", 491 type); 492 fatalx(NULL); 493 } 494 return (xn); 495 } 496 497 static void 498 lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn) 499 { 500 struct envelope *ep; 501 struct dispatcher *dsp; 502 const char *user; 503 const char *format; 504 505 ep = xmemdup(&lks->envelope, sizeof *ep); 506 (void)strlcpy(ep->dispatcher, rule->dispatcher, sizeof ep->dispatcher); 507 508 dsp = dict_xget(env->sc_dispatchers, ep->dispatcher); 509 510 switch (dsp->type) { 511 case DISPATCHER_REMOTE: 512 if (xn->type != EXPAND_ADDRESS) 513 fatalx("lka_deliver: expect address"); 514 ep->type = D_MTA; 515 ep->dest = xn->u.mailaddr; 516 break; 517 518 case DISPATCHER_BOUNCE: 519 case DISPATCHER_LOCAL: 520 if (xn->type != EXPAND_USERNAME && 521 xn->type != EXPAND_FILENAME && 522 xn->type != EXPAND_FILTER) 523 fatalx("lka_deliver: wrong type: %d", xn->type); 524 525 ep->type = D_MDA; 526 ep->dest = lka_find_ancestor(xn, EXPAND_ADDRESS)->u.mailaddr; 527 if (xn->type == EXPAND_USERNAME) { 528 (void)strlcpy(ep->mda_user, xn->u.user, sizeof(ep->mda_user)); 529 (void)strlcpy(ep->mda_subaddress, xn->subaddress, sizeof(ep->mda_subaddress)); 530 } 531 else { 532 user = !xn->parent->realuser ? 533 SMTPD_USER : 534 xn->parent->u.user; 535 (void)strlcpy(ep->mda_user, user, sizeof (ep->mda_user)); 536 537 /* this battle needs to be fought ... */ 538 if (xn->type == EXPAND_FILTER && 539 strcmp(ep->mda_user, SMTPD_USER) == 0) 540 log_warnx("commands executed from aliases " 541 "run with %s privileges", SMTPD_USER); 542 543 if (xn->type == EXPAND_FILENAME) 544 format = "/usr/libexec/mail.mboxfile -f %%{mbox.from} %s"; 545 else if (xn->type == EXPAND_FILTER) 546 format = "%s"; 547 (void)snprintf(ep->mda_exec, sizeof(ep->mda_exec), 548 format, xn->u.buffer); 549 } 550 break; 551 } 552 553 TAILQ_INSERT_TAIL(&lks->deliverylist, ep, entry); 554 } 555