1 /* $OpenBSD: lka_session.c,v 1.80 2016/08/31 10:18:08 gilles 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 static size_t lka_expand_format(char *, size_t, const struct envelope *, 70 const struct userinfo *); 71 72 static int mod_lowercase(char *, size_t); 73 static int mod_uppercase(char *, size_t); 74 static int mod_strip(char *, size_t); 75 76 struct modifiers { 77 char *name; 78 int (*f)(char *buf, size_t len); 79 } token_modifiers[] = { 80 { "lowercase", mod_lowercase }, 81 { "uppercase", mod_uppercase }, 82 { "strip", mod_strip }, 83 { "raw", NULL }, /* special case, must stay last */ 84 }; 85 86 static int init; 87 static struct tree sessions; 88 89 #define MAXTOKENLEN 128 90 91 void 92 lka_session(uint64_t id, struct envelope *envelope) 93 { 94 struct lka_session *lks; 95 struct expandnode xn; 96 97 if (init == 0) { 98 init = 1; 99 tree_init(&sessions); 100 } 101 102 lks = xcalloc(1, sizeof(*lks), "lka_session"); 103 lks->id = id; 104 RB_INIT(&lks->expand.tree); 105 TAILQ_INIT(&lks->deliverylist); 106 tree_xset(&sessions, lks->id, lks); 107 108 lks->envelope = *envelope; 109 110 TAILQ_INIT(&lks->nodes); 111 memset(&xn, 0, sizeof xn); 112 xn.type = EXPAND_ADDRESS; 113 xn.u.mailaddr = lks->envelope.rcpt; 114 lks->expand.rule = NULL; 115 lks->expand.queue = &lks->nodes; 116 expand_insert(&lks->expand, &xn); 117 lka_resume(lks); 118 } 119 120 void 121 lka_session_forward_reply(struct forward_req *fwreq, int fd) 122 { 123 struct lka_session *lks; 124 struct rule *rule; 125 struct expandnode *xn; 126 int ret; 127 128 lks = tree_xget(&sessions, fwreq->id); 129 xn = lks->node; 130 rule = lks->rule; 131 132 lks->flags &= ~F_WAITING; 133 134 switch (fwreq->status) { 135 case 0: 136 /* permanent failure while lookup ~/.forward */ 137 log_trace(TRACE_EXPAND, "expand: ~/.forward failed for user %s", 138 fwreq->user); 139 lks->error = LKA_PERMFAIL; 140 break; 141 case 1: 142 if (fd == -1) { 143 if (lks->expand.rule->r_forwardonly) { 144 log_trace(TRACE_EXPAND, "expand: no .forward " 145 "for user %s on forward-only rule", fwreq->user); 146 lks->error = LKA_TEMPFAIL; 147 } 148 else if (lks->expand.rule->r_action == A_NONE) { 149 log_trace(TRACE_EXPAND, "expand: no .forward " 150 "for user %s and no default action on rule", fwreq->user); 151 lks->error = LKA_PERMFAIL; 152 } 153 else { 154 log_trace(TRACE_EXPAND, "expand: no .forward for " 155 "user %s, just deliver", fwreq->user); 156 lka_submit(lks, rule, xn); 157 } 158 } 159 else { 160 /* expand for the current user and rule */ 161 lks->expand.rule = rule; 162 lks->expand.parent = xn; 163 lks->expand.alias = 0; 164 xn->mapping = rule->r_mapping; 165 xn->userbase = rule->r_userbase; 166 /* forwards_get() will close the descriptor no matter what */ 167 ret = forwards_get(fd, &lks->expand); 168 if (ret == -1) { 169 log_trace(TRACE_EXPAND, "expand: temporary " 170 "forward error for user %s", fwreq->user); 171 lks->error = LKA_TEMPFAIL; 172 } 173 else if (ret == 0) { 174 if (lks->expand.rule->r_forwardonly) { 175 log_trace(TRACE_EXPAND, "expand: empty .forward " 176 "for user %s on forward-only rule", fwreq->user); 177 lks->error = LKA_TEMPFAIL; 178 } 179 else if (lks->expand.rule->r_action == A_NONE) { 180 log_trace(TRACE_EXPAND, "expand: empty .forward " 181 "for user %s and no default action on rule", fwreq->user); 182 lks->error = LKA_PERMFAIL; 183 } 184 else { 185 log_trace(TRACE_EXPAND, "expand: empty .forward " 186 "for user %s, just deliver", fwreq->user); 187 lka_submit(lks, rule, xn); 188 } 189 } 190 } 191 break; 192 default: 193 /* temporary failure while looking up ~/.forward */ 194 lks->error = LKA_TEMPFAIL; 195 } 196 197 lka_resume(lks); 198 } 199 200 static void 201 lka_resume(struct lka_session *lks) 202 { 203 struct envelope *ep; 204 struct expandnode *xn; 205 206 if (lks->error) 207 goto error; 208 209 /* pop next node and expand it */ 210 while ((xn = TAILQ_FIRST(&lks->nodes))) { 211 TAILQ_REMOVE(&lks->nodes, xn, tq_entry); 212 lka_expand(lks, xn->rule, xn); 213 if (lks->flags & F_WAITING) 214 return; 215 if (lks->error) 216 goto error; 217 } 218 219 /* delivery list is empty, reject */ 220 if (TAILQ_FIRST(&lks->deliverylist) == NULL) { 221 log_trace(TRACE_EXPAND, "expand: lka_done: expanded to empty " 222 "delivery list"); 223 lks->error = LKA_PERMFAIL; 224 } 225 error: 226 if (lks->error) { 227 m_create(p_pony, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1); 228 m_add_id(p_pony, lks->id); 229 m_add_int(p_pony, lks->error); 230 231 if (lks->errormsg) 232 m_add_string(p_pony, lks->errormsg); 233 else { 234 if (lks->error == LKA_PERMFAIL) 235 m_add_string(p_pony, "550 Invalid recipient"); 236 else if (lks->error == LKA_TEMPFAIL) 237 m_add_string(p_pony, "451 Temporary failure"); 238 } 239 240 m_close(p_pony); 241 while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) { 242 TAILQ_REMOVE(&lks->deliverylist, ep, entry); 243 free(ep); 244 } 245 } 246 else { 247 /* Process the delivery list and submit envelopes to queue */ 248 while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) { 249 TAILQ_REMOVE(&lks->deliverylist, ep, entry); 250 m_create(p_queue, IMSG_LKA_ENVELOPE_SUBMIT, 0, 0, -1); 251 m_add_id(p_queue, lks->id); 252 m_add_envelope(p_queue, ep); 253 m_close(p_queue); 254 free(ep); 255 } 256 257 m_create(p_queue, IMSG_LKA_ENVELOPE_COMMIT, 0, 0, -1); 258 m_add_id(p_queue, lks->id); 259 m_close(p_queue); 260 } 261 262 expand_clear(&lks->expand); 263 tree_xpop(&sessions, lks->id); 264 free(lks); 265 } 266 267 static void 268 lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) 269 { 270 struct forward_req fwreq; 271 struct envelope ep; 272 struct expandnode node; 273 struct mailaddr maddr; 274 int r; 275 union lookup lk; 276 char *tag; 277 278 if (xn->depth >= EXPAND_DEPTH) { 279 log_trace(TRACE_EXPAND, "expand: lka_expand: node too deep."); 280 lks->error = LKA_PERMFAIL; 281 return; 282 } 283 284 switch (xn->type) { 285 case EXPAND_INVALID: 286 case EXPAND_INCLUDE: 287 fatalx("lka_expand: unexpected type"); 288 break; 289 290 case EXPAND_ADDRESS: 291 292 log_trace(TRACE_EXPAND, "expand: lka_expand: address: %s@%s " 293 "[depth=%d]", 294 xn->u.mailaddr.user, xn->u.mailaddr.domain, xn->depth); 295 296 /* Pass the node through the ruleset */ 297 ep = lks->envelope; 298 ep.dest = xn->u.mailaddr; 299 if (xn->parent) /* nodes with parent are forward addresses */ 300 ep.flags |= EF_INTERNAL; 301 rule = ruleset_match(&ep); 302 if (rule == NULL || rule->r_decision == R_REJECT) { 303 lks->error = (errno == EAGAIN) ? 304 LKA_TEMPFAIL : LKA_PERMFAIL; 305 break; 306 } 307 308 xn->mapping = rule->r_mapping; 309 xn->userbase = rule->r_userbase; 310 311 if (rule->r_action == A_RELAY || rule->r_action == A_RELAYVIA) { 312 lka_submit(lks, rule, xn); 313 } 314 else if (rule->r_desttype == DEST_VDOM) { 315 /* expand */ 316 lks->expand.rule = rule; 317 lks->expand.parent = xn; 318 lks->expand.alias = 1; 319 320 /* temporary replace the mailaddr with a copy where 321 * we eventually strip the '+'-part before lookup. 322 */ 323 maddr = xn->u.mailaddr; 324 xlowercase(maddr.user, xn->u.mailaddr.user, 325 sizeof maddr.user); 326 r = aliases_virtual_get(&lks->expand, &maddr); 327 if (r == -1) { 328 lks->error = LKA_TEMPFAIL; 329 log_trace(TRACE_EXPAND, "expand: lka_expand: " 330 "error in virtual alias lookup"); 331 } 332 else if (r == 0) { 333 lks->error = LKA_PERMFAIL; 334 log_trace(TRACE_EXPAND, "expand: lka_expand: " 335 "no aliases for virtual"); 336 } 337 } 338 else { 339 lks->expand.rule = rule; 340 lks->expand.parent = xn; 341 lks->expand.alias = 1; 342 memset(&node, 0, sizeof node); 343 node.type = EXPAND_USERNAME; 344 xlowercase(node.u.user, xn->u.mailaddr.user, 345 sizeof node.u.user); 346 node.mapping = rule->r_mapping; 347 node.userbase = rule->r_userbase; 348 expand_insert(&lks->expand, &node); 349 } 350 break; 351 352 case EXPAND_USERNAME: 353 log_trace(TRACE_EXPAND, "expand: lka_expand: username: %s " 354 "[depth=%d]", xn->u.user, xn->depth); 355 356 if (xn->sameuser) { 357 log_trace(TRACE_EXPAND, "expand: lka_expand: same " 358 "user, submitting"); 359 lka_submit(lks, rule, xn); 360 break; 361 } 362 363 /* expand aliases with the given rule */ 364 lks->expand.rule = rule; 365 lks->expand.parent = xn; 366 lks->expand.alias = 1; 367 xn->mapping = rule->r_mapping; 368 xn->userbase = rule->r_userbase; 369 if (rule->r_mapping) { 370 r = aliases_get(&lks->expand, xn->u.user); 371 if (r == -1) { 372 log_trace(TRACE_EXPAND, "expand: lka_expand: " 373 "error in alias lookup"); 374 lks->error = LKA_TEMPFAIL; 375 } 376 if (r) 377 break; 378 } 379 380 /* gilles+hackers@ -> gilles@ */ 381 if ((tag = strchr(xn->u.user, *env->sc_subaddressing_delim)) != NULL) 382 *tag++ = '\0'; 383 384 r = table_lookup(rule->r_userbase, NULL, xn->u.user, K_USERINFO, &lk); 385 if (r == -1) { 386 log_trace(TRACE_EXPAND, "expand: lka_expand: " 387 "backend error while searching user"); 388 lks->error = LKA_TEMPFAIL; 389 break; 390 } 391 if (r == 0) { 392 log_trace(TRACE_EXPAND, "expand: lka_expand: " 393 "user-part does not match system user"); 394 lks->error = LKA_PERMFAIL; 395 break; 396 } 397 398 /* no aliases found, query forward file */ 399 lks->rule = rule; 400 lks->node = xn; 401 402 memset(&fwreq, 0, sizeof(fwreq)); 403 fwreq.id = lks->id; 404 (void)strlcpy(fwreq.user, lk.userinfo.username, sizeof(fwreq.user)); 405 (void)strlcpy(fwreq.directory, lk.userinfo.directory, sizeof(fwreq.directory)); 406 fwreq.uid = lk.userinfo.uid; 407 fwreq.gid = lk.userinfo.gid; 408 409 m_compose(p_parent, IMSG_LKA_OPEN_FORWARD, 0, 0, -1, 410 &fwreq, sizeof(fwreq)); 411 lks->flags |= F_WAITING; 412 break; 413 414 case EXPAND_FILENAME: 415 if (rule->r_forwardonly) { 416 log_trace(TRACE_EXPAND, "expand: filename matched on forward-only rule"); 417 lks->error = LKA_TEMPFAIL; 418 break; 419 } 420 log_trace(TRACE_EXPAND, "expand: lka_expand: filename: %s " 421 "[depth=%d]", xn->u.buffer, xn->depth); 422 lka_submit(lks, rule, xn); 423 break; 424 425 case EXPAND_ERROR: 426 if (rule->r_forwardonly) { 427 log_trace(TRACE_EXPAND, "expand: error matched on forward-only rule"); 428 lks->error = LKA_TEMPFAIL; 429 break; 430 } 431 log_trace(TRACE_EXPAND, "expand: lka_expand: error: %s " 432 "[depth=%d]", xn->u.buffer, xn->depth); 433 if (xn->u.buffer[0] == '4') 434 lks->error = LKA_TEMPFAIL; 435 else if (xn->u.buffer[0] == '5') 436 lks->error = LKA_PERMFAIL; 437 lks->errormsg = xn->u.buffer; 438 break; 439 440 case EXPAND_FILTER: 441 if (rule->r_forwardonly) { 442 log_trace(TRACE_EXPAND, "expand: filter matched on forward-only rule"); 443 lks->error = LKA_TEMPFAIL; 444 break; 445 } 446 log_trace(TRACE_EXPAND, "expand: lka_expand: filter: %s " 447 "[depth=%d]", xn->u.buffer, xn->depth); 448 lka_submit(lks, rule, xn); 449 break; 450 451 case EXPAND_MAILDIR: 452 log_trace(TRACE_EXPAND, "expand: lka_expand: maildir: %s " 453 "[depth=%d]", xn->u.buffer, xn->depth); 454 r = table_lookup(rule->r_userbase, NULL, 455 xn->parent->u.user, K_USERINFO, &lk); 456 if (r == -1) { 457 log_trace(TRACE_EXPAND, "expand: lka_expand: maildir: " 458 "backend error while searching user"); 459 lks->error = LKA_TEMPFAIL; 460 break; 461 } 462 if (r == 0) { 463 log_trace(TRACE_EXPAND, "expand: lka_expand: maildir: " 464 "user-part does not match system user"); 465 lks->error = LKA_PERMFAIL; 466 break; 467 } 468 469 lka_submit(lks, rule, xn); 470 break; 471 } 472 } 473 474 static struct expandnode * 475 lka_find_ancestor(struct expandnode *xn, enum expand_type type) 476 { 477 while (xn && (xn->type != type)) 478 xn = xn->parent; 479 if (xn == NULL) { 480 log_warnx("warn: lka_find_ancestor: no ancestors of type %d", 481 type); 482 fatalx(NULL); 483 } 484 return (xn); 485 } 486 487 static void 488 lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn) 489 { 490 union lookup lk; 491 struct envelope *ep; 492 struct expandnode *xn2; 493 int r; 494 495 ep = xmemdup(&lks->envelope, sizeof *ep, "lka_submit"); 496 ep->expire = rule->r_qexpire; 497 498 switch (rule->r_action) { 499 case A_RELAY: 500 case A_RELAYVIA: 501 if (xn->type != EXPAND_ADDRESS) 502 fatalx("lka_deliver: expect address"); 503 ep->type = D_MTA; 504 ep->dest = xn->u.mailaddr; 505 ep->agent.mta.relay = rule->r_value.relayhost; 506 507 /* only rewrite if not a bounce */ 508 if (ep->sender.user[0] && rule->r_as && rule->r_as->user[0]) 509 (void)strlcpy(ep->sender.user, rule->r_as->user, 510 sizeof ep->sender.user); 511 if (ep->sender.user[0] && rule->r_as && rule->r_as->domain[0]) 512 (void)strlcpy(ep->sender.domain, rule->r_as->domain, 513 sizeof ep->sender.domain); 514 break; 515 case A_NONE: 516 case A_MBOX: 517 case A_MAILDIR: 518 case A_FILENAME: 519 case A_MDA: 520 case A_LMTP: 521 ep->type = D_MDA; 522 ep->dest = lka_find_ancestor(xn, EXPAND_ADDRESS)->u.mailaddr; 523 524 /* set username */ 525 if ((xn->type == EXPAND_FILTER || xn->type == EXPAND_FILENAME) 526 && xn->alias) { 527 (void)strlcpy(ep->agent.mda.username, SMTPD_USER, 528 sizeof(ep->agent.mda.username)); 529 } 530 else { 531 xn2 = lka_find_ancestor(xn, EXPAND_USERNAME); 532 (void)strlcpy(ep->agent.mda.username, xn2->u.user, 533 sizeof(ep->agent.mda.username)); 534 } 535 536 r = table_lookup(rule->r_userbase, NULL, ep->agent.mda.username, 537 K_USERINFO, &lk); 538 if (r <= 0) { 539 lks->error = (r == -1) ? LKA_TEMPFAIL : LKA_PERMFAIL; 540 free(ep); 541 return; 542 } 543 (void)strlcpy(ep->agent.mda.usertable, rule->r_userbase->t_name, 544 sizeof ep->agent.mda.usertable); 545 (void)strlcpy(ep->agent.mda.username, lk.userinfo.username, 546 sizeof ep->agent.mda.username); 547 strlcpy(ep->agent.mda.delivery_user, rule->r_delivery_user, 548 sizeof ep->agent.mda.delivery_user); 549 550 if (xn->type == EXPAND_FILENAME) { 551 ep->agent.mda.method = A_FILENAME; 552 (void)strlcpy(ep->agent.mda.buffer, xn->u.buffer, 553 sizeof ep->agent.mda.buffer); 554 } 555 else if (xn->type == EXPAND_FILTER) { 556 ep->agent.mda.method = A_MDA; 557 (void)strlcpy(ep->agent.mda.buffer, xn->u.buffer, 558 sizeof ep->agent.mda.buffer); 559 } 560 else if (xn->type == EXPAND_USERNAME) { 561 ep->agent.mda.method = rule->r_action; 562 (void)strlcpy(ep->agent.mda.buffer, rule->r_value.buffer, 563 sizeof ep->agent.mda.buffer); 564 } 565 else if (xn->type == EXPAND_MAILDIR) { 566 ep->agent.mda.method = A_MAILDIR; 567 (void)strlcpy(ep->agent.mda.buffer, xn->u.buffer, 568 sizeof ep->agent.mda.buffer); 569 } 570 else 571 fatalx("lka_deliver: bad node type"); 572 573 r = lka_expand_format(ep->agent.mda.buffer, 574 sizeof(ep->agent.mda.buffer), ep, &lk.userinfo); 575 if (!r) { 576 lks->error = LKA_TEMPFAIL; 577 log_warnx("warn: format string error while" 578 " expanding for user %s", ep->agent.mda.username); 579 free(ep); 580 return; 581 } 582 break; 583 default: 584 fatalx("lka_submit: bad rule action"); 585 } 586 587 TAILQ_INSERT_TAIL(&lks->deliverylist, ep, entry); 588 } 589 590 591 static size_t 592 lka_expand_token(char *dest, size_t len, const char *token, 593 const struct envelope *ep, const struct userinfo *ui) 594 { 595 char rtoken[MAXTOKENLEN]; 596 char tmp[EXPAND_BUFFER]; 597 const char *string; 598 char *lbracket, *rbracket, *content, *sep, *mods; 599 ssize_t i; 600 ssize_t begoff, endoff; 601 const char *errstr = NULL; 602 int replace = 1; 603 int raw = 0; 604 605 begoff = 0; 606 endoff = EXPAND_BUFFER; 607 mods = NULL; 608 609 if (strlcpy(rtoken, token, sizeof rtoken) >= sizeof rtoken) 610 return 0; 611 612 /* token[x[:y]] -> extracts optional x and y, converts into offsets */ 613 if ((lbracket = strchr(rtoken, '[')) && 614 (rbracket = strchr(rtoken, ']'))) { 615 /* ] before [ ... or empty */ 616 if (rbracket < lbracket || rbracket - lbracket <= 1) 617 return 0; 618 619 *lbracket = *rbracket = '\0'; 620 content = lbracket + 1; 621 622 if ((sep = strchr(content, ':')) == NULL) 623 endoff = begoff = strtonum(content, -EXPAND_BUFFER, 624 EXPAND_BUFFER, &errstr); 625 else { 626 *sep = '\0'; 627 if (content != sep) 628 begoff = strtonum(content, -EXPAND_BUFFER, 629 EXPAND_BUFFER, &errstr); 630 if (*(++sep)) { 631 if (errstr == NULL) 632 endoff = strtonum(sep, -EXPAND_BUFFER, 633 EXPAND_BUFFER, &errstr); 634 } 635 } 636 if (errstr) 637 return 0; 638 639 /* token:mod_1,mod_2,mod_n -> extract modifiers */ 640 mods = strchr(rbracket + 1, ':'); 641 } else { 642 if ((mods = strchr(rtoken, ':')) != NULL) 643 *mods++ = '\0'; 644 } 645 646 /* token -> expanded token */ 647 if (!strcasecmp("sender", rtoken)) { 648 if (snprintf(tmp, sizeof tmp, "%s@%s", 649 ep->sender.user, ep->sender.domain) >= (int)sizeof tmp) 650 return 0; 651 string = tmp; 652 } 653 else if (!strcasecmp("dest", rtoken)) { 654 if (snprintf(tmp, sizeof tmp, "%s@%s", 655 ep->dest.user, ep->dest.domain) >= (int)sizeof tmp) 656 return 0; 657 string = tmp; 658 } 659 else if (!strcasecmp("rcpt", rtoken)) { 660 if (snprintf(tmp, sizeof tmp, "%s@%s", 661 ep->rcpt.user, ep->rcpt.domain) >= (int)sizeof tmp) 662 return 0; 663 string = tmp; 664 } 665 else if (!strcasecmp("sender.user", rtoken)) 666 string = ep->sender.user; 667 else if (!strcasecmp("sender.domain", rtoken)) 668 string = ep->sender.domain; 669 else if (!strcasecmp("user.username", rtoken)) 670 string = ui->username; 671 else if (!strcasecmp("user.directory", rtoken)) { 672 string = ui->directory; 673 replace = 0; 674 } 675 else if (!strcasecmp("dest.user", rtoken)) 676 string = ep->dest.user; 677 else if (!strcasecmp("dest.domain", rtoken)) 678 string = ep->dest.domain; 679 else if (!strcasecmp("rcpt.user", rtoken)) 680 string = ep->rcpt.user; 681 else if (!strcasecmp("rcpt.domain", rtoken)) 682 string = ep->rcpt.domain; 683 else 684 return 0; 685 686 if (string != tmp) { 687 if (strlcpy(tmp, string, sizeof tmp) >= sizeof tmp) 688 return 0; 689 string = tmp; 690 } 691 692 /* apply modifiers */ 693 if (mods != NULL) { 694 do { 695 if ((sep = strchr(mods, '|')) != NULL) 696 *sep++ = '\0'; 697 for (i = 0; (size_t)i < nitems(token_modifiers); ++i) { 698 if (!strcasecmp(token_modifiers[i].name, mods)) { 699 if (token_modifiers[i].f == NULL) { 700 raw = 1; 701 break; 702 } 703 if (!token_modifiers[i].f(tmp, sizeof tmp)) 704 return 0; /* modifier error */ 705 break; 706 } 707 } 708 if ((size_t)i == nitems(token_modifiers)) 709 return 0; /* modifier not found */ 710 } while ((mods = sep) != NULL); 711 } 712 713 if (!raw && replace) 714 for (i = 0; (size_t)i < strlen(tmp); ++i) 715 if (strchr(MAILADDR_ESCAPE, tmp[i])) 716 tmp[i] = ':'; 717 718 /* expanded string is empty */ 719 i = strlen(string); 720 if (i == 0) 721 return 0; 722 723 /* begin offset beyond end of string */ 724 if (begoff >= i) 725 return 0; 726 727 /* end offset beyond end of string, make it end of string */ 728 if (endoff >= i) 729 endoff = i - 1; 730 731 /* negative begin offset, make it relative to end of string */ 732 if (begoff < 0) 733 begoff += i; 734 /* negative end offset, make it relative to end of string, 735 * note that end offset is inclusive. 736 */ 737 if (endoff < 0) 738 endoff += i - 1; 739 740 /* check that final offsets are valid */ 741 if (begoff < 0 || endoff < 0 || endoff < begoff) 742 return 0; 743 endoff += 1; /* end offset is inclusive */ 744 745 /* check that substring does not exceed destination buffer length */ 746 i = endoff - begoff; 747 if ((size_t)i + 1 >= len) 748 return 0; 749 750 string += begoff; 751 for (; i; i--) { 752 *dest = (replace && *string == '/') ? ':' : *string; 753 dest++; 754 string++; 755 } 756 757 return endoff - begoff; 758 } 759 760 761 static size_t 762 lka_expand_format(char *buf, size_t len, const struct envelope *ep, 763 const struct userinfo *ui) 764 { 765 char tmpbuf[EXPAND_BUFFER], *ptmp, *pbuf, *ebuf; 766 char exptok[EXPAND_BUFFER]; 767 size_t exptoklen; 768 char token[MAXTOKENLEN]; 769 size_t ret, tmpret; 770 771 if (len < sizeof tmpbuf) { 772 log_warnx("lka_expand_format: tmp buffer < rule buffer"); 773 return 0; 774 } 775 776 memset(tmpbuf, 0, sizeof tmpbuf); 777 pbuf = buf; 778 ptmp = tmpbuf; 779 ret = tmpret = 0; 780 781 /* special case: ~/ only allowed expanded at the beginning */ 782 if (strncmp(pbuf, "~/", 2) == 0) { 783 tmpret = snprintf(ptmp, sizeof tmpbuf, "%s/", ui->directory); 784 if (tmpret >= sizeof tmpbuf) { 785 log_warnx("warn: user directory for %s too large", 786 ui->directory); 787 return 0; 788 } 789 ret += tmpret; 790 ptmp += tmpret; 791 pbuf += 2; 792 } 793 794 795 /* expansion loop */ 796 for (; *pbuf && ret < sizeof tmpbuf; ret += tmpret) { 797 if (*pbuf == '%' && *(pbuf + 1) == '%') { 798 *ptmp++ = *pbuf++; 799 pbuf += 1; 800 tmpret = 1; 801 continue; 802 } 803 804 if (*pbuf != '%' || *(pbuf + 1) != '{') { 805 *ptmp++ = *pbuf++; 806 tmpret = 1; 807 continue; 808 } 809 810 /* %{...} otherwise fail */ 811 if (*(pbuf+1) != '{' || (ebuf = strchr(pbuf+1, '}')) == NULL) 812 return 0; 813 814 /* extract token from %{token} */ 815 if ((size_t)(ebuf - pbuf) - 1 >= sizeof token) 816 return 0; 817 818 memcpy(token, pbuf+2, ebuf-pbuf-1); 819 if (strchr(token, '}') == NULL) 820 return 0; 821 *strchr(token, '}') = '\0'; 822 823 exptoklen = lka_expand_token(exptok, sizeof exptok, token, ep, 824 ui); 825 if (exptoklen == 0) 826 return 0; 827 828 /* writing expanded token at ptmp will overflow tmpbuf */ 829 if (sizeof (tmpbuf) - (ptmp - tmpbuf) <= exptoklen) 830 return 0; 831 832 memcpy(ptmp, exptok, exptoklen); 833 pbuf = ebuf + 1; 834 ptmp += exptoklen; 835 tmpret = exptoklen; 836 } 837 if (ret >= sizeof tmpbuf) 838 return 0; 839 840 if ((ret = strlcpy(buf, tmpbuf, len)) >= len) 841 return 0; 842 843 return ret; 844 } 845 846 static int 847 mod_lowercase(char *buf, size_t len) 848 { 849 char tmp[EXPAND_BUFFER]; 850 851 if (!lowercase(tmp, buf, sizeof tmp)) 852 return 0; 853 if (strlcpy(buf, tmp, len) >= len) 854 return 0; 855 return 1; 856 } 857 858 static int 859 mod_uppercase(char *buf, size_t len) 860 { 861 char tmp[EXPAND_BUFFER]; 862 863 if (!uppercase(tmp, buf, sizeof tmp)) 864 return 0; 865 if (strlcpy(buf, tmp, len) >= len) 866 return 0; 867 return 1; 868 } 869 870 static int 871 mod_strip(char *buf, size_t len) 872 { 873 char *tag, *at; 874 unsigned int i; 875 876 /* gilles+hackers -> gilles */ 877 if ((tag = strchr(buf, *env->sc_subaddressing_delim)) != NULL) { 878 /* gilles+hackers@poolp.org -> gilles@poolp.org */ 879 if ((at = strchr(tag, '@')) != NULL) { 880 for (i = 0; i <= strlen(at); ++i) 881 tag[i] = at[i]; 882 } else 883 *tag = '\0'; 884 } 885 return 1; 886 } 887