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