1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 #ifdef USERDB 11 static char sccsid [] = "@(#)udb.c 6.8 (Berkeley) 02/19/93 (with USERDB)"; 12 #else 13 static char sccsid [] = "@(#)udb.c 6.8 (Berkeley) 02/19/93 (without USERDB)"; 14 #endif 15 #endif 16 17 #include "sendmail.h" 18 19 #ifdef USERDB 20 21 #include <sys/time.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <netdb.h> 25 #include <db.h> 26 27 /* 28 ** UDB.C -- interface between sendmail and Berkeley User Data Base. 29 ** 30 ** This depends on the 4.4BSD db package. 31 */ 32 33 34 struct udbent 35 { 36 char *udb_spec; /* string version of spec */ 37 int udb_type; /* type of entry */ 38 char *udb_default; /* default host for outgoing mail */ 39 union 40 { 41 /* type UE_REMOTE -- do remote call for lookup */ 42 struct 43 { 44 struct sockaddr_in _udb_addr; /* address */ 45 int _udb_timeout; /* timeout */ 46 } udb_remote; 47 #define udb_addr udb_u.udb_remote._udb_addr 48 #define udb_timeout udb_u.udb_remote._udb_timeout 49 50 /* type UE_FORWARD -- forward message to remote */ 51 struct 52 { 53 char *_udb_fwdhost; /* name of forward host */ 54 } udb_forward; 55 #define udb_fwdhost udb_u.udb_forward._udb_fwdhost 56 57 /* type UE_FETCH -- lookup in local database */ 58 struct 59 { 60 char *_udb_dbname; /* pathname of database */ 61 DB *_udb_dbp; /* open database ptr */ 62 } udb_lookup; 63 #define udb_dbname udb_u.udb_lookup._udb_dbname 64 #define udb_dbp udb_u.udb_lookup._udb_dbp 65 } udb_u; 66 }; 67 68 #define UDB_EOLIST 0 /* end of list */ 69 #define UDB_SKIP 1 /* skip this entry */ 70 #define UDB_REMOTE 2 /* look up in remote database */ 71 #define UDB_DBFETCH 3 /* look up in local database */ 72 #define UDB_FORWARD 4 /* forward to remote host */ 73 74 #define MAXUDBENT 10 /* maximum number of UDB entries */ 75 76 77 struct option 78 { 79 char *name; 80 char *val; 81 }; 82 /* 83 ** UDBEXPAND -- look up user in database and expand 84 ** 85 ** Parameters: 86 ** a -- address to expand. 87 ** sendq -- pointer to head of sendq to put the expansions in. 88 ** 89 ** Returns: 90 ** EX_TEMPFAIL -- if something "odd" happened -- probably due 91 ** to accessing a file on an NFS server that is down. 92 ** EX_OK -- otherwise. 93 ** 94 ** Side Effects: 95 ** Modifies sendq. 96 */ 97 98 int UdbPort = 1616; 99 int UdbTimeout = 10; 100 101 struct udbent UdbEnts[MAXUDBENT + 1]; 102 int UdbSock = -1; 103 bool UdbInitialized = FALSE; 104 105 int 106 udbexpand(a, sendq, e) 107 register ADDRESS *a; 108 ADDRESS **sendq; 109 register ENVELOPE *e; 110 { 111 int i; 112 register char *p; 113 DBT key; 114 DBT info; 115 bool breakout; 116 register struct udbent *up; 117 int keylen; 118 char keybuf[MAXKEY]; 119 char buf[BUFSIZ]; 120 121 if (tTd(28, 1)) 122 printf("udbexpand(%s)\n", a->q_paddr); 123 124 /* make certain we are supposed to send to this address */ 125 if (bitset(QDONTSEND, a->q_flags)) 126 return EX_OK; 127 e->e_to = a->q_paddr; 128 129 /* on first call, locate the database */ 130 if (!UdbInitialized) 131 { 132 extern int _udbx_init(); 133 134 if (_udbx_init() == EX_TEMPFAIL) 135 return EX_TEMPFAIL; 136 } 137 138 /* short circuit the process if no chance of a match */ 139 if (UdbSpec == NULL || UdbSpec[0] == '\0') 140 return EX_OK; 141 142 /* if name is too long, assume it won't match */ 143 if (strlen(a->q_user) > sizeof keybuf - 12) 144 return EX_OK; 145 146 /* if name begins with a colon, it indicates our metadata */ 147 if (a->q_user[0] == ':') 148 return EX_OK; 149 150 /* build actual database key */ 151 (void) strcpy(keybuf, a->q_user); 152 (void) strcat(keybuf, ":maildrop"); 153 keylen = strlen(keybuf); 154 155 breakout = FALSE; 156 for (up = UdbEnts; !breakout; up++) 157 { 158 char *user; 159 160 /* 161 ** Select action based on entry type. 162 ** 163 ** On dropping out of this switch, "class" should 164 ** explain the type of the data, and "user" should 165 ** contain the user information. 166 */ 167 168 switch (up->udb_type) 169 { 170 case UDB_DBFETCH: 171 key.data = keybuf; 172 key.size = keylen; 173 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); 174 if (i > 0 || info.size <= 0) 175 { 176 if (tTd(28, 2)) 177 printf("udbexpand: no match on %s\n", keybuf); 178 continue; 179 } 180 181 while (i == 0 && key.size == keylen && 182 bcmp(key.data, keybuf, keylen) == 0) 183 { 184 breakout = TRUE; 185 if (info.size < sizeof buf) 186 user = buf; 187 else 188 user = xalloc(info.size + 1); 189 bcopy(info.data, user, info.size); 190 user[info.size] = '\0'; 191 192 message(Arpa_Info, "expanded to %s", user); 193 #ifdef LOG 194 if (LogLevel >= 10) 195 syslog(LOG_INFO, "%s: expand %s => %s", 196 e->e_id, e->e_to, user); 197 #endif 198 AliasLevel++; 199 sendtolist(user, a, sendq, e); 200 AliasLevel--; 201 202 if (user != buf) 203 free(user); 204 205 /* get the next record */ 206 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); 207 } 208 if (!bitset(QSELFREF, a->q_flags)) 209 { 210 if (tTd(28, 5)) 211 { 212 printf("udbexpand: QDONTSEND "); 213 printaddr(a, FALSE); 214 } 215 a->q_flags |= QDONTSEND; 216 } 217 if (i < 0) 218 { 219 syserr("udbexpand: db-get %.*s stat %d", 220 key.size, key.data, i); 221 return EX_TEMPFAIL; 222 } 223 break; 224 225 case UDB_REMOTE: 226 /* not yet implemented */ 227 continue; 228 229 case UDB_FORWARD: 230 i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; 231 if (i < sizeof buf) 232 user = buf; 233 else 234 user = xalloc(i + 1); 235 (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost); 236 message(Arpa_Info, "expanded to %s", user); 237 AliasLevel++; 238 sendtolist(user, a, sendq, e); 239 AliasLevel--; 240 if (!bitset(QSELFREF, a->q_flags)) 241 { 242 if (tTd(28, 5)) 243 { 244 printf("udbexpand: QDONTSEND "); 245 printaddr(a, FALSE); 246 } 247 a->q_flags |= QDONTSEND; 248 } 249 if (user != buf) 250 free(user); 251 breakout = TRUE; 252 break; 253 254 case UDB_EOLIST: 255 breakout = TRUE; 256 continue; 257 258 default: 259 /* unknown entry type */ 260 continue; 261 } 262 } 263 return EX_OK; 264 } 265 /* 266 ** UDBSENDER -- return canonical external name of sender, given local name 267 ** 268 ** Parameters: 269 ** sender -- the name of the sender on the local machine. 270 ** 271 ** Returns: 272 ** The external name for this sender, if derivable from the 273 ** database. 274 ** NULL -- if nothing is changed from the database. 275 ** 276 ** Side Effects: 277 ** none. 278 */ 279 280 char * 281 udbsender(sender) 282 char *sender; 283 { 284 register char *p; 285 register struct udbent *up; 286 int i; 287 int keylen; 288 DBT key, info; 289 char keybuf[MAXKEY]; 290 291 if (tTd(28, 1)) 292 printf("udbsender(%s)\n", sender); 293 294 if (!UdbInitialized) 295 { 296 if (_udbx_init() == EX_TEMPFAIL) 297 return NULL; 298 } 299 300 /* short circuit if no spec */ 301 if (UdbSpec == NULL || UdbSpec[0] == '\0') 302 return NULL; 303 304 /* long names can never match and are a pain to deal with */ 305 if (strlen(sender) > sizeof keybuf - 12) 306 return NULL; 307 308 /* names beginning with colons indicate metadata */ 309 if (sender[0] == ':') 310 return NULL; 311 312 /* build database key */ 313 (void) strcpy(keybuf, sender); 314 (void) strcat(keybuf, ":mailname"); 315 keylen = strlen(keybuf); 316 317 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 318 { 319 /* 320 ** Select action based on entry type. 321 */ 322 323 switch (up->udb_type) 324 { 325 case UDB_DBFETCH: 326 key.data = keybuf; 327 key.size = keylen; 328 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 329 if (i != 0 || info.size <= 0) 330 { 331 if (tTd(28, 2)) 332 printf("udbsender: no match on %s\n", 333 keybuf); 334 continue; 335 } 336 337 p = xalloc(info.size + 1); 338 bcopy(info.data, p, info.size); 339 p[info.size] = '\0'; 340 if (tTd(28, 1)) 341 printf("udbsender ==> %s\n", p); 342 return p; 343 } 344 } 345 346 /* 347 ** Nothing yet. Search again for a default case. But only 348 ** use it if we also have a forward (:maildrop) pointer already 349 ** in the database. 350 */ 351 352 /* build database key */ 353 (void) strcpy(keybuf, sender); 354 (void) strcat(keybuf, ":maildrop"); 355 keylen = strlen(keybuf); 356 357 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 358 { 359 switch (up->udb_type) 360 { 361 case UDB_DBFETCH: 362 /* get the default case for this database */ 363 if (up->udb_default == NULL) 364 { 365 key.data = ":default:mailname"; 366 key.size = strlen(key.data); 367 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 368 if (i != 0 || info.size <= 0) 369 { 370 /* no default case */ 371 up->udb_default = ""; 372 continue; 373 } 374 375 /* save the default case */ 376 up->udb_default = xalloc(info.size + 1); 377 bcopy(info.data, up->udb_default, info.size); 378 up->udb_default[info.size] = '\0'; 379 } 380 else if (up->udb_default[0] == '\0') 381 continue; 382 383 /* we have a default case -- verify user:maildrop */ 384 key.data = keybuf; 385 key.size = keylen; 386 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 387 if (i != 0 || info.size <= 0) 388 { 389 /* nope -- no aliasing for this user */ 390 continue; 391 } 392 393 /* they exist -- build the actual address */ 394 p = xalloc(strlen(sender) + strlen(up->udb_default) + 2); 395 (void) strcpy(p, sender); 396 (void) strcat(p, "@"); 397 (void) strcat(p, up->udb_default); 398 if (tTd(28, 1)) 399 printf("udbsender ==> %s\n", p); 400 return p; 401 } 402 } 403 404 /* still nothing.... too bad */ 405 return NULL; 406 } 407 /* 408 ** _UDBX_INIT -- parse the UDB specification, opening any valid entries. 409 ** 410 ** Parameters: 411 ** none. 412 ** 413 ** Returns: 414 ** EX_TEMPFAIL -- if it appeared it couldn't get hold of a 415 ** database due to a host being down or some similar 416 ** (recoverable) situation. 417 ** EX_OK -- otherwise. 418 ** 419 ** Side Effects: 420 ** Fills in the UdbEnts structure from UdbSpec. 421 */ 422 423 #define MAXUDBOPTS 27 424 425 int 426 _udbx_init() 427 { 428 register char *p; 429 int i; 430 register struct udbent *up; 431 char buf[BUFSIZ]; 432 433 if (UdbInitialized) 434 return EX_OK; 435 436 # ifdef UDB_DEFAULT_SPEC 437 if (UdbSpec == NULL) 438 UdbSpec = UDB_DEFAULT_SPEC; 439 # endif 440 441 p = UdbSpec; 442 up = UdbEnts; 443 while (p != NULL) 444 { 445 char *spec; 446 auto int rcode; 447 int nopts; 448 int nmx; 449 register struct hostent *h; 450 char *mxhosts[MAXMXHOSTS + 1]; 451 struct option opts[MAXUDBOPTS + 1]; 452 453 while (*p == ' ' || *p == '\t' || *p == ',') 454 p++; 455 if (*p == '\0') 456 break; 457 spec = p; 458 p = strchr(p, ','); 459 if (p != NULL) 460 *p++ = '\0'; 461 462 /* extract options */ 463 nopts = _udb_parsespec(spec, opts, MAXUDBOPTS); 464 465 /* 466 ** Decode database specification. 467 ** 468 ** In the sendmail tradition, the leading character 469 ** defines the semantics of the rest of the entry. 470 ** 471 ** +hostname -- send a datagram to the udb server 472 ** on host "hostname" asking for the 473 ** home mail server for this user. 474 ** *hostname -- similar to +hostname, except that the 475 ** hostname is searched as an MX record; 476 ** resulting hosts are searched as for 477 ** +mxhostname. If no MX host is found, 478 ** this is the same as +hostname. 479 ** @hostname -- forward email to the indicated host. 480 ** This should be the last in the list, 481 ** since it always matches the input. 482 ** /dbname -- search the named database on the local 483 ** host using the Berkeley db package. 484 */ 485 486 switch (*spec) 487 { 488 case '+': /* search remote database */ 489 case '*': /* search remote database (expand MX) */ 490 if (*spec == '*') 491 { 492 #ifdef NAMED_BIND 493 nmx = getmxrr(spec + 1, mxhosts, "", &rcode); 494 #else 495 mxhosts[0] = spec + 1; 496 nmx = 1; 497 rcode = 0; 498 #endif 499 if (tTd(28, 16)) 500 { 501 int i; 502 503 printf("getmxrr(%s): %d", spec + 1, nmx); 504 for (i = 0; i <= nmx; i++) 505 printf(" %s", mxhosts[i]); 506 printf("\n"); 507 } 508 } 509 else 510 { 511 nmx = 1; 512 mxhosts[0] = spec + 1; 513 } 514 515 for (i = 0; i < nmx; i++) 516 { 517 h = gethostbyname(mxhosts[i]); 518 if (h == NULL) 519 continue; 520 up->udb_type = UDB_REMOTE; 521 up->udb_addr.sin_family = h->h_addrtype; 522 bcopy(h->h_addr_list[0], 523 (char *) &up->udb_addr.sin_addr, 524 h->h_length); 525 up->udb_addr.sin_port = UdbPort; 526 up->udb_timeout = UdbTimeout; 527 up++; 528 } 529 530 /* set up a datagram socket */ 531 if (UdbSock < 0) 532 { 533 UdbSock = socket(AF_INET, SOCK_DGRAM, 0); 534 (void) fcntl(UdbSock, F_SETFD, 1); 535 } 536 break; 537 538 case '@': /* forward to remote host */ 539 up->udb_type = UDB_FORWARD; 540 up->udb_fwdhost = spec + 1; 541 up++; 542 break; 543 544 case '/': /* look up remote name */ 545 up->udb_dbname = spec; 546 errno = 0; 547 up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL); 548 if (up->udb_dbp == NULL) 549 { 550 if (errno != ENOENT && errno != EACCES) 551 { 552 up->udb_type = UDB_EOLIST; 553 goto tempfail; 554 } 555 break; 556 } 557 up->udb_type = UDB_DBFETCH; 558 up++; 559 break; 560 } 561 } 562 up->udb_type = UDB_EOLIST; 563 564 if (tTd(28, 4)) 565 { 566 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 567 { 568 switch (up->udb_type) 569 { 570 case UDB_REMOTE: 571 printf("REMOTE: addr %s, timeo %d\n", 572 inet_ntoa(up->udb_addr.sin_addr), 573 up->udb_timeout); 574 break; 575 576 case UDB_DBFETCH: 577 printf("FETCH: file %s\n", 578 up->udb_dbname); 579 break; 580 581 case UDB_FORWARD: 582 printf("FORWARD: host %s\n", 583 up->udb_fwdhost); 584 break; 585 586 default: 587 printf("UNKNOWN\n"); 588 break; 589 } 590 } 591 } 592 593 UdbInitialized = TRUE; 594 errno = 0; 595 return EX_OK; 596 597 /* 598 ** On temporary failure, back out anything we've already done 599 */ 600 601 tempfail: 602 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 603 { 604 if (up->udb_type == UDB_DBFETCH) 605 { 606 (*up->udb_dbp->close)(up->udb_dbp); 607 } 608 } 609 return EX_TEMPFAIL; 610 } 611 612 int 613 _udb_parsespec(udbspec, opt, maxopts) 614 char *udbspec; 615 struct option opt[]; 616 int maxopts; 617 { 618 register char *spec; 619 register char *spec_end; 620 register int optnum; 621 622 spec_end = strchr(udbspec, ':'); 623 for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) 624 { 625 register char *p; 626 627 while (isascii(*spec) && isspace(*spec)) 628 spec++; 629 spec_end = strchr(spec, ':'); 630 if (spec_end != NULL) 631 *spec_end++ = '\0'; 632 633 opt[optnum].name = spec; 634 opt[optnum].val = NULL; 635 p = strchr(spec, '='); 636 if (p != NULL) 637 opt[optnum].val = ++p; 638 } 639 return optnum; 640 } 641 642 #else /* not USERDB */ 643 644 int 645 udbexpand(a, sendq, e) 646 ADDRESS *a; 647 ADDRESS **sendq; 648 ENVELOPE *e; 649 { 650 return EX_OK; 651 } 652 653 #endif /* USERDB */ 654