1 /* $OpenBSD: lka_session.c,v 1.68 2014/07/08 13:49:09 eric 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 40 #include "smtpd.h" 41 #include "log.h" 42 43 #define EXPAND_DEPTH 10 44 45 #define F_WAITING 0x01 46 47 struct lka_session { 48 uint64_t id; /* given by smtp */ 49 50 TAILQ_HEAD(, envelope) deliverylist; 51 struct expand expand; 52 53 int flags; 54 int error; 55 const char *errormsg; 56 struct envelope envelope; 57 struct xnodes nodes; 58 /* waiting for fwdrq */ 59 struct rule *rule; 60 struct expandnode *node; 61 }; 62 63 static void lka_expand(struct lka_session *, struct rule *, 64 struct expandnode *); 65 static void lka_submit(struct lka_session *, struct rule *, 66 struct expandnode *); 67 static void lka_resume(struct lka_session *); 68 static size_t lka_expand_format(char *, size_t, const struct envelope *, 69 const struct userinfo *); 70 static void mailaddr_to_username(const struct mailaddr *, char *, size_t); 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 277 if (xn->depth >= EXPAND_DEPTH) { 278 log_trace(TRACE_EXPAND, "expand: lka_expand: node too deep."); 279 lks->error = LKA_PERMFAIL; 280 return; 281 } 282 283 switch (xn->type) { 284 case EXPAND_INVALID: 285 case EXPAND_INCLUDE: 286 fatalx("lka_expand: unexpected type"); 287 break; 288 289 case EXPAND_ADDRESS: 290 291 log_trace(TRACE_EXPAND, "expand: lka_expand: address: %s@%s " 292 "[depth=%d]", 293 xn->u.mailaddr.user, xn->u.mailaddr.domain, xn->depth); 294 295 /* Pass the node through the ruleset */ 296 ep = lks->envelope; 297 ep.dest = xn->u.mailaddr; 298 if (xn->parent) /* nodes with parent are forward addresses */ 299 ep.flags |= EF_INTERNAL; 300 rule = ruleset_match(&ep); 301 if (rule == NULL || rule->r_decision == R_REJECT) { 302 lks->error = (errno == EAGAIN) ? 303 LKA_TEMPFAIL : LKA_PERMFAIL; 304 break; 305 } 306 307 xn->mapping = rule->r_mapping; 308 xn->userbase = rule->r_userbase; 309 310 if (rule->r_action == A_RELAY || rule->r_action == A_RELAYVIA) { 311 lka_submit(lks, rule, xn); 312 } 313 else if (rule->r_desttype == DEST_VDOM) { 314 /* expand */ 315 lks->expand.rule = rule; 316 lks->expand.parent = xn; 317 lks->expand.alias = 1; 318 319 /* temporary replace the mailaddr with a copy where 320 * we eventually strip the '+'-part before lookup. 321 */ 322 maddr = xn->u.mailaddr; 323 mailaddr_to_username(&xn->u.mailaddr, maddr.user, 324 sizeof maddr.user); 325 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 mailaddr_to_username(&xn->u.mailaddr, node.u.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 /* A username should not exceed the size of a system user */ 381 if (strlen(xn->u.user) >= sizeof fwreq.user) { 382 log_trace(TRACE_EXPAND, "expand: lka_expand: " 383 "user-part too long to be a system user"); 384 lks->error = LKA_PERMFAIL; 385 break; 386 } 387 388 r = table_lookup(rule->r_userbase, NULL, xn->u.user, K_USERINFO, &lk); 389 if (r == -1) { 390 log_trace(TRACE_EXPAND, "expand: lka_expand: " 391 "backend error while searching user"); 392 lks->error = LKA_TEMPFAIL; 393 break; 394 } 395 if (r == 0) { 396 log_trace(TRACE_EXPAND, "expand: lka_expand: " 397 "user-part does not match system user"); 398 lks->error = LKA_PERMFAIL; 399 break; 400 } 401 402 /* no aliases found, query forward file */ 403 lks->rule = rule; 404 lks->node = xn; 405 406 memset(&fwreq, 0, sizeof(fwreq)); 407 fwreq.id = lks->id; 408 (void)strlcpy(fwreq.user, lk.userinfo.username, sizeof(fwreq.user)); 409 (void)strlcpy(fwreq.directory, lk.userinfo.directory, sizeof(fwreq.directory)); 410 fwreq.uid = lk.userinfo.uid; 411 fwreq.gid = lk.userinfo.gid; 412 413 m_compose(p_parent, IMSG_LKA_OPEN_FORWARD, 0, 0, -1, 414 &fwreq, sizeof(fwreq)); 415 lks->flags |= F_WAITING; 416 break; 417 418 case EXPAND_FILENAME: 419 if (rule->r_forwardonly) { 420 log_trace(TRACE_EXPAND, "expand: filename matched on forward-only rule"); 421 lks->error = LKA_TEMPFAIL; 422 break; 423 } 424 log_trace(TRACE_EXPAND, "expand: lka_expand: filename: %s " 425 "[depth=%d]", xn->u.buffer, xn->depth); 426 lka_submit(lks, rule, xn); 427 break; 428 429 case EXPAND_ERROR: 430 if (rule->r_forwardonly) { 431 log_trace(TRACE_EXPAND, "expand: error matched on forward-only rule"); 432 lks->error = LKA_TEMPFAIL; 433 break; 434 } 435 log_trace(TRACE_EXPAND, "expand: lka_expand: error: %s " 436 "[depth=%d]", xn->u.buffer, xn->depth); 437 if (xn->u.buffer[0] == '4') 438 lks->error = LKA_TEMPFAIL; 439 else if (xn->u.buffer[0] == '5') 440 lks->error = LKA_PERMFAIL; 441 lks->errormsg = xn->u.buffer; 442 break; 443 444 case EXPAND_FILTER: 445 if (rule->r_forwardonly) { 446 log_trace(TRACE_EXPAND, "expand: filter matched on forward-only rule"); 447 lks->error = LKA_TEMPFAIL; 448 break; 449 } 450 log_trace(TRACE_EXPAND, "expand: lka_expand: filter: %s " 451 "[depth=%d]", xn->u.buffer, xn->depth); 452 lka_submit(lks, rule, xn); 453 break; 454 } 455 } 456 457 static struct expandnode * 458 lka_find_ancestor(struct expandnode *xn, enum expand_type type) 459 { 460 while (xn && (xn->type != type)) 461 xn = xn->parent; 462 if (xn == NULL) { 463 log_warnx("warn: lka_find_ancestor: no ancestors of type %d", 464 type); 465 fatalx(NULL); 466 } 467 return (xn); 468 } 469 470 static void 471 lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn) 472 { 473 union lookup lk; 474 struct envelope *ep; 475 struct expandnode *xn2; 476 int r; 477 478 ep = xmemdup(&lks->envelope, sizeof *ep, "lka_submit"); 479 ep->expire = rule->r_qexpire; 480 481 switch (rule->r_action) { 482 case A_RELAY: 483 case A_RELAYVIA: 484 if (xn->type != EXPAND_ADDRESS) 485 fatalx("lka_deliver: expect address"); 486 ep->type = D_MTA; 487 ep->dest = xn->u.mailaddr; 488 ep->agent.mta.relay = rule->r_value.relayhost; 489 490 /* only rewrite if not a bounce */ 491 if (ep->sender.user[0] && rule->r_as && rule->r_as->user[0]) 492 (void)strlcpy(ep->sender.user, rule->r_as->user, 493 sizeof ep->sender.user); 494 if (ep->sender.user[0] && rule->r_as && rule->r_as->domain[0]) 495 (void)strlcpy(ep->sender.domain, rule->r_as->domain, 496 sizeof ep->sender.domain); 497 break; 498 case A_NONE: 499 case A_MBOX: 500 case A_MAILDIR: 501 case A_FILENAME: 502 case A_MDA: 503 case A_LMTP: 504 ep->type = D_MDA; 505 ep->dest = lka_find_ancestor(xn, EXPAND_ADDRESS)->u.mailaddr; 506 507 /* set username */ 508 if ((xn->type == EXPAND_FILTER || xn->type == EXPAND_FILENAME) 509 && xn->alias) { 510 (void)strlcpy(ep->agent.mda.username, SMTPD_USER, 511 sizeof(ep->agent.mda.username)); 512 } 513 else { 514 xn2 = lka_find_ancestor(xn, EXPAND_USERNAME); 515 (void)strlcpy(ep->agent.mda.username, xn2->u.user, 516 sizeof(ep->agent.mda.username)); 517 } 518 519 r = table_lookup(rule->r_userbase, NULL, ep->agent.mda.username, 520 K_USERINFO, &lk); 521 if (r <= 0) { 522 lks->error = (r == -1) ? LKA_TEMPFAIL : LKA_PERMFAIL; 523 free(ep); 524 return; 525 } 526 (void)strlcpy(ep->agent.mda.usertable, rule->r_userbase->t_name, 527 sizeof ep->agent.mda.usertable); 528 (void)strlcpy(ep->agent.mda.username, lk.userinfo.username, 529 sizeof ep->agent.mda.username); 530 531 if (xn->type == EXPAND_FILENAME) { 532 ep->agent.mda.method = A_FILENAME; 533 (void)strlcpy(ep->agent.mda.buffer, xn->u.buffer, 534 sizeof ep->agent.mda.buffer); 535 } 536 else if (xn->type == EXPAND_FILTER) { 537 ep->agent.mda.method = A_MDA; 538 (void)strlcpy(ep->agent.mda.buffer, xn->u.buffer, 539 sizeof ep->agent.mda.buffer); 540 } 541 else if (xn->type == EXPAND_USERNAME) { 542 ep->agent.mda.method = rule->r_action; 543 (void)strlcpy(ep->agent.mda.buffer, rule->r_value.buffer, 544 sizeof ep->agent.mda.buffer); 545 } 546 else 547 fatalx("lka_deliver: bad node type"); 548 549 r = lka_expand_format(ep->agent.mda.buffer, 550 sizeof(ep->agent.mda.buffer), ep, &lk.userinfo); 551 if (!r) { 552 lks->error = LKA_TEMPFAIL; 553 log_warnx("warn: format string error while" 554 " expanding for user %s", ep->agent.mda.username); 555 free(ep); 556 return; 557 } 558 break; 559 default: 560 fatalx("lka_submit: bad rule action"); 561 } 562 563 TAILQ_INSERT_TAIL(&lks->deliverylist, ep, entry); 564 } 565 566 567 static size_t 568 lka_expand_token(char *dest, size_t len, const char *token, 569 const struct envelope *ep, const struct userinfo *ui) 570 { 571 char rtoken[MAXTOKENLEN]; 572 char tmp[EXPAND_BUFFER]; 573 const char *string; 574 char *lbracket, *rbracket, *content, *sep, *mods; 575 ssize_t i; 576 ssize_t begoff, endoff; 577 const char *errstr = NULL; 578 int replace = 1; 579 int raw = 0; 580 581 begoff = 0; 582 endoff = EXPAND_BUFFER; 583 mods = NULL; 584 585 if (strlcpy(rtoken, token, sizeof rtoken) >= sizeof rtoken) 586 return 0; 587 588 /* token[x[:y]] -> extracts optional x and y, converts into offsets */ 589 if ((lbracket = strchr(rtoken, '[')) && 590 (rbracket = strchr(rtoken, ']'))) { 591 /* ] before [ ... or empty */ 592 if (rbracket < lbracket || rbracket - lbracket <= 1) 593 return 0; 594 595 *lbracket = *rbracket = '\0'; 596 content = lbracket + 1; 597 598 if ((sep = strchr(content, ':')) == NULL) 599 endoff = begoff = strtonum(content, -EXPAND_BUFFER, 600 EXPAND_BUFFER, &errstr); 601 else { 602 *sep = '\0'; 603 if (content != sep) 604 begoff = strtonum(content, -EXPAND_BUFFER, 605 EXPAND_BUFFER, &errstr); 606 if (*(++sep)) { 607 if (errstr == NULL) 608 endoff = strtonum(sep, -EXPAND_BUFFER, 609 EXPAND_BUFFER, &errstr); 610 } 611 } 612 if (errstr) 613 return 0; 614 615 /* token:mod_1,mod_2,mod_n -> extract modifiers */ 616 mods = strchr(rbracket + 1, ':'); 617 } else { 618 if ((mods = strchr(rtoken, ':')) != NULL) 619 *mods++ = '\0'; 620 } 621 622 /* token -> expanded token */ 623 if (! strcasecmp("sender", rtoken)) { 624 if (snprintf(tmp, sizeof tmp, "%s@%s", 625 ep->sender.user, ep->sender.domain) <= 0) 626 return 0; 627 string = tmp; 628 } 629 else if (! strcasecmp("dest", rtoken)) { 630 if (snprintf(tmp, sizeof tmp, "%s@%s", 631 ep->dest.user, ep->dest.domain) <= 0) 632 return 0; 633 string = tmp; 634 } 635 else if (! strcasecmp("rcpt", rtoken)) { 636 if (snprintf(tmp, sizeof tmp, "%s@%s", 637 ep->rcpt.user, ep->rcpt.domain) <= 0) 638 return 0; 639 string = tmp; 640 } 641 else if (! strcasecmp("sender.user", rtoken)) 642 string = ep->sender.user; 643 else if (! strcasecmp("sender.domain", rtoken)) 644 string = ep->sender.domain; 645 else if (! strcasecmp("user.username", rtoken)) 646 string = ui->username; 647 else if (! strcasecmp("user.directory", rtoken)) { 648 string = ui->directory; 649 replace = 0; 650 } 651 else if (! strcasecmp("dest.user", rtoken)) 652 string = ep->dest.user; 653 else if (! strcasecmp("dest.domain", rtoken)) 654 string = ep->dest.domain; 655 else if (! strcasecmp("rcpt.user", rtoken)) 656 string = ep->rcpt.user; 657 else if (! strcasecmp("rcpt.domain", rtoken)) 658 string = ep->rcpt.domain; 659 else 660 return 0; 661 662 if (string != tmp) { 663 if (strlcpy(tmp, string, sizeof tmp) >= sizeof tmp) 664 return 0; 665 string = tmp; 666 } 667 668 /* apply modifiers */ 669 if (mods != NULL) { 670 do { 671 if ((sep = strchr(mods, '|')) != NULL) 672 *sep++ = '\0'; 673 for (i = 0; (size_t)i < nitems(token_modifiers); ++i) { 674 if (! strcasecmp(token_modifiers[i].name, mods)) { 675 if (token_modifiers[i].f == NULL) { 676 raw = 1; 677 break; 678 } 679 if (! token_modifiers[i].f(tmp, sizeof tmp)) 680 return 0; /* modifier error */ 681 break; 682 } 683 } 684 if ((size_t)i == nitems(token_modifiers)) 685 return 0; /* modifier not found */ 686 } while ((mods = sep) != NULL); 687 } 688 689 if (! raw && replace) 690 for (i = 0; (size_t)i < strlen(tmp); ++i) 691 if (strchr(MAILADDR_ESCAPE, tmp[i])) 692 tmp[i] = ':'; 693 694 /* expanded string is empty */ 695 i = strlen(string); 696 if (i == 0) 697 return 0; 698 699 /* begin offset beyond end of string */ 700 if (begoff >= i) 701 return 0; 702 703 /* end offset beyond end of string, make it end of string */ 704 if (endoff >= i) 705 endoff = i - 1; 706 707 /* negative begin offset, make it relative to end of string */ 708 if (begoff < 0) 709 begoff += i; 710 /* negative end offset, make it relative to end of string, 711 * note that end offset is inclusive. 712 */ 713 if (endoff < 0) 714 endoff += i - 1; 715 716 /* check that final offsets are valid */ 717 if (begoff < 0 || endoff < 0 || endoff < begoff) 718 return 0; 719 endoff += 1; /* end offset is inclusive */ 720 721 /* check that substring does not exceed destination buffer length */ 722 i = endoff - begoff; 723 if ((size_t)i + 1 >= len) 724 return 0; 725 726 string += begoff; 727 for (; i; i--) { 728 *dest = (replace && *string == '/') ? ':' : *string; 729 dest++; 730 string++; 731 } 732 733 return endoff - begoff; 734 } 735 736 737 static size_t 738 lka_expand_format(char *buf, size_t len, const struct envelope *ep, 739 const struct userinfo *ui) 740 { 741 char tmpbuf[EXPAND_BUFFER], *ptmp, *pbuf, *ebuf; 742 char exptok[EXPAND_BUFFER]; 743 size_t exptoklen; 744 char token[MAXTOKENLEN]; 745 size_t ret, tmpret; 746 747 if (len < sizeof tmpbuf) 748 fatalx("lka_expand_format: tmp buffer < rule buffer"); 749 750 memset(tmpbuf, 0, sizeof tmpbuf); 751 pbuf = buf; 752 ptmp = tmpbuf; 753 ret = tmpret = 0; 754 755 /* special case: ~/ only allowed expanded at the beginning */ 756 if (strncmp(pbuf, "~/", 2) == 0) { 757 tmpret = snprintf(ptmp, sizeof tmpbuf, "%s/", ui->directory); 758 if (tmpret >= sizeof tmpbuf) { 759 log_warnx("warn: user directory for %s too large", 760 ui->directory); 761 return 0; 762 } 763 ret += tmpret; 764 ptmp += tmpret; 765 pbuf += 2; 766 } 767 768 769 /* expansion loop */ 770 for (; *pbuf && ret < sizeof tmpbuf; ret += tmpret) { 771 if (*pbuf == '%' && *(pbuf + 1) == '%') { 772 *ptmp++ = *pbuf++; 773 pbuf += 1; 774 tmpret = 1; 775 continue; 776 } 777 778 if (*pbuf != '%' || *(pbuf + 1) != '{') { 779 *ptmp++ = *pbuf++; 780 tmpret = 1; 781 continue; 782 } 783 784 /* %{...} otherwise fail */ 785 if (*(pbuf+1) != '{' || (ebuf = strchr(pbuf+1, '}')) == NULL) 786 return 0; 787 788 /* extract token from %{token} */ 789 if ((size_t)(ebuf - pbuf) - 1 >= sizeof token) 790 return 0; 791 792 memcpy(token, pbuf+2, ebuf-pbuf-1); 793 if (strchr(token, '}') == NULL) 794 return 0; 795 *strchr(token, '}') = '\0'; 796 797 exptoklen = lka_expand_token(exptok, sizeof exptok, token, ep, 798 ui); 799 if (exptoklen == 0) 800 return 0; 801 802 memcpy(ptmp, exptok, exptoklen); 803 pbuf = ebuf + 1; 804 ptmp += exptoklen; 805 tmpret = exptoklen; 806 } 807 if (ret >= sizeof tmpbuf) 808 return 0; 809 810 if ((ret = strlcpy(buf, tmpbuf, len)) >= len) 811 return 0; 812 813 return ret; 814 } 815 816 static void 817 mailaddr_to_username(const struct mailaddr *maddr, char *dst, size_t len) 818 { 819 char *tag; 820 821 xlowercase(dst, maddr->user, len); 822 823 /* gilles+hackers@ -> gilles@ */ 824 if ((tag = strchr(dst, TAG_CHAR)) != NULL) 825 *tag++ = '\0'; 826 } 827 828 static int 829 mod_lowercase(char *buf, size_t len) 830 { 831 char tmp[EXPAND_BUFFER]; 832 833 if (! lowercase(tmp, buf, sizeof tmp)) 834 return 0; 835 if (strlcpy(buf, tmp, len) >= len) 836 return 0; 837 return 1; 838 } 839 840 static int 841 mod_uppercase(char *buf, size_t len) 842 { 843 char tmp[EXPAND_BUFFER]; 844 845 if (! uppercase(tmp, buf, sizeof tmp)) 846 return 0; 847 if (strlcpy(buf, tmp, len) >= len) 848 return 0; 849 return 1; 850 } 851 852 static int 853 mod_strip(char *buf, size_t len) 854 { 855 char *tag, *at; 856 unsigned int i; 857 858 /* gilles+hackers -> gilles */ 859 if ((tag = strchr(buf, TAG_CHAR)) != NULL) { 860 /* gilles+hackers@poolp.org -> gilles@poolp.org */ 861 if ((at = strchr(tag, '@')) != NULL) { 862 for (i = 0; i <= strlen(at); ++i) 863 tag[i] = at[i]; 864 } else 865 *tag = '\0'; 866 } 867 return 1; 868 } 869