150581Seric /* 250581Seric * Copyright (c) 1983 Eric P. Allman 350581Seric * Copyright (c) 1988 Regents of the University of California. 450581Seric * All rights reserved. 550581Seric * 650581Seric * %sccs.include.redist.c% 750581Seric */ 850581Seric 950581Seric #ifndef lint 1051360Seric #ifdef USERDB 11*51951Seric static char sccsid [] = "@(#)udb.c 5.15 (Berkeley) 12/15/91 (with USERDB)"; 1251360Seric #else 13*51951Seric static char sccsid [] = "@(#)udb.c 5.15 (Berkeley) 12/15/91 (without USERDB)"; 1450581Seric #endif 1551360Seric #endif 1650581Seric 1750581Seric #include "sendmail.h" 1850581Seric 1950581Seric #ifdef USERDB 2050581Seric 2150581Seric #include <sys/file.h> 2251360Seric #include <sys/time.h> 2351923Seric #include <errno.h> 2451360Seric #include <fcntl.h> 2551360Seric #include <netdb.h> 2650581Seric #include <db.h> 2750581Seric 2850581Seric /* 2951363Seric ** UDBEXPAND.C -- interface between sendmail and Berkeley User Data Base. 3050581Seric ** 3151363Seric ** This depends on the 4.4BSD db package. 3250581Seric */ 3350581Seric 3451362Seric 3551360Seric struct udbent 3651360Seric { 3751360Seric char *udb_spec; /* string version of spec */ 3851360Seric int udb_type; /* type of entry */ 39*51951Seric char *udb_default; /* default host for outgoing mail */ 4051360Seric union 4151360Seric { 4251360Seric /* type UE_REMOTE -- do remote call for lookup */ 4351360Seric struct 4451360Seric { 4551360Seric struct sockaddr_in _udb_addr; /* address */ 4651360Seric int _udb_timeout; /* timeout */ 4751360Seric } udb_remote; 4851360Seric #define udb_addr udb_u.udb_remote._udb_addr 4951360Seric #define udb_timeout udb_u.udb_remote._udb_timeout 5051360Seric 5151360Seric /* type UE_FORWARD -- forward message to remote */ 5251360Seric struct 5351360Seric { 5451360Seric char *_udb_fwdhost; /* name of forward host */ 5551360Seric } udb_forward; 5651360Seric #define udb_fwdhost udb_u.udb_forward._udb_fwdhost 5751360Seric 58*51951Seric /* type UE_FETCH -- lookup in local database */ 5951360Seric struct 6051360Seric { 6151360Seric char *_udb_dbname; /* pathname of database */ 6251360Seric DB *_udb_dbp; /* open database ptr */ 6351360Seric } udb_lookup; 6451360Seric #define udb_dbname udb_u.udb_lookup._udb_dbname 6551360Seric #define udb_dbp udb_u.udb_lookup._udb_dbp 6651360Seric } udb_u; 6751360Seric }; 6851360Seric 6951360Seric #define UDB_EOLIST 0 /* end of list */ 7051360Seric #define UDB_SKIP 1 /* skip this entry */ 7151360Seric #define UDB_REMOTE 2 /* look up in remote database */ 72*51951Seric #define UDB_DBFETCH 3 /* look up in local database */ 7351360Seric #define UDB_FORWARD 4 /* forward to remote host */ 7451360Seric 7551360Seric #define MAXUDBENT 10 /* maximum number of UDB entries */ 7651360Seric 7751363Seric 7851363Seric struct option 7951363Seric { 8051363Seric char *name; 8151363Seric char *val; 8251363Seric }; 8351363Seric /* 8451363Seric ** UDBEXPAND -- look up user in database and expand 8551363Seric ** 8651363Seric ** Parameters: 8751363Seric ** a -- address to expand. 8851363Seric ** sendq -- pointer to head of sendq to put the expansions in. 8951363Seric ** 9051363Seric ** Returns: 9151923Seric ** EX_TEMPFAIL -- if something "odd" happened -- probably due 9251923Seric ** to accessing a file on an NFS server that is down. 9351923Seric ** EX_OK -- otherwise. 9451363Seric ** 9551363Seric ** Side Effects: 9651363Seric ** Modifies sendq. 9751363Seric */ 9851363Seric 9951363Seric int UdbPort = 1616; 10051363Seric int UdbTimeout = 10; 10151363Seric 102*51951Seric STATIC struct udbent UdbEnts[MAXUDBENT + 1]; 103*51951Seric STATIC int UdbSock = -1; 104*51951Seric STATIC bool UdbInitialized = FALSE; 10551360Seric 10651923Seric int 10750581Seric udbexpand(a, sendq) 10850581Seric register ADDRESS *a; 10950581Seric ADDRESS **sendq; 11050581Seric { 11150581Seric int i; 11250581Seric register char *p; 11350581Seric DBT key; 11450581Seric DBT info; 11551360Seric bool breakout; 11651360Seric register struct udbent *up; 11751362Seric int keylen; 11851362Seric char keybuf[128]; 11950581Seric char buf[8192]; 12050581Seric 12150581Seric if (tTd(28, 1)) 12250581Seric printf("expand(%s)\n", a->q_paddr); 12350581Seric 12450581Seric /* make certain we are supposed to send to this address */ 12551909Seric if (bitset(QDONTSEND, a->q_flags)) 12651923Seric return EX_OK; 12750581Seric CurEnv->e_to = a->q_paddr; 12850581Seric 12951360Seric /* on first call, locate the database */ 130*51951Seric if (!UdbInitialized) 13150581Seric { 13251923Seric extern int _udbx_init(); 13351362Seric 13451923Seric if (_udbx_init() == EX_TEMPFAIL) 13551923Seric return EX_TEMPFAIL; 13651362Seric } 13750581Seric 13851909Seric /* short circuit the process if no chance of a match */ 13951909Seric if (UdbSpec == NULL || UdbSpec[0] == '\0') 14051923Seric return EX_OK; 14151909Seric 14251362Seric /* if name is too long, assume it won't match */ 14351362Seric if (strlen(a->q_user) > sizeof keybuf - 12) 14451923Seric return EX_OK; 14551360Seric 14651362Seric /* if name begins with a colon, it indicates our metadata */ 14751362Seric if (a->q_user[0] == ':') 14851923Seric return EX_OK; 14951360Seric 15051362Seric /* build actual database key */ 15151362Seric (void) strcpy(keybuf, a->q_user); 15251362Seric (void) strcat(keybuf, ":maildrop"); 15351362Seric keylen = strlen(keybuf); 15451360Seric 15551360Seric breakout = FALSE; 15651362Seric for (up = UdbEnts; !breakout; up++) 15750581Seric { 15851360Seric char *user; 15951360Seric struct timeval timeout; 16051360Seric fd_set fdset; 16150581Seric 16251360Seric /* 16351360Seric ** Select action based on entry type. 16451360Seric ** 16551360Seric ** On dropping out of this switch, "class" should 16651360Seric ** explain the type of the data, and "user" should 16751360Seric ** contain the user information. 16851360Seric */ 16950581Seric 17051360Seric switch (up->udb_type) 17151360Seric { 172*51951Seric case UDB_DBFETCH: 17351362Seric key.data = keybuf; 17451362Seric key.size = keylen; 17551362Seric i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); 17651923Seric if (i > 0 || info.size <= 0) 17751360Seric { 17851360Seric if (tTd(28, 2)) 17951362Seric printf("expand: no match on %s\n", keybuf); 18051360Seric continue; 18151360Seric } 18250581Seric 18351830Seric while (i == 0 && key.size == keylen && 18451830Seric bcmp(key.data, keybuf, keylen) == 0) 18551362Seric { 18651830Seric breakout = TRUE; 18751362Seric if (info.size < sizeof buf) 18851362Seric user = buf; 18951362Seric else 19051362Seric user = xalloc(info.size + 1); 19151362Seric bcopy(info.data, user, info.size); 19251362Seric user[info.size] = '\0'; 19350581Seric 19451362Seric message(Arpa_Info, "expanded to %s", user); 19551362Seric AliasLevel++; 19651362Seric sendtolist(user, a, sendq); 19751362Seric AliasLevel--; 19851362Seric 19951362Seric if (user != buf) 20051362Seric free(user); 20151362Seric 20251362Seric /* get the next record */ 20351362Seric i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); 20451830Seric } 20551923Seric if (i < 0) 20651923Seric { 20751923Seric syserr("udbexpand: db-get stat %s"); 20851923Seric return EX_TEMPFAIL; 20951923Seric } 21051360Seric break; 21151360Seric 21251360Seric case UDB_REMOTE: 21351741Seric /* not yet implemented */ 21451741Seric continue; 21551362Seric 21651360Seric case UDB_FORWARD: 21751360Seric i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; 21851360Seric if (i < sizeof buf) 21951360Seric user = buf; 22051360Seric else 22151360Seric user = xalloc(i + 1); 22251360Seric (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost); 22351362Seric message(Arpa_Info, "expanded to %s", user); 22451362Seric AliasLevel++; 22551362Seric sendtolist(user, a, sendq); 22651362Seric AliasLevel--; 22751362Seric if (user != buf) 22851362Seric free(user); 22951362Seric breakout = TRUE; 23051360Seric break; 23151360Seric 23251360Seric case UDB_EOLIST: 23351360Seric breakout = TRUE; 23451360Seric continue; 23551360Seric 23651360Seric default: 23751360Seric /* unknown entry type */ 23851360Seric continue; 23951360Seric } 24051362Seric } 24151923Seric return EX_OK; 24251362Seric } 243*51951Seric /* 244*51951Seric ** UDBSENDER -- return canonical external name of sender, given local name 245*51951Seric ** 246*51951Seric ** Parameters: 247*51951Seric ** sender -- the name of the sender on the local machine. 248*51951Seric ** 249*51951Seric ** Returns: 250*51951Seric ** The external name for this sender, if derivable from the 251*51951Seric ** database. 252*51951Seric ** NULL -- if nothing is changed from the database. 253*51951Seric ** 254*51951Seric ** Side Effects: 255*51951Seric ** none. 256*51951Seric */ 25751360Seric 258*51951Seric char * 259*51951Seric udbsender(sender) 260*51951Seric char *sender; 261*51951Seric { 262*51951Seric register char *p; 263*51951Seric register struct udbent *up; 264*51951Seric int i; 265*51951Seric int keylen; 266*51951Seric DBT key, info; 267*51951Seric char keybuf[128]; 268*51951Seric 269*51951Seric if (tTd(28, 1)) 270*51951Seric printf("udbsender(%s)\n", sender); 271*51951Seric 272*51951Seric if (!UdbInitialized) 273*51951Seric { 274*51951Seric if (_udbx_init() == EX_TEMPFAIL) 275*51951Seric return NULL; 276*51951Seric } 277*51951Seric 278*51951Seric /* short circuit if no spec */ 279*51951Seric if (UdbSpec == NULL || UdbSpec[0] == '\0') 280*51951Seric return NULL; 281*51951Seric 282*51951Seric /* long names can never match and are a pain to deal with */ 283*51951Seric if (strlen(sender) > sizeof keybuf - 12) 284*51951Seric return NULL; 285*51951Seric 286*51951Seric /* names beginning with colons indicate metadata */ 287*51951Seric if (sender[0] == ':') 288*51951Seric return NULL; 289*51951Seric 290*51951Seric /* build database key */ 291*51951Seric (void) strcpy(keybuf, sender); 292*51951Seric (void) strcat(keybuf, ":mailname"); 293*51951Seric keylen = strlen(keybuf); 294*51951Seric 295*51951Seric for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 296*51951Seric { 297*51951Seric /* 298*51951Seric ** Select action based on entry type. 299*51951Seric */ 300*51951Seric 301*51951Seric switch (up->udb_type) 302*51951Seric { 303*51951Seric case UDB_DBFETCH: 304*51951Seric key.data = keybuf; 305*51951Seric key.size = keylen; 306*51951Seric i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 307*51951Seric if (i != 0 || info.size <= 0) 308*51951Seric { 309*51951Seric if (tTd(28, 2)) 310*51951Seric printf("udbsender: no match on %s\n", 311*51951Seric keybuf); 312*51951Seric continue; 313*51951Seric } 314*51951Seric 315*51951Seric p = xalloc(info.size + 1); 316*51951Seric bcopy(info.data, p, info.size); 317*51951Seric p[info.size] = '\0'; 318*51951Seric if (tTd(28, 1)) 319*51951Seric printf("udbsender ==> %s\n", p); 320*51951Seric return p; 321*51951Seric } 322*51951Seric } 323*51951Seric 324*51951Seric /* 325*51951Seric ** Nothing yet. Search again for a default case. But only 326*51951Seric ** use it if we also have a forward (:maildrop) pointer already 327*51951Seric ** in the database. 328*51951Seric */ 329*51951Seric 330*51951Seric /* build database key */ 331*51951Seric (void) strcpy(keybuf, sender); 332*51951Seric (void) strcat(keybuf, ":maildrop"); 333*51951Seric keylen = strlen(keybuf); 334*51951Seric 335*51951Seric for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 336*51951Seric { 337*51951Seric switch (up->udb_type) 338*51951Seric { 339*51951Seric case UDB_DBFETCH: 340*51951Seric /* get the default case for this database */ 341*51951Seric if (up->udb_default == NULL) 342*51951Seric { 343*51951Seric key.data = ":default:mailname"; 344*51951Seric key.size = strlen(key.data); 345*51951Seric i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 346*51951Seric if (i != 0 || info.size <= 0) 347*51951Seric { 348*51951Seric /* no default case */ 349*51951Seric up->udb_default = ""; 350*51951Seric continue; 351*51951Seric } 352*51951Seric 353*51951Seric /* save the default case */ 354*51951Seric up->udb_default = xalloc(info.size + 1); 355*51951Seric bcopy(info.data, up->udb_default, info.size); 356*51951Seric up->udb_default[info.size] = '\0'; 357*51951Seric } 358*51951Seric else if (up->udb_default[0] == '\0') 359*51951Seric continue; 360*51951Seric 361*51951Seric /* we have a default case -- verify user:maildrop */ 362*51951Seric key.data = keybuf; 363*51951Seric key.size = keylen; 364*51951Seric i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 365*51951Seric if (i != 0 || info.size <= 0) 366*51951Seric { 367*51951Seric /* nope -- no aliasing for this user */ 368*51951Seric continue; 369*51951Seric } 370*51951Seric 371*51951Seric /* they exist -- build the actual address */ 372*51951Seric p = xalloc(strlen(sender) + strlen(up->udb_default) + 2); 373*51951Seric (void) strcpy(p, sender); 374*51951Seric (void) strcat(p, "@"); 375*51951Seric (void) strcat(p, up->udb_default); 376*51951Seric if (tTd(28, 1)) 377*51951Seric printf("udbsender ==> %s\n", p); 378*51951Seric return p; 379*51951Seric } 380*51951Seric } 381*51951Seric 382*51951Seric /* still nothing.... too bad */ 383*51951Seric return NULL; 384*51951Seric } 385*51951Seric /* 386*51951Seric ** _UDBX_INIT -- parse the UDB specification, opening any valid entries. 387*51951Seric ** 388*51951Seric ** Parameters: 389*51951Seric ** none. 390*51951Seric ** 391*51951Seric ** Returns: 392*51951Seric ** EX_TEMPFAIL -- if it appeared it couldn't get hold of a 393*51951Seric ** database due to a host being down or some similar 394*51951Seric ** (recoverable) situation. 395*51951Seric ** EX_OK -- otherwise. 396*51951Seric ** 397*51951Seric ** Side Effects: 398*51951Seric ** Fills in the UdbEnts structure from UdbSpec. 399*51951Seric */ 400*51951Seric 40151363Seric #define MAXUDBOPTS 27 40251363Seric 403*51951Seric STATIC int 40451362Seric _udbx_init() 40551362Seric { 40651362Seric register char *p; 40751362Seric int i; 40851362Seric register struct udbent *up; 40951362Seric char buf[8192]; 41051360Seric 411*51951Seric if (UdbInitialized) 412*51951Seric return EX_OK; 413*51951Seric 41451908Seric # ifdef UDB_DEFAULT_SPEC 41551908Seric if (UdbSpec == NULL) 41651908Seric UdbSpec = UDB_DEFAULT_SPEC; 41751908Seric # endif 41851908Seric 41951362Seric p = UdbSpec; 42051362Seric up = UdbEnts; 42151762Seric while (p != NULL) 42251362Seric { 42351362Seric char *spec; 42451362Seric auto int rcode; 42551363Seric int nopts; 42651362Seric int nmx; 42751362Seric register struct hostent *h; 42851362Seric char *mxhosts[MAXMXHOSTS + 1]; 42951363Seric struct option opts[MAXUDBOPTS + 1]; 43051362Seric 43151362Seric while (*p == ' ' || *p == '\t' || *p == ',') 43251362Seric p++; 43351362Seric if (*p == '\0') 43451362Seric break; 43551362Seric spec = p; 43651362Seric p = index(p, ','); 43751761Seric if (p != NULL) 43851362Seric *p++ = '\0'; 43951363Seric 44051363Seric /* extract options */ 44151363Seric nopts = _udb_parsespec(spec, opts, MAXUDBOPTS); 44251363Seric 44351363Seric /* 44451363Seric ** Decode database specification. 44551363Seric ** 44651363Seric ** In the sendmail tradition, the leading character 44751363Seric ** defines the semantics of the rest of the entry. 44851363Seric ** 44951363Seric ** +hostname -- send a datagram to the udb server 45051363Seric ** on host "hostname" asking for the 45151363Seric ** home mail server for this user. 45251363Seric ** *hostname -- similar to +hostname, except that the 45351363Seric ** hostname is searched as an MX record; 45451363Seric ** resulting hosts are searched as for 45551363Seric ** +mxhostname. If no MX host is found, 45651363Seric ** this is the same as +hostname. 45751363Seric ** @hostname -- forward email to the indicated host. 45851363Seric ** This should be the last in the list, 45951363Seric ** since it always matches the input. 46051363Seric ** /dbname -- search the named database on the local 46151363Seric ** host using the Berkeley db package. 46251363Seric */ 46351363Seric 46451362Seric switch (*spec) 46551360Seric { 46651362Seric case '+': /* search remote database */ 46751363Seric case '*': /* search remote database (expand MX) */ 46851363Seric if (*spec == '*') 46951363Seric { 47051363Seric nmx = getmxrr(spec + 1, mxhosts, "", &rcode); 47151363Seric if (tTd(28, 16)) 47251363Seric { 47351363Seric int i; 47451362Seric 47551363Seric printf("getmxrr(%s): %d", spec + 1, nmx); 47651363Seric for (i = 0; i <= nmx; i++) 47751363Seric printf(" %s", mxhosts[i]); 47851363Seric printf("\n"); 47951363Seric } 48051363Seric } 48151363Seric else 48251362Seric { 48351363Seric nmx = 1; 48451363Seric mxhosts[0] = spec + 1; 48551362Seric } 48651362Seric 48751362Seric for (i = 0; i < nmx; i++) 48851362Seric { 48951362Seric h = gethostbyname(mxhosts[i]); 49051362Seric if (h == NULL) 49151362Seric continue; 49251362Seric up->udb_type = UDB_REMOTE; 49351362Seric up->udb_addr.sin_family = h->h_addrtype; 49451362Seric bcopy(h->h_addr_list[0], 49551362Seric (char *) &up->udb_addr.sin_addr, 49651362Seric h->h_length); 49751362Seric up->udb_addr.sin_port = UdbPort; 49851362Seric up->udb_timeout = UdbTimeout; 49951362Seric up++; 50051362Seric } 50151362Seric 50251362Seric /* set up a datagram socket */ 50351362Seric if (UdbSock < 0) 50451362Seric { 50551362Seric UdbSock = socket(AF_INET, SOCK_DGRAM, 0); 50651362Seric (void) fcntl(UdbSock, F_SETFD, 1); 50751362Seric } 50851362Seric break; 50951362Seric 51051362Seric case '@': /* forward to remote host */ 51151362Seric up->udb_type = UDB_FORWARD; 51251362Seric up->udb_fwdhost = spec + 1; 51351362Seric up++; 51451362Seric break; 51551362Seric 51651362Seric case '/': /* look up remote name */ 51751831Seric up->udb_dbname = spec; 51851923Seric errno = 0; 51951362Seric up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL); 52051362Seric if (up->udb_dbp == NULL) 52151923Seric { 52251923Seric if (errno != ENOENT && errno != EACCES) 523*51951Seric { 524*51951Seric up->udb_type = UDB_EOLIST; 525*51951Seric goto tempfail; 526*51951Seric } 52751362Seric break; 52851923Seric } 529*51951Seric up->udb_type = UDB_DBFETCH; 53051362Seric up++; 53151362Seric break; 53251360Seric } 53351362Seric } 53451362Seric up->udb_type = UDB_EOLIST; 53551360Seric 53651362Seric if (tTd(28, 4)) 53751362Seric { 538*51951Seric for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 53951360Seric { 54051362Seric switch (up->udb_type) 54151362Seric { 54251362Seric case UDB_REMOTE: 54351362Seric printf("REMOTE: addr %s, timeo %d\n", 54451362Seric inet_ntoa(up->udb_addr.sin_addr), 54551362Seric up->udb_timeout); 54651362Seric break; 54751362Seric 548*51951Seric case UDB_DBFETCH: 549*51951Seric printf("FETCH: file %s\n", 55051830Seric up->udb_dbname); 55151362Seric break; 55251362Seric 55351362Seric case UDB_FORWARD: 55451362Seric printf("FORWARD: host %s\n", 55551362Seric up->udb_fwdhost); 55651362Seric break; 55751362Seric 55851362Seric default: 55951362Seric printf("UNKNOWN\n"); 56051362Seric break; 56151362Seric } 56251360Seric } 56350581Seric } 564*51951Seric 565*51951Seric UdbInitialized = TRUE; 566*51951Seric return EX_OK; 567*51951Seric 568*51951Seric /* 569*51951Seric ** On temporary failure, back out anything we've already done 570*51951Seric */ 571*51951Seric 572*51951Seric tempfail: 573*51951Seric for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 574*51951Seric { 575*51951Seric if (up->udb_type == UDB_DBFETCH) 576*51951Seric { 577*51951Seric (*up->udb_dbp->close)(up->udb_dbp); 578*51951Seric } 579*51951Seric } 580*51951Seric return EX_TEMPFAIL; 58151360Seric } 58250581Seric 58351363Seric int 58451363Seric _udb_parsespec(udbspec, opt, maxopts) 58551363Seric char *udbspec; 58651363Seric struct option opt[]; 58751363Seric int maxopts; 58851363Seric { 58951363Seric register char *spec; 59051363Seric register char *spec_end; 59151363Seric register int optnum; 59251363Seric 59351363Seric spec_end = index(udbspec, ':'); 59451363Seric for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) 59551363Seric { 59651363Seric register char *p; 59751363Seric 59851363Seric while (isspace(*spec)) 59951363Seric spec++; 60051363Seric spec_end = index(spec, ':'); 60151363Seric if (spec_end != NULL) 60251363Seric *spec_end++ = '\0'; 60351363Seric 60451363Seric opt[optnum].name = spec; 60551363Seric opt[optnum].val = NULL; 60651363Seric p = index(spec, '='); 60751363Seric if (p != NULL) 60851363Seric opt[optnum].val = ++p; 60951363Seric } 61051363Seric return optnum; 61151363Seric } 61251363Seric 61351360Seric #else /* not USERDB */ 61451360Seric 61551923Seric int 61651360Seric udbexpand(a, sendq) 61751360Seric ADDRESS *a; 61851360Seric ADDRESS **sendq; 61951360Seric { 62051923Seric return EX_OK; 62150581Seric } 62250581Seric 62350581Seric #endif /* USERDB */ 624