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*57977Seric static char sccsid [] = "@(#)udb.c 6.5 (Berkeley) 02/14/93 (with USERDB)"; 1251360Seric #else 13*57977Seric static char sccsid [] = "@(#)udb.c 6.5 (Berkeley) 02/14/93 (without USERDB)"; 1450581Seric #endif 1551360Seric #endif 1650581Seric 1750581Seric #include "sendmail.h" 1850581Seric 1950581Seric #ifdef USERDB 2050581Seric 2151360Seric #include <sys/time.h> 2251923Seric #include <errno.h> 2351360Seric #include <fcntl.h> 2451360Seric #include <netdb.h> 2550581Seric #include <db.h> 2650581Seric 2750581Seric /* 2853654Seric ** UDB.C -- interface between sendmail and Berkeley User Data Base. 2950581Seric ** 3051363Seric ** This depends on the 4.4BSD db package. 3150581Seric */ 3250581Seric 3351362Seric 3451360Seric struct udbent 3551360Seric { 3651360Seric char *udb_spec; /* string version of spec */ 3751360Seric int udb_type; /* type of entry */ 3851951Seric char *udb_default; /* default host for outgoing mail */ 3951360Seric union 4051360Seric { 4151360Seric /* type UE_REMOTE -- do remote call for lookup */ 4251360Seric struct 4351360Seric { 4451360Seric struct sockaddr_in _udb_addr; /* address */ 4551360Seric int _udb_timeout; /* timeout */ 4651360Seric } udb_remote; 4751360Seric #define udb_addr udb_u.udb_remote._udb_addr 4851360Seric #define udb_timeout udb_u.udb_remote._udb_timeout 4951360Seric 5051360Seric /* type UE_FORWARD -- forward message to remote */ 5151360Seric struct 5251360Seric { 5351360Seric char *_udb_fwdhost; /* name of forward host */ 5451360Seric } udb_forward; 5551360Seric #define udb_fwdhost udb_u.udb_forward._udb_fwdhost 5651360Seric 5751951Seric /* type UE_FETCH -- lookup in local database */ 5851360Seric struct 5951360Seric { 6051360Seric char *_udb_dbname; /* pathname of database */ 6151360Seric DB *_udb_dbp; /* open database ptr */ 6251360Seric } udb_lookup; 6351360Seric #define udb_dbname udb_u.udb_lookup._udb_dbname 6451360Seric #define udb_dbp udb_u.udb_lookup._udb_dbp 6551360Seric } udb_u; 6651360Seric }; 6751360Seric 6851360Seric #define UDB_EOLIST 0 /* end of list */ 6951360Seric #define UDB_SKIP 1 /* skip this entry */ 7051360Seric #define UDB_REMOTE 2 /* look up in remote database */ 7151951Seric #define UDB_DBFETCH 3 /* look up in local database */ 7251360Seric #define UDB_FORWARD 4 /* forward to remote host */ 7351360Seric 7451360Seric #define MAXUDBENT 10 /* maximum number of UDB entries */ 7551360Seric 7651363Seric 7751363Seric struct option 7851363Seric { 7951363Seric char *name; 8051363Seric char *val; 8151363Seric }; 8251363Seric /* 8351363Seric ** UDBEXPAND -- look up user in database and expand 8451363Seric ** 8551363Seric ** Parameters: 8651363Seric ** a -- address to expand. 8751363Seric ** sendq -- pointer to head of sendq to put the expansions in. 8851363Seric ** 8951363Seric ** Returns: 9051923Seric ** EX_TEMPFAIL -- if something "odd" happened -- probably due 9151923Seric ** to accessing a file on an NFS server that is down. 9251923Seric ** EX_OK -- otherwise. 9351363Seric ** 9451363Seric ** Side Effects: 9551363Seric ** Modifies sendq. 9651363Seric */ 9751363Seric 9851363Seric int UdbPort = 1616; 9951363Seric int UdbTimeout = 10; 10051363Seric 10151953Seric struct udbent UdbEnts[MAXUDBENT + 1]; 10251953Seric int UdbSock = -1; 10351953Seric bool UdbInitialized = FALSE; 10451360Seric 10551923Seric int 10655012Seric udbexpand(a, sendq, e) 10750581Seric register ADDRESS *a; 10850581Seric ADDRESS **sendq; 10955012Seric register ENVELOPE *e; 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; 11857232Seric char keybuf[MAXKEY]; 11957232Seric char buf[BUFSIZ]; 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; 12755012Seric e->e_to = a->q_paddr; 12850581Seric 12951360Seric /* on first call, locate the database */ 13051951Seric 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; 15950581Seric 16051360Seric /* 16151360Seric ** Select action based on entry type. 16251360Seric ** 16351360Seric ** On dropping out of this switch, "class" should 16451360Seric ** explain the type of the data, and "user" should 16551360Seric ** contain the user information. 16651360Seric */ 16750581Seric 16851360Seric switch (up->udb_type) 16951360Seric { 17051951Seric case UDB_DBFETCH: 17151362Seric key.data = keybuf; 17251362Seric key.size = keylen; 17351362Seric i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); 17451923Seric if (i > 0 || info.size <= 0) 17551360Seric { 17651360Seric if (tTd(28, 2)) 17751362Seric printf("expand: no match on %s\n", keybuf); 17851360Seric continue; 17951360Seric } 18050581Seric 18151830Seric while (i == 0 && key.size == keylen && 18251830Seric bcmp(key.data, keybuf, keylen) == 0) 18351362Seric { 18451830Seric breakout = TRUE; 18551362Seric if (info.size < sizeof buf) 18651362Seric user = buf; 18751362Seric else 18851362Seric user = xalloc(info.size + 1); 18951362Seric bcopy(info.data, user, info.size); 19051362Seric user[info.size] = '\0'; 19150581Seric 19251362Seric message(Arpa_Info, "expanded to %s", user); 193*57977Seric #ifdef LOG 194*57977Seric if (LogLevel >= 10) 195*57977Seric syslog(LOG_INFO, "%s: expand %s => %s", 196*57977Seric e->e_id, e->e_to, user); 197*57977Seric #endif 19851362Seric AliasLevel++; 19955012Seric sendtolist(user, a, sendq, e); 20051362Seric AliasLevel--; 20151362Seric 20251362Seric if (user != buf) 20351362Seric free(user); 20451362Seric 20551362Seric /* get the next record */ 20651362Seric i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); 20751830Seric } 20851923Seric if (i < 0) 20951923Seric { 21051923Seric syserr("udbexpand: db-get stat %s"); 21151923Seric return EX_TEMPFAIL; 21251923Seric } 21351360Seric break; 21451360Seric 21551360Seric case UDB_REMOTE: 21651741Seric /* not yet implemented */ 21751741Seric continue; 21851362Seric 21951360Seric case UDB_FORWARD: 22051360Seric i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; 22151360Seric if (i < sizeof buf) 22251360Seric user = buf; 22351360Seric else 22451360Seric user = xalloc(i + 1); 22551360Seric (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost); 22651362Seric message(Arpa_Info, "expanded to %s", user); 22751362Seric AliasLevel++; 22855012Seric sendtolist(user, a, sendq, e); 22951362Seric AliasLevel--; 23051362Seric if (user != buf) 23151362Seric free(user); 23251362Seric breakout = TRUE; 23351360Seric break; 23451360Seric 23551360Seric case UDB_EOLIST: 23651360Seric breakout = TRUE; 23751360Seric continue; 23851360Seric 23951360Seric default: 24051360Seric /* unknown entry type */ 24151360Seric continue; 24251360Seric } 24351362Seric } 24451923Seric return EX_OK; 24551362Seric } 24651951Seric /* 24751951Seric ** UDBSENDER -- return canonical external name of sender, given local name 24851951Seric ** 24951951Seric ** Parameters: 25051951Seric ** sender -- the name of the sender on the local machine. 25151951Seric ** 25251951Seric ** Returns: 25351951Seric ** The external name for this sender, if derivable from the 25451951Seric ** database. 25551951Seric ** NULL -- if nothing is changed from the database. 25651951Seric ** 25751951Seric ** Side Effects: 25851951Seric ** none. 25951951Seric */ 26051360Seric 26151951Seric char * 26251951Seric udbsender(sender) 26351951Seric char *sender; 26451951Seric { 26551951Seric register char *p; 26651951Seric register struct udbent *up; 26751951Seric int i; 26851951Seric int keylen; 26951951Seric DBT key, info; 27057232Seric char keybuf[MAXKEY]; 27151951Seric 27251951Seric if (tTd(28, 1)) 27351951Seric printf("udbsender(%s)\n", sender); 27451951Seric 27551951Seric if (!UdbInitialized) 27651951Seric { 27751951Seric if (_udbx_init() == EX_TEMPFAIL) 27851951Seric return NULL; 27951951Seric } 28051951Seric 28151951Seric /* short circuit if no spec */ 28251951Seric if (UdbSpec == NULL || UdbSpec[0] == '\0') 28351951Seric return NULL; 28451951Seric 28551951Seric /* long names can never match and are a pain to deal with */ 28651951Seric if (strlen(sender) > sizeof keybuf - 12) 28751951Seric return NULL; 28851951Seric 28951951Seric /* names beginning with colons indicate metadata */ 29051951Seric if (sender[0] == ':') 29151951Seric return NULL; 29251951Seric 29351951Seric /* build database key */ 29451951Seric (void) strcpy(keybuf, sender); 29551951Seric (void) strcat(keybuf, ":mailname"); 29651951Seric keylen = strlen(keybuf); 29751951Seric 29851951Seric for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 29951951Seric { 30051951Seric /* 30151951Seric ** Select action based on entry type. 30251951Seric */ 30351951Seric 30451951Seric switch (up->udb_type) 30551951Seric { 30651951Seric case UDB_DBFETCH: 30751951Seric key.data = keybuf; 30851951Seric key.size = keylen; 30951951Seric i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 31051951Seric if (i != 0 || info.size <= 0) 31151951Seric { 31251951Seric if (tTd(28, 2)) 31351951Seric printf("udbsender: no match on %s\n", 31451951Seric keybuf); 31551951Seric continue; 31651951Seric } 31751951Seric 31851951Seric p = xalloc(info.size + 1); 31951951Seric bcopy(info.data, p, info.size); 32051951Seric p[info.size] = '\0'; 32151951Seric if (tTd(28, 1)) 32251951Seric printf("udbsender ==> %s\n", p); 32351951Seric return p; 32451951Seric } 32551951Seric } 32651951Seric 32751951Seric /* 32851951Seric ** Nothing yet. Search again for a default case. But only 32951951Seric ** use it if we also have a forward (:maildrop) pointer already 33051951Seric ** in the database. 33151951Seric */ 33251951Seric 33351951Seric /* build database key */ 33451951Seric (void) strcpy(keybuf, sender); 33551951Seric (void) strcat(keybuf, ":maildrop"); 33651951Seric keylen = strlen(keybuf); 33751951Seric 33851951Seric for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 33951951Seric { 34051951Seric switch (up->udb_type) 34151951Seric { 34251951Seric case UDB_DBFETCH: 34351951Seric /* get the default case for this database */ 34451951Seric if (up->udb_default == NULL) 34551951Seric { 34651951Seric key.data = ":default:mailname"; 34751951Seric key.size = strlen(key.data); 34851951Seric i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 34951951Seric if (i != 0 || info.size <= 0) 35051951Seric { 35151951Seric /* no default case */ 35251951Seric up->udb_default = ""; 35351951Seric continue; 35451951Seric } 35551951Seric 35651951Seric /* save the default case */ 35751951Seric up->udb_default = xalloc(info.size + 1); 35851951Seric bcopy(info.data, up->udb_default, info.size); 35951951Seric up->udb_default[info.size] = '\0'; 36051951Seric } 36151951Seric else if (up->udb_default[0] == '\0') 36251951Seric continue; 36351951Seric 36451951Seric /* we have a default case -- verify user:maildrop */ 36551951Seric key.data = keybuf; 36651951Seric key.size = keylen; 36751951Seric i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 36851951Seric if (i != 0 || info.size <= 0) 36951951Seric { 37051951Seric /* nope -- no aliasing for this user */ 37151951Seric continue; 37251951Seric } 37351951Seric 37451951Seric /* they exist -- build the actual address */ 37551951Seric p = xalloc(strlen(sender) + strlen(up->udb_default) + 2); 37651951Seric (void) strcpy(p, sender); 37751951Seric (void) strcat(p, "@"); 37851951Seric (void) strcat(p, up->udb_default); 37951951Seric if (tTd(28, 1)) 38051951Seric printf("udbsender ==> %s\n", p); 38151951Seric return p; 38251951Seric } 38351951Seric } 38451951Seric 38551951Seric /* still nothing.... too bad */ 38651951Seric return NULL; 38751951Seric } 38851951Seric /* 38951951Seric ** _UDBX_INIT -- parse the UDB specification, opening any valid entries. 39051951Seric ** 39151951Seric ** Parameters: 39251951Seric ** none. 39351951Seric ** 39451951Seric ** Returns: 39551951Seric ** EX_TEMPFAIL -- if it appeared it couldn't get hold of a 39651951Seric ** database due to a host being down or some similar 39751951Seric ** (recoverable) situation. 39851951Seric ** EX_OK -- otherwise. 39951951Seric ** 40051951Seric ** Side Effects: 40151951Seric ** Fills in the UdbEnts structure from UdbSpec. 40251951Seric */ 40351951Seric 40451363Seric #define MAXUDBOPTS 27 40551363Seric 40651953Seric int 40751362Seric _udbx_init() 40851362Seric { 40951362Seric register char *p; 41051362Seric int i; 41151362Seric register struct udbent *up; 41257232Seric char buf[BUFSIZ]; 41351360Seric 41451951Seric if (UdbInitialized) 41551951Seric return EX_OK; 41651951Seric 41751908Seric # ifdef UDB_DEFAULT_SPEC 41851908Seric if (UdbSpec == NULL) 41951908Seric UdbSpec = UDB_DEFAULT_SPEC; 42051908Seric # endif 42151908Seric 42251362Seric p = UdbSpec; 42351362Seric up = UdbEnts; 42451762Seric while (p != NULL) 42551362Seric { 42651362Seric char *spec; 42751362Seric auto int rcode; 42851363Seric int nopts; 42951362Seric int nmx; 43051362Seric register struct hostent *h; 43151362Seric char *mxhosts[MAXMXHOSTS + 1]; 43251363Seric struct option opts[MAXUDBOPTS + 1]; 43351362Seric 43451362Seric while (*p == ' ' || *p == '\t' || *p == ',') 43551362Seric p++; 43651362Seric if (*p == '\0') 43751362Seric break; 43851362Seric spec = p; 43956795Seric p = strchr(p, ','); 44051761Seric if (p != NULL) 44151362Seric *p++ = '\0'; 44251363Seric 44351363Seric /* extract options */ 44451363Seric nopts = _udb_parsespec(spec, opts, MAXUDBOPTS); 44551363Seric 44651363Seric /* 44751363Seric ** Decode database specification. 44851363Seric ** 44951363Seric ** In the sendmail tradition, the leading character 45051363Seric ** defines the semantics of the rest of the entry. 45151363Seric ** 45251363Seric ** +hostname -- send a datagram to the udb server 45351363Seric ** on host "hostname" asking for the 45451363Seric ** home mail server for this user. 45551363Seric ** *hostname -- similar to +hostname, except that the 45651363Seric ** hostname is searched as an MX record; 45751363Seric ** resulting hosts are searched as for 45851363Seric ** +mxhostname. If no MX host is found, 45951363Seric ** this is the same as +hostname. 46051363Seric ** @hostname -- forward email to the indicated host. 46151363Seric ** This should be the last in the list, 46251363Seric ** since it always matches the input. 46351363Seric ** /dbname -- search the named database on the local 46451363Seric ** host using the Berkeley db package. 46551363Seric */ 46651363Seric 46751362Seric switch (*spec) 46851360Seric { 46951362Seric case '+': /* search remote database */ 47051363Seric case '*': /* search remote database (expand MX) */ 47151363Seric if (*spec == '*') 47251363Seric { 47357629Seric #ifdef NAMED_BIND 47451363Seric nmx = getmxrr(spec + 1, mxhosts, "", &rcode); 47557629Seric #else 47657629Seric mxhosts[0] = spec + 1; 47757629Seric nmx = 1; 47857629Seric rcode = 0; 47957629Seric #endif 48051363Seric if (tTd(28, 16)) 48151363Seric { 48251363Seric int i; 48351362Seric 48451363Seric printf("getmxrr(%s): %d", spec + 1, nmx); 48551363Seric for (i = 0; i <= nmx; i++) 48651363Seric printf(" %s", mxhosts[i]); 48751363Seric printf("\n"); 48851363Seric } 48951363Seric } 49051363Seric else 49151362Seric { 49251363Seric nmx = 1; 49351363Seric mxhosts[0] = spec + 1; 49451362Seric } 49551362Seric 49651362Seric for (i = 0; i < nmx; i++) 49751362Seric { 49851362Seric h = gethostbyname(mxhosts[i]); 49951362Seric if (h == NULL) 50051362Seric continue; 50151362Seric up->udb_type = UDB_REMOTE; 50251362Seric up->udb_addr.sin_family = h->h_addrtype; 50351362Seric bcopy(h->h_addr_list[0], 50451362Seric (char *) &up->udb_addr.sin_addr, 50551362Seric h->h_length); 50651362Seric up->udb_addr.sin_port = UdbPort; 50751362Seric up->udb_timeout = UdbTimeout; 50851362Seric up++; 50951362Seric } 51051362Seric 51151362Seric /* set up a datagram socket */ 51251362Seric if (UdbSock < 0) 51351362Seric { 51451362Seric UdbSock = socket(AF_INET, SOCK_DGRAM, 0); 51551362Seric (void) fcntl(UdbSock, F_SETFD, 1); 51651362Seric } 51751362Seric break; 51851362Seric 51951362Seric case '@': /* forward to remote host */ 52051362Seric up->udb_type = UDB_FORWARD; 52151362Seric up->udb_fwdhost = spec + 1; 52251362Seric up++; 52351362Seric break; 52451362Seric 52551362Seric case '/': /* look up remote name */ 52651831Seric up->udb_dbname = spec; 52751923Seric errno = 0; 52851362Seric up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL); 52951362Seric if (up->udb_dbp == NULL) 53051923Seric { 53151923Seric if (errno != ENOENT && errno != EACCES) 53251951Seric { 53351951Seric up->udb_type = UDB_EOLIST; 53451951Seric goto tempfail; 53551951Seric } 53651362Seric break; 53751923Seric } 53851951Seric up->udb_type = UDB_DBFETCH; 53951362Seric up++; 54051362Seric break; 54151360Seric } 54251362Seric } 54351362Seric up->udb_type = UDB_EOLIST; 54451360Seric 54551362Seric if (tTd(28, 4)) 54651362Seric { 54751951Seric for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 54851360Seric { 54951362Seric switch (up->udb_type) 55051362Seric { 55151362Seric case UDB_REMOTE: 55251362Seric printf("REMOTE: addr %s, timeo %d\n", 55351362Seric inet_ntoa(up->udb_addr.sin_addr), 55451362Seric up->udb_timeout); 55551362Seric break; 55651362Seric 55751951Seric case UDB_DBFETCH: 55851951Seric printf("FETCH: file %s\n", 55951830Seric up->udb_dbname); 56051362Seric break; 56151362Seric 56251362Seric case UDB_FORWARD: 56351362Seric printf("FORWARD: host %s\n", 56451362Seric up->udb_fwdhost); 56551362Seric break; 56651362Seric 56751362Seric default: 56851362Seric printf("UNKNOWN\n"); 56951362Seric break; 57051362Seric } 57151360Seric } 57250581Seric } 57351951Seric 57451951Seric UdbInitialized = TRUE; 57551955Seric errno = 0; 57651951Seric return EX_OK; 57751951Seric 57851951Seric /* 57951951Seric ** On temporary failure, back out anything we've already done 58051951Seric */ 58151951Seric 58251951Seric tempfail: 58351951Seric for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 58451951Seric { 58551951Seric if (up->udb_type == UDB_DBFETCH) 58651951Seric { 58751951Seric (*up->udb_dbp->close)(up->udb_dbp); 58851951Seric } 58951951Seric } 59051951Seric return EX_TEMPFAIL; 59151360Seric } 59250581Seric 59351363Seric int 59451363Seric _udb_parsespec(udbspec, opt, maxopts) 59551363Seric char *udbspec; 59651363Seric struct option opt[]; 59751363Seric int maxopts; 59851363Seric { 59951363Seric register char *spec; 60051363Seric register char *spec_end; 60151363Seric register int optnum; 60251363Seric 60356795Seric spec_end = strchr(udbspec, ':'); 60451363Seric for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) 60551363Seric { 60651363Seric register char *p; 60751363Seric 60851363Seric while (isspace(*spec)) 60951363Seric spec++; 61056795Seric spec_end = strchr(spec, ':'); 61151363Seric if (spec_end != NULL) 61251363Seric *spec_end++ = '\0'; 61351363Seric 61451363Seric opt[optnum].name = spec; 61551363Seric opt[optnum].val = NULL; 61656795Seric p = strchr(spec, '='); 61751363Seric if (p != NULL) 61851363Seric opt[optnum].val = ++p; 61951363Seric } 62051363Seric return optnum; 62151363Seric } 62251363Seric 62351360Seric #else /* not USERDB */ 62451360Seric 62551923Seric int 62655012Seric udbexpand(a, sendq, e) 62751360Seric ADDRESS *a; 62851360Seric ADDRESS **sendq; 62955012Seric ENVELOPE *e; 63051360Seric { 63151923Seric return EX_OK; 63250581Seric } 63350581Seric 63450581Seric #endif /* USERDB */ 635