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.2 (Berkeley) 01/20/93 (with USERDB)"; 12 #else 13 static char sccsid [] = "@(#)udb.c 6.2 (Berkeley) 01/20/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 struct timeval timeout; 161 fd_set fdset; 162 163 /* 164 ** Select action based on entry type. 165 ** 166 ** On dropping out of this switch, "class" should 167 ** explain the type of the data, and "user" should 168 ** contain the user information. 169 */ 170 171 switch (up->udb_type) 172 { 173 case UDB_DBFETCH: 174 key.data = keybuf; 175 key.size = keylen; 176 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); 177 if (i > 0 || info.size <= 0) 178 { 179 if (tTd(28, 2)) 180 printf("expand: no match on %s\n", keybuf); 181 continue; 182 } 183 184 while (i == 0 && key.size == keylen && 185 bcmp(key.data, keybuf, keylen) == 0) 186 { 187 breakout = TRUE; 188 if (info.size < sizeof buf) 189 user = buf; 190 else 191 user = xalloc(info.size + 1); 192 bcopy(info.data, user, info.size); 193 user[info.size] = '\0'; 194 195 message(Arpa_Info, "expanded to %s", user); 196 AliasLevel++; 197 sendtolist(user, a, sendq, e); 198 AliasLevel--; 199 200 if (user != buf) 201 free(user); 202 203 /* get the next record */ 204 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); 205 } 206 if (i < 0) 207 { 208 syserr("udbexpand: db-get stat %s"); 209 return EX_TEMPFAIL; 210 } 211 break; 212 213 case UDB_REMOTE: 214 /* not yet implemented */ 215 continue; 216 217 case UDB_FORWARD: 218 i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; 219 if (i < sizeof buf) 220 user = buf; 221 else 222 user = xalloc(i + 1); 223 (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost); 224 message(Arpa_Info, "expanded to %s", user); 225 AliasLevel++; 226 sendtolist(user, a, sendq, e); 227 AliasLevel--; 228 if (user != buf) 229 free(user); 230 breakout = TRUE; 231 break; 232 233 case UDB_EOLIST: 234 breakout = TRUE; 235 continue; 236 237 default: 238 /* unknown entry type */ 239 continue; 240 } 241 } 242 return EX_OK; 243 } 244 /* 245 ** UDBSENDER -- return canonical external name of sender, given local name 246 ** 247 ** Parameters: 248 ** sender -- the name of the sender on the local machine. 249 ** 250 ** Returns: 251 ** The external name for this sender, if derivable from the 252 ** database. 253 ** NULL -- if nothing is changed from the database. 254 ** 255 ** Side Effects: 256 ** none. 257 */ 258 259 char * 260 udbsender(sender) 261 char *sender; 262 { 263 register char *p; 264 register struct udbent *up; 265 int i; 266 int keylen; 267 DBT key, info; 268 char keybuf[MAXKEY]; 269 270 if (tTd(28, 1)) 271 printf("udbsender(%s)\n", sender); 272 273 if (!UdbInitialized) 274 { 275 if (_udbx_init() == EX_TEMPFAIL) 276 return NULL; 277 } 278 279 /* short circuit if no spec */ 280 if (UdbSpec == NULL || UdbSpec[0] == '\0') 281 return NULL; 282 283 /* long names can never match and are a pain to deal with */ 284 if (strlen(sender) > sizeof keybuf - 12) 285 return NULL; 286 287 /* names beginning with colons indicate metadata */ 288 if (sender[0] == ':') 289 return NULL; 290 291 /* build database key */ 292 (void) strcpy(keybuf, sender); 293 (void) strcat(keybuf, ":mailname"); 294 keylen = strlen(keybuf); 295 296 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 297 { 298 /* 299 ** Select action based on entry type. 300 */ 301 302 switch (up->udb_type) 303 { 304 case UDB_DBFETCH: 305 key.data = keybuf; 306 key.size = keylen; 307 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 308 if (i != 0 || info.size <= 0) 309 { 310 if (tTd(28, 2)) 311 printf("udbsender: no match on %s\n", 312 keybuf); 313 continue; 314 } 315 316 p = xalloc(info.size + 1); 317 bcopy(info.data, p, info.size); 318 p[info.size] = '\0'; 319 if (tTd(28, 1)) 320 printf("udbsender ==> %s\n", p); 321 return p; 322 } 323 } 324 325 /* 326 ** Nothing yet. Search again for a default case. But only 327 ** use it if we also have a forward (:maildrop) pointer already 328 ** in the database. 329 */ 330 331 /* build database key */ 332 (void) strcpy(keybuf, sender); 333 (void) strcat(keybuf, ":maildrop"); 334 keylen = strlen(keybuf); 335 336 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 337 { 338 switch (up->udb_type) 339 { 340 case UDB_DBFETCH: 341 /* get the default case for this database */ 342 if (up->udb_default == NULL) 343 { 344 key.data = ":default:mailname"; 345 key.size = strlen(key.data); 346 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 347 if (i != 0 || info.size <= 0) 348 { 349 /* no default case */ 350 up->udb_default = ""; 351 continue; 352 } 353 354 /* save the default case */ 355 up->udb_default = xalloc(info.size + 1); 356 bcopy(info.data, up->udb_default, info.size); 357 up->udb_default[info.size] = '\0'; 358 } 359 else if (up->udb_default[0] == '\0') 360 continue; 361 362 /* we have a default case -- verify user:maildrop */ 363 key.data = keybuf; 364 key.size = keylen; 365 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 366 if (i != 0 || info.size <= 0) 367 { 368 /* nope -- no aliasing for this user */ 369 continue; 370 } 371 372 /* they exist -- build the actual address */ 373 p = xalloc(strlen(sender) + strlen(up->udb_default) + 2); 374 (void) strcpy(p, sender); 375 (void) strcat(p, "@"); 376 (void) strcat(p, up->udb_default); 377 if (tTd(28, 1)) 378 printf("udbsender ==> %s\n", p); 379 return p; 380 } 381 } 382 383 /* still nothing.... too bad */ 384 return NULL; 385 } 386 /* 387 ** _UDBX_INIT -- parse the UDB specification, opening any valid entries. 388 ** 389 ** Parameters: 390 ** none. 391 ** 392 ** Returns: 393 ** EX_TEMPFAIL -- if it appeared it couldn't get hold of a 394 ** database due to a host being down or some similar 395 ** (recoverable) situation. 396 ** EX_OK -- otherwise. 397 ** 398 ** Side Effects: 399 ** Fills in the UdbEnts structure from UdbSpec. 400 */ 401 402 #define MAXUDBOPTS 27 403 404 int 405 _udbx_init() 406 { 407 register char *p; 408 int i; 409 register struct udbent *up; 410 char buf[BUFSIZ]; 411 412 if (UdbInitialized) 413 return EX_OK; 414 415 # ifdef UDB_DEFAULT_SPEC 416 if (UdbSpec == NULL) 417 UdbSpec = UDB_DEFAULT_SPEC; 418 # endif 419 420 p = UdbSpec; 421 up = UdbEnts; 422 while (p != NULL) 423 { 424 char *spec; 425 auto int rcode; 426 int nopts; 427 int nmx; 428 register struct hostent *h; 429 char *mxhosts[MAXMXHOSTS + 1]; 430 struct option opts[MAXUDBOPTS + 1]; 431 432 while (*p == ' ' || *p == '\t' || *p == ',') 433 p++; 434 if (*p == '\0') 435 break; 436 spec = p; 437 p = strchr(p, ','); 438 if (p != NULL) 439 *p++ = '\0'; 440 441 /* extract options */ 442 nopts = _udb_parsespec(spec, opts, MAXUDBOPTS); 443 444 /* 445 ** Decode database specification. 446 ** 447 ** In the sendmail tradition, the leading character 448 ** defines the semantics of the rest of the entry. 449 ** 450 ** +hostname -- send a datagram to the udb server 451 ** on host "hostname" asking for the 452 ** home mail server for this user. 453 ** *hostname -- similar to +hostname, except that the 454 ** hostname is searched as an MX record; 455 ** resulting hosts are searched as for 456 ** +mxhostname. If no MX host is found, 457 ** this is the same as +hostname. 458 ** @hostname -- forward email to the indicated host. 459 ** This should be the last in the list, 460 ** since it always matches the input. 461 ** /dbname -- search the named database on the local 462 ** host using the Berkeley db package. 463 */ 464 465 switch (*spec) 466 { 467 case '+': /* search remote database */ 468 case '*': /* search remote database (expand MX) */ 469 if (*spec == '*') 470 { 471 #ifdef NAMED_BIND 472 nmx = getmxrr(spec + 1, mxhosts, "", &rcode); 473 #else 474 mxhosts[0] = spec + 1; 475 nmx = 1; 476 rcode = 0; 477 #endif 478 if (tTd(28, 16)) 479 { 480 int i; 481 482 printf("getmxrr(%s): %d", spec + 1, nmx); 483 for (i = 0; i <= nmx; i++) 484 printf(" %s", mxhosts[i]); 485 printf("\n"); 486 } 487 } 488 else 489 { 490 nmx = 1; 491 mxhosts[0] = spec + 1; 492 } 493 494 for (i = 0; i < nmx; i++) 495 { 496 h = gethostbyname(mxhosts[i]); 497 if (h == NULL) 498 continue; 499 up->udb_type = UDB_REMOTE; 500 up->udb_addr.sin_family = h->h_addrtype; 501 bcopy(h->h_addr_list[0], 502 (char *) &up->udb_addr.sin_addr, 503 h->h_length); 504 up->udb_addr.sin_port = UdbPort; 505 up->udb_timeout = UdbTimeout; 506 up++; 507 } 508 509 /* set up a datagram socket */ 510 if (UdbSock < 0) 511 { 512 UdbSock = socket(AF_INET, SOCK_DGRAM, 0); 513 (void) fcntl(UdbSock, F_SETFD, 1); 514 } 515 break; 516 517 case '@': /* forward to remote host */ 518 up->udb_type = UDB_FORWARD; 519 up->udb_fwdhost = spec + 1; 520 up++; 521 break; 522 523 case '/': /* look up remote name */ 524 up->udb_dbname = spec; 525 errno = 0; 526 up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL); 527 if (up->udb_dbp == NULL) 528 { 529 if (errno != ENOENT && errno != EACCES) 530 { 531 up->udb_type = UDB_EOLIST; 532 goto tempfail; 533 } 534 break; 535 } 536 up->udb_type = UDB_DBFETCH; 537 up++; 538 break; 539 } 540 } 541 up->udb_type = UDB_EOLIST; 542 543 if (tTd(28, 4)) 544 { 545 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 546 { 547 switch (up->udb_type) 548 { 549 case UDB_REMOTE: 550 printf("REMOTE: addr %s, timeo %d\n", 551 inet_ntoa(up->udb_addr.sin_addr), 552 up->udb_timeout); 553 break; 554 555 case UDB_DBFETCH: 556 printf("FETCH: file %s\n", 557 up->udb_dbname); 558 break; 559 560 case UDB_FORWARD: 561 printf("FORWARD: host %s\n", 562 up->udb_fwdhost); 563 break; 564 565 default: 566 printf("UNKNOWN\n"); 567 break; 568 } 569 } 570 } 571 572 UdbInitialized = TRUE; 573 errno = 0; 574 return EX_OK; 575 576 /* 577 ** On temporary failure, back out anything we've already done 578 */ 579 580 tempfail: 581 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 582 { 583 if (up->udb_type == UDB_DBFETCH) 584 { 585 (*up->udb_dbp->close)(up->udb_dbp); 586 } 587 } 588 return EX_TEMPFAIL; 589 } 590 591 int 592 _udb_parsespec(udbspec, opt, maxopts) 593 char *udbspec; 594 struct option opt[]; 595 int maxopts; 596 { 597 register char *spec; 598 register char *spec_end; 599 register int optnum; 600 601 spec_end = strchr(udbspec, ':'); 602 for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) 603 { 604 register char *p; 605 606 while (isspace(*spec)) 607 spec++; 608 spec_end = strchr(spec, ':'); 609 if (spec_end != NULL) 610 *spec_end++ = '\0'; 611 612 opt[optnum].name = spec; 613 opt[optnum].val = NULL; 614 p = strchr(spec, '='); 615 if (p != NULL) 616 opt[optnum].val = ++p; 617 } 618 return optnum; 619 } 620 621 #else /* not USERDB */ 622 623 int 624 udbexpand(a, sendq, e) 625 ADDRESS *a; 626 ADDRESS **sendq; 627 ENVELOPE *e; 628 { 629 return EX_OK; 630 } 631 632 #endif /* USERDB */ 633