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